View Javadoc
1   /*
2    * Copyright 2016-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.windows;
6   
7   import java.util.ArrayList;
8   import java.util.Collections;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Map.Entry;
12  import java.util.Set;
13  import java.util.stream.Collectors;
14  
15  import com.sun.jna.platform.win32.Guid.GUID;
16  
17  import oshi.annotation.concurrent.Immutable;
18  import oshi.driver.windows.DeviceTree;
19  import oshi.hardware.UsbDevice;
20  import oshi.hardware.common.AbstractUsbDevice;
21  import oshi.util.ParseUtil;
22  import oshi.util.tuples.Quintet;
23  import oshi.util.tuples.Triplet;
24  
25  /**
26   * Windows Usb Device
27   */
28  @Immutable
29  public class WindowsUsbDevice extends AbstractUsbDevice {
30  
31      private static final GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = new GUID(
32              "{3ABF6F2D-71C4-462A-8A92-1E6861E6AF27}");
33  
34      public WindowsUsbDevice(String name, String vendor, String vendorId, String productId, String serialNumber,
35              String uniqueDeviceId, List<UsbDevice> connectedDevices) {
36          super(name, vendor, vendorId, productId, serialNumber, uniqueDeviceId, connectedDevices);
37      }
38  
39      /**
40       * Instantiates a list of {@link oshi.hardware.UsbDevice} objects, representing devices connected via a usb port
41       * (including internal devices).
42       * <p>
43       * If the value of {@code tree} is true, the top level devices returned from this method are the USB Controllers;
44       * connected hubs and devices in its device tree share that controller's bandwidth. If the value of {@code tree} is
45       * false, USB devices (not controllers) are listed in a single flat list.
46       *
47       * @param tree If true, returns a list of controllers, which requires recursive iteration of connected devices. If
48       *             false, returns a flat list of devices excluding controllers.
49       * @return a list of {@link oshi.hardware.UsbDevice} objects.
50       */
51      public static List<UsbDevice> getUsbDevices(boolean tree) {
52          List<UsbDevice> devices = queryUsbDevices();
53          if (tree) {
54              return devices;
55          }
56          List<UsbDevice> deviceList = new ArrayList<>();
57          // Top level is controllers; they won't be added to the list, but all
58          // their connected devices will be
59          for (UsbDevice device : devices) {
60              // Recursively add all child devices
61              addDevicesToList(deviceList, device.getConnectedDevices());
62          }
63          return deviceList;
64      }
65  
66      private static void addDevicesToList(List<UsbDevice> deviceList, List<UsbDevice> list) {
67          for (UsbDevice device : list) {
68              deviceList.add(new WindowsUsbDevice(device.getName(), device.getVendor(), device.getVendorId(),
69                      device.getProductId(), device.getSerialNumber(), device.getUniqueDeviceId(),
70                      Collections.emptyList()));
71              addDevicesToList(deviceList, device.getConnectedDevices());
72          }
73      }
74  
75      private static List<UsbDevice> queryUsbDevices() {
76          Quintet<Set<Integer>, Map<Integer, Integer>, Map<Integer, String>, Map<Integer, String>, Map<Integer, String>> controllerDevices = DeviceTree
77                  .queryDeviceTree(GUID_DEVINTERFACE_USB_HOST_CONTROLLER);
78          Map<Integer, Integer> parentMap = controllerDevices.getB();
79          Map<Integer, String> nameMap = controllerDevices.getC();
80          Map<Integer, String> deviceIdMap = controllerDevices.getD();
81          Map<Integer, String> mfgMap = controllerDevices.getE();
82  
83          List<UsbDevice> usbDevices = new ArrayList<>();
84          // recursively build results
85          for (Integer controllerDevice : controllerDevices.getA()) {
86              WindowsUsbDevice deviceAndChildren = queryDeviceAndChildren(controllerDevice, parentMap, nameMap,
87                      deviceIdMap, mfgMap, "0000", "0000", "");
88              if (deviceAndChildren != null) {
89                  usbDevices.add(deviceAndChildren);
90              }
91          }
92          return usbDevices;
93      }
94  
95      private static WindowsUsbDevice queryDeviceAndChildren(Integer device, Map<Integer, Integer> parentMap,
96              Map<Integer, String> nameMap, Map<Integer, String> deviceIdMap, Map<Integer, String> mfgMap, String vid,
97              String pid, String parentSerial) {
98          // Parse vendor and product IDs from the device ID
99          // If this doesn't work, use the IDs from the parent
100         String vendorId = vid;
101         String productId = pid;
102         String serial = parentSerial;
103         Triplet<String, String, String> idsAndSerial = ParseUtil
104                 .parseDeviceIdToVendorProductSerial(deviceIdMap.get(device));
105         if (idsAndSerial != null) {
106             vendorId = idsAndSerial.getA();
107             productId = idsAndSerial.getB();
108             serial = idsAndSerial.getC();
109             if (serial.isEmpty() && vendorId.equals(vid) && productId.equals(pid)) {
110                 serial = parentSerial;
111             }
112         }
113         // Iterate the parent map looking for children
114         Set<Integer> childDeviceSet = parentMap.entrySet().stream().filter(e -> e.getValue().equals(device))
115                 .map(Entry::getKey).collect(Collectors.toSet());
116         // Recursively find those children and put in a list
117         List<UsbDevice> childDevices = new ArrayList<>();
118         for (Integer child : childDeviceSet) {
119             WindowsUsbDevice deviceAndChildren = queryDeviceAndChildren(child, parentMap, nameMap, deviceIdMap, mfgMap,
120                     vendorId, productId, serial);
121             if (deviceAndChildren != null) {
122                 childDevices.add(deviceAndChildren);
123             }
124         }
125         Collections.sort(childDevices);
126         // Finally construct the object and return
127         if (nameMap.containsKey(device)) {
128             String name = nameMap.get(device);
129             if (name.isEmpty()) {
130                 name = vendorId + ":" + productId;
131             }
132             String deviceId = deviceIdMap.get(device);
133             String mfg = mfgMap.get(device);
134             return new WindowsUsbDevice(name, mfg, vendorId, productId, serial, deviceId, childDevices);
135         }
136         return null;
137     }
138 }