oshi.hardware.platform.windows.WindowsUsbDevice Maven / Gradle / Ivy
/*
* Copyright 2016-2022 The OSHI Project Contributors
* SPDX-License-Identifier: MIT
*/
package oshi.hardware.platform.windows;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.stream.Collectors;
import com.sun.jna.platform.win32.Guid.GUID;
import oshi.annotation.concurrent.Immutable;
import oshi.driver.windows.DeviceTree;
import oshi.hardware.UsbDevice;
import oshi.hardware.common.AbstractUsbDevice;
import oshi.util.ParseUtil;
import oshi.util.tuples.Quintet;
import oshi.util.tuples.Triplet;
/**
* Windows Usb Device
*/
@Immutable
public class WindowsUsbDevice extends AbstractUsbDevice {
private static final GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new GUID(
"{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}");
public WindowsUsbDevice(String name, String vendor, String vendorId, String productId, String serialNumber,
String uniqueDeviceId, List connectedDevices) {
super(name, vendor, vendorId, productId, serialNumber, uniqueDeviceId, connectedDevices);
}
/**
* Instantiates a list of {@link oshi.hardware.UsbDevice} objects, representing devices connected via a usb port
* (including internal devices).
*
* If the value of {@code tree} is true, the top level devices returned from this method are the USB Controllers;
* connected hubs and devices in its device tree share that controller's bandwidth. If the value of {@code tree} is
* false, USB devices (not controllers) are listed in a single flat list.
*
* @param tree If true, returns a list of controllers, which requires recursive iteration of connected devices. If
* false, returns a flat list of devices excluding controllers.
* @return a list of {@link oshi.hardware.UsbDevice} objects.
*/
public static List getUsbDevices(boolean tree) {
List devices = queryUsbDevices();
if (tree) {
return devices;
}
List deviceList = new ArrayList<>();
// Top level is controllers; they won't be added to the list, but all
// their connected devices will be
for (UsbDevice device : devices) {
// Recursively add all child devices
addDevicesToList(deviceList, device.getConnectedDevices());
}
return deviceList;
}
private static void addDevicesToList(List deviceList, List list) {
for (UsbDevice device : list) {
deviceList.add(new WindowsUsbDevice(device.getName(), device.getVendor(), device.getVendorId(),
device.getProductId(), device.getSerialNumber(), device.getUniqueDeviceId(),
Collections.emptyList()));
addDevicesToList(deviceList, device.getConnectedDevices());
}
}
private static List queryUsbDevices() {
Quintet, Map, Map, Map, Map> controllerDevices = DeviceTree
.queryDeviceTree(GUID_DEVINTERFACE_USB_HOST_CONTROLLER);
Map parentMap = controllerDevices.getB();
Map nameMap = controllerDevices.getC();
Map deviceIdMap = controllerDevices.getD();
Map mfgMap = controllerDevices.getE();
List usbDevices = new ArrayList<>();
// recursively build results
for (Integer controllerDevice : controllerDevices.getA()) {
WindowsUsbDevice deviceAndChildren = queryDeviceAndChildren(controllerDevice, parentMap, nameMap,
deviceIdMap, mfgMap, "0000", "0000", "");
if (deviceAndChildren != null) {
usbDevices.add(deviceAndChildren);
}
}
return usbDevices;
}
private static WindowsUsbDevice queryDeviceAndChildren(Integer device, Map parentMap,
Map nameMap, Map deviceIdMap, Map mfgMap, String vid,
String pid, String parentSerial) {
// Parse vendor and product IDs from the device ID
// If this doesn't work, use the IDs from the parent
String vendorId = vid;
String productId = pid;
String serial = parentSerial;
Triplet idsAndSerial = ParseUtil
.parseDeviceIdToVendorProductSerial(deviceIdMap.get(device));
if (idsAndSerial != null) {
vendorId = idsAndSerial.getA();
productId = idsAndSerial.getB();
serial = idsAndSerial.getC();
if (serial.isEmpty() && vendorId.equals(vid) && productId.equals(pid)) {
serial = parentSerial;
}
}
// Iterate the parent map looking for children
Set childDeviceSet = parentMap.entrySet().stream().filter(e -> e.getValue().equals(device))
.map(Entry::getKey).collect(Collectors.toSet());
// Recursively find those children and put in a list
List childDevices = new ArrayList<>();
for (Integer child : childDeviceSet) {
WindowsUsbDevice deviceAndChildren = queryDeviceAndChildren(child, parentMap, nameMap, deviceIdMap, mfgMap,
vendorId, productId, serial);
if (deviceAndChildren != null) {
childDevices.add(deviceAndChildren);
}
}
Collections.sort(childDevices);
// Finally construct the object and return
if (nameMap.containsKey(device)) {
String name = nameMap.get(device);
if (name.isEmpty()) {
name = vendorId + ":" + productId;
}
String deviceId = deviceIdMap.get(device);
String mfg = mfgMap.get(device);
return new WindowsUsbDevice(name, mfg, vendorId, productId, serial, deviceId, childDevices);
}
return null;
}
}