Java - Use usb4java to detect USB devices plug and unplug

Java要存取到OS上面USB device相關的資訊本身不支援,一種是透過JNI的方式,讓Java呼叫C來幫忙存取,而C現成的library可以想到知名的libusb。在這邊使用的usb4java就是基於libusb來開發,可以說是libusb for java的代表,目前也有列在libusb的github上! 可惜的是在2018年後就沒有再出新版本,但並不影響今日要實作的部分,因為只有用到list出目前電腦上有哪些連接的USB devices,來偵測當下有哪些裝置plug or unplug!
首先,請至usb4java github下載目前最新的v1.3.0版本,解壓縮後將
commons-lang3-3.8.1.jar
libusb4java-1.3.0-win32-x86-64.jar (在此以Windows環境開發)
usb4java-1.3.0.jar
等加入到build path內

再來說明程式碼的部分:
#UsbDevice.java
  1. import java.util.Objects;
  2. public class UsbDevice {
  3. private short vid;
  4. private short pid;
  5. private int bus;
  6. private int port;
  7. private int device;
  8. public UsbDevice(int bus, int port, int device, short vid, short pid){
  9. this.bus = bus;
  10. this.port = port;
  11. this.device = device;
  12. this.vid = vid;
  13. this.pid = pid;
  14. }
  15. public short getVid() {
  16. return vid;
  17. }
  18. public short getPid() {
  19. return pid;
  20. }
  21. public int getDevice() {
  22. return device;
  23. }
  24. public void setDevice(int device) {
  25. this.device = device;
  26. }
  27. public int getBus() {
  28. return bus;
  29. }
  30. public int getPort() {
  31. return port;
  32. }
  33. @Override
  34. public int hashCode() {
  35. return Objects.hash(bus, device, port);
  36. }
  37. @Override
  38. public boolean equals(Object obj) {
  39. if (this == obj)
  40. return true;
  41. if (obj == null)
  42. return false;
  43. if (getClass() != obj.getClass())
  44. return false;
  45. UsbDevice other = (UsbDevice) obj;
  46. return bus == other.bus && device == other.device && port == other.port;
  47. }
  48. @Override
  49. public String toString() {
  50. return "UsbDevice [vid=" + String.format("0x%04x", vid) + ", pid=" + String.format("0x%04x", pid) + ", bus=" + bus + ", port=" + port + ", device=" + device +"]";
  51. }
  52. }

定義存放USB device的Vendor ID、Product ID、bus number、port number及device address等資訊
從Vendor ID、Product ID可以得知是否為相同的USB裝置
從bus number、port number及device address可以得知插入的位置
這邊的equals是判斷哪些device被插入或移除

#UsbDeviceDetectService.java
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Timer;
  4. import java.util.TimerTask;
  5. import org.usb4java.Context;
  6. import org.usb4java.Device;
  7. import org.usb4java.DeviceDescriptor;
  8. import org.usb4java.DeviceList;
  9. import org.usb4java.LibUsb;
  10. import org.usb4java.LibUsbException;
  11. public abstract class UsbDeviceDetectService {
  12. private boolean enabled = false;
  13. private Timer timer;
  14. private Context context = new Context();
  15. protected abstract void getConnectedDevices(List<UsbDevice> usbDeviceList);
  16. protected void addUsbServicesListener() {
  17. timer = new Timer();
  18. timer.schedule(new TimerTask(){
  19. @Override
  20. public void run() {
  21. DeviceList list = new DeviceList();
  22. List<UsbDevice> plugTemp = new ArrayList<>();
  23. int result = LibUsb.getDeviceList(context, list);
  24. if (result < 0) {
  25. cancel();
  26. throw new LibUsbException("Unable to get device list", result);
  27. }
  28. // Iterate over all devices and scan for the right one
  29. try {
  30. for (Device device: list){
  31. DeviceDescriptor descriptor = new DeviceDescriptor();
  32. result = LibUsb.getDeviceDescriptor(device, descriptor);
  33. if (result != LibUsb.SUCCESS)
  34. throw new LibUsbException("Unable to read device descriptor", result);
  35. UsbDevice usbDevice = new UsbDevice(LibUsb.getBusNumber(device), LibUsb.getPortNumber(device), LibUsb.getDeviceAddress(device),
  36. descriptor.idVendor(), descriptor.idProduct());
  37. plugTemp.add(usbDevice);
  38. }
  39. getConnectedDevices(plugTemp);
  40. } finally{
  41. // Ensure the allocated device list is freed
  42. LibUsb.freeDeviceList(list, true);
  43. }
  44. }
  45. }, 0 , 3000);
  46. }
  47. public void removeUsbServicesListener() {
  48. LibUsb.exit(context);
  49. context = null;
  50. if(timer != null){
  51. timer.cancel();
  52. timer = null;
  53. }
  54. }
  55. public boolean isEnabled() {
  56. return this.enabled;
  57. }
  58. public void setEnabled(boolean enabled) {
  59. this.enabled = enabled;
  60. if (this.enabled) {
  61. int result = LibUsb.init(context);
  62. if (result != LibUsb.SUCCESS)
  63. throw new LibUsbException("Unable to initialize libusb.", result);
  64. addUsbServicesListener();
  65. } else {
  66. removeUsbServicesListener();
  67. }
  68. }
  69. }

此抽象類別需定義getConnectedDevices method要做什麼處理,當下傳入的usbDeviceList為目前正連接著的USB device,用一個timer每3秒輪詢一次作為間隔,以此達到隨時detect是否有新插入或移除的裝置!

#Main.java
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.Scanner;
  4. public class Main {
  5. private List<UsbDevice> connectedLists = new ArrayList<>();
  6. public static void main(String[] args) {
  7. // TODO Auto-generated method stub
  8. new Main();
  9. }
  10. public Main() {
  11. UsbDeviceDetectService service = new UsbDeviceDetectService() {
  12. boolean firstDetected = true;
  13. @Override
  14. protected void getConnectedDevices(List<UsbDevice> usbDeviceList) {
  15. // TODO Auto-generated method stub
  16. for(UsbDevice device : usbDeviceList) {
  17. if(!connectedLists.contains(device)) {
  18. connectedLists.add(device);
  19. if(!firstDetected) {
  20. System.out.println(">> Plug: "+device);
  21. }
  22. }
  23. }
  24. firstDetected = false;
  25. if(connectedLists.size() > usbDeviceList.size()) {
  26. List<UsbDevice> unplugList = new ArrayList<>();
  27. for(UsbDevice device : connectedLists) {
  28. if(!usbDeviceList.contains(device)) {
  29. unplugList.add(device);
  30. System.out.println("<< Unplug: "+device);
  31. }
  32. }
  33. connectedLists.removeAll(unplugList);
  34. }
  35. }
  36. };
  37. Scanner input = new Scanner(System.in);
  38. try {
  39. while(true) {
  40. System.out.println("(1)Start USB Service(2)List Connected Devices(3)Terminate");
  41. int option = input.nextInt();
  42. if(option == 1) {
  43. if(!service.isEnabled()) {
  44. service.setEnabled(true);
  45. System.out.println("Start USB Detect Listener");
  46. }else {
  47. System.out.println("USB Detect has been started");
  48. }
  49. }else if(option == 2) {
  50. System.out.println("Connected Devices:");
  51. for(UsbDevice device : connectedLists) {
  52. System.out.println(device);
  53. }
  54. }else if(option == 3) {
  55. if(service.isEnabled()) {
  56. service.setEnabled(false);
  57. System.out.println("Terminate and remove USB Detect Listener");
  58. break;
  59. }else {
  60. System.out.println("Terminate");
  61. }
  62. }else {
  63. continue;
  64. }
  65. System.out.println();
  66. }
  67. } finally {
  68. input.close();
  69. }
  70. }
  71. }

主程式提供一操作,分別為啟動USB detect server及列出目前連接的裝置及終止程式運作
其覆寫的getConnectedDevices method內簡單判斷目前什麼裝置plug or unplug,並撇除了啟動USB detect時本身已插著的裝置

執行結果如下:

留言