From 7508842859bc1c791d1fff2ba514f547a59a631d Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 2 Jan 2025 15:39:41 +0100 Subject: Android: Clean up naming in Java_GCAdapter and Java_WiimoteAdapter This isn't how we name things in Java/Kotlin. --- .../dolphinemu/dolphinemu/DolphinApplication.java | 8 +- .../org/dolphinemu/dolphinemu/utils/GCAdapter.java | 158 +++++++++++++++++++ .../dolphinemu/utils/Java_GCAdapter.java | 158 ------------------- .../dolphinemu/utils/Java_WiimoteAdapter.java | 172 --------------------- .../dolphinemu/utils/WiimoteAdapter.java | 172 +++++++++++++++++++++ 5 files changed, 334 insertions(+), 334 deletions(-) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java index 8ff78ebb0e..91200852a1 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/DolphinApplication.java @@ -9,8 +9,8 @@ import android.hardware.usb.UsbManager; import org.dolphinemu.dolphinemu.utils.ActivityTracker; import org.dolphinemu.dolphinemu.utils.DirectoryInitialization; -import org.dolphinemu.dolphinemu.utils.Java_GCAdapter; -import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter; +import org.dolphinemu.dolphinemu.utils.GCAdapter; +import org.dolphinemu.dolphinemu.utils.WiimoteAdapter; import org.dolphinemu.dolphinemu.utils.VolleyUtil; public class DolphinApplication extends Application @@ -28,8 +28,8 @@ public class DolphinApplication extends Application VolleyUtil.init(getApplicationContext()); System.loadLibrary("main"); - Java_GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); - Java_WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); + GCAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); + WiimoteAdapter.manager = (UsbManager) getSystemService(Context.USB_SERVICE); if (DirectoryInitialization.shouldStart(getApplicationContext())) DirectoryInitialization.start(getApplicationContext()); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java new file mode 100644 index 0000000000..924496e488 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbConfiguration; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.os.Build; +import android.widget.Toast; + +import androidx.annotation.Keep; + +import org.dolphinemu.dolphinemu.DolphinApplication; +import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.services.USBPermService; + +import java.util.HashMap; +import java.util.Map; + +public class GCAdapter +{ + public static UsbManager manager; + + @Keep + static byte[] controllerPayload = new byte[37]; + + static UsbDeviceConnection usbConnection; + static UsbInterface usbInterface; + static UsbEndpoint usbIn; + static UsbEndpoint usbOut; + + private static void requestPermission() + { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) + { + if (!manager.hasPermission(dev)) + { + Context context = DolphinApplication.getAppContext(); + Intent intent = new Intent(context, USBPermService.class); + + int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? + PendingIntent.FLAG_IMMUTABLE : 0; + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); + + manager.requestPermission(dev, pendingIntent); + } + } + } + } + + public static void shutdown() + { + usbConnection.close(); + } + + @Keep + public static int getFd() + { + return usbConnection.getFileDescriptor(); + } + + @Keep + public static boolean queryAdapter() + { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) + { + if (manager.hasPermission(dev)) + return true; + else + requestPermission(); + } + } + return false; + } + + public static void initAdapter() + { + byte[] init = {0x13}; + usbConnection.bulkTransfer(usbOut, init, init.length, 0); + } + + @Keep + public static int input() + { + return usbConnection.bulkTransfer(usbIn, controllerPayload, controllerPayload.length, 16); + } + + @Keep + public static int output(byte[] rumble) + { + return usbConnection.bulkTransfer(usbOut, rumble, 5, 16); + } + + @Keep + public static boolean openAdapter() + { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) + { + if (manager.hasPermission(dev)) + { + usbConnection = manager.openDevice(dev); + + Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); + Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); + + if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) + { + UsbConfiguration conf = dev.getConfiguration(0); + usbInterface = conf.getInterface(0); + usbConnection.claimInterface(usbInterface, true); + + Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount()); + + if (usbInterface.getEndpointCount() == 2) + { + for (int i = 0; i < usbInterface.getEndpointCount(); ++i) + if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) + usbIn = usbInterface.getEndpoint(i); + else + usbOut = usbInterface.getEndpoint(i); + + initAdapter(); + return true; + } + else + { + usbConnection.releaseInterface(usbInterface); + } + } + + Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, + Toast.LENGTH_LONG).show(); + usbConnection.close(); + } + } + } + return false; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java deleted file mode 100644 index 3fcd58afac..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_GCAdapter.java +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.hardware.usb.UsbConfiguration; -import android.hardware.usb.UsbConstants; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbEndpoint; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.os.Build; -import android.widget.Toast; - -import androidx.annotation.Keep; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.services.USBPermService; - -import java.util.HashMap; -import java.util.Map; - -public class Java_GCAdapter -{ - public static UsbManager manager; - - @Keep - static byte[] controller_payload = new byte[37]; - - static UsbDeviceConnection usb_con; - static UsbInterface usb_intf; - static UsbEndpoint usb_in; - static UsbEndpoint usb_out; - - private static void RequestPermission() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (!manager.hasPermission(dev)) - { - Context context = DolphinApplication.getAppContext(); - Intent intent = new Intent(context, USBPermService.class); - - int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - PendingIntent.FLAG_IMMUTABLE : 0; - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); - - manager.requestPermission(dev, pendingIntent); - } - } - } - } - - public static void Shutdown() - { - usb_con.close(); - } - - @Keep - public static int GetFD() - { - return usb_con.getFileDescriptor(); - } - - @Keep - public static boolean QueryAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (manager.hasPermission(dev)) - return true; - else - RequestPermission(); - } - } - return false; - } - - public static void InitAdapter() - { - byte[] init = {0x13}; - usb_con.bulkTransfer(usb_out, init, init.length, 0); - } - - @Keep - public static int Input() - { - return usb_con.bulkTransfer(usb_in, controller_payload, controller_payload.length, 16); - } - - @Keep - public static int Output(byte[] rumble) - { - return usb_con.bulkTransfer(usb_out, rumble, 5, 16); - } - - @Keep - public static boolean OpenAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) - { - if (manager.hasPermission(dev)) - { - usb_con = manager.openDevice(dev); - - Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); - Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); - - if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) - { - UsbConfiguration conf = dev.getConfiguration(0); - usb_intf = conf.getInterface(0); - usb_con.claimInterface(usb_intf, true); - - Log.info("GCAdapter: Number of endpoints: " + usb_intf.getEndpointCount()); - - if (usb_intf.getEndpointCount() == 2) - { - for (int i = 0; i < usb_intf.getEndpointCount(); ++i) - if (usb_intf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) - usb_in = usb_intf.getEndpoint(i); - else - usb_out = usb_intf.getEndpoint(i); - - InitAdapter(); - return true; - } - else - { - usb_con.releaseInterface(usb_intf); - } - } - - Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, - Toast.LENGTH_LONG).show(); - usb_con.close(); - } - } - } - return false; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java deleted file mode 100644 index 469375614f..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/Java_WiimoteAdapter.java +++ /dev/null @@ -1,172 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.hardware.usb.UsbConfiguration; -import android.hardware.usb.UsbDevice; -import android.hardware.usb.UsbDeviceConnection; -import android.hardware.usb.UsbEndpoint; -import android.hardware.usb.UsbInterface; -import android.hardware.usb.UsbManager; -import android.os.Build; - -import androidx.annotation.Keep; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.services.USBPermService; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -public class Java_WiimoteAdapter -{ - final static int MAX_PAYLOAD = 23; - final static int MAX_WIIMOTES = 4; - final static int TIMEOUT = 200; - final static short NINTENDO_VENDOR_ID = 0x057e; - final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306; - public static UsbManager manager; - - static UsbDeviceConnection usb_con; - static UsbInterface[] usb_intf = new UsbInterface[MAX_WIIMOTES]; - static UsbEndpoint[] usb_in = new UsbEndpoint[MAX_WIIMOTES]; - - @Keep - public static byte[][] wiimote_payload = new byte[MAX_WIIMOTES][MAX_PAYLOAD]; - - private static void RequestPermission() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (!manager.hasPermission(dev)) - { - Log.warning("Requesting permission for Wii Remote adapter"); - - Context context = DolphinApplication.getAppContext(); - Intent intent = new Intent(context, USBPermService.class); - - int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? - PendingIntent.FLAG_IMMUTABLE : 0; - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); - - manager.requestPermission(dev, pendingIntent); - } - } - } - } - - @Keep - public static boolean QueryAdapter() - { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (manager.hasPermission(dev)) - return true; - else - RequestPermission(); - } - } - return false; - } - - @Keep - public static int Input(int index) - { - return usb_con.bulkTransfer(usb_in[index], wiimote_payload[index], MAX_PAYLOAD, TIMEOUT); - } - - @Keep - public static int Output(int index, byte[] buf, int size) - { - byte report_number = buf[0]; - - // Remove the report number from the buffer - buf = Arrays.copyOfRange(buf, 1, buf.length); - size--; - - final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5); - final int LIBUSB_RECIPIENT_INTERFACE = 0x1; - final int LIBUSB_ENDPOINT_OUT = 0; - - final int HID_SET_REPORT = 0x9; - final int HID_OUTPUT = (2 << 8); - - int write = usb_con.controlTransfer( - LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, - HID_SET_REPORT, - HID_OUTPUT | report_number, - index, - buf, size, - 1000); - - if (write < 0) - return 0; - - return write + 1; - } - - @Keep - public static boolean OpenAdapter() - { - // If the adapter is already open. Don't attempt to do it again - if (usb_con != null && usb_con.getFileDescriptor() != -1) - return true; - - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) - { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && - dev.getVendorId() == NINTENDO_VENDOR_ID) - { - if (manager.hasPermission(dev)) - { - usb_con = manager.openDevice(dev); - UsbConfiguration conf = dev.getConfiguration(0); - - Log.info("Number of configurations: " + dev.getConfigurationCount()); - Log.info("Number of Interfaces: " + dev.getInterfaceCount()); - Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount()); - - // Sometimes the interface count is returned as zero. - // Means the device needs to be unplugged and plugged back in again - if (dev.getInterfaceCount() > 0) - { - for (int i = 0; i < MAX_WIIMOTES; ++i) - { - // One interface per Wii Remote - usb_intf[i] = dev.getInterface(i); - usb_con.claimInterface(usb_intf[i], true); - - // One endpoint per Wii Remote. Input only - // Output reports go through the control channel. - usb_in[i] = usb_intf[i].getEndpoint(0); - Log.info("Interface " + i + " endpoint count:" + usb_intf[i].getEndpointCount()); - } - return true; - } - else - { - // XXX: Message that the device was found, but it needs to be unplugged and plugged back in? - usb_con.close(); - } - } - } - } - return false; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java new file mode 100644 index 0000000000..98a7d51900 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/WiimoteAdapter.java @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.hardware.usb.UsbConfiguration; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +import android.hardware.usb.UsbManager; +import android.os.Build; + +import androidx.annotation.Keep; + +import org.dolphinemu.dolphinemu.DolphinApplication; +import org.dolphinemu.dolphinemu.services.USBPermService; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class WiimoteAdapter +{ + final static int MAX_PAYLOAD = 23; + final static int MAX_WIIMOTES = 4; + final static int TIMEOUT = 200; + final static short NINTENDO_VENDOR_ID = 0x057e; + final static short NINTENDO_WIIMOTE_PRODUCT_ID = 0x0306; + public static UsbManager manager; + + static UsbDeviceConnection usbConnection; + static UsbInterface[] usbInterface = new UsbInterface[MAX_WIIMOTES]; + static UsbEndpoint[] usbIn = new UsbEndpoint[MAX_WIIMOTES]; + + @Keep + public static byte[][] wiimotePayload = new byte[MAX_WIIMOTES][MAX_PAYLOAD]; + + private static void requestPermission() + { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && + dev.getVendorId() == NINTENDO_VENDOR_ID) + { + if (!manager.hasPermission(dev)) + { + Log.warning("Requesting permission for Wii Remote adapter"); + + Context context = DolphinApplication.getAppContext(); + Intent intent = new Intent(context, USBPermService.class); + + int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? + PendingIntent.FLAG_IMMUTABLE : 0; + PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); + + manager.requestPermission(dev, pendingIntent); + } + } + } + } + + @Keep + public static boolean queryAdapter() + { + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && + dev.getVendorId() == NINTENDO_VENDOR_ID) + { + if (manager.hasPermission(dev)) + return true; + else + requestPermission(); + } + } + return false; + } + + @Keep + public static int input(int index) + { + return usbConnection.bulkTransfer(usbIn[index], wiimotePayload[index], MAX_PAYLOAD, TIMEOUT); + } + + @Keep + public static int output(int index, byte[] buf, int size) + { + byte report_number = buf[0]; + + // Remove the report number from the buffer + buf = Arrays.copyOfRange(buf, 1, buf.length); + size--; + + final int LIBUSB_REQUEST_TYPE_CLASS = (1 << 5); + final int LIBUSB_RECIPIENT_INTERFACE = 0x1; + final int LIBUSB_ENDPOINT_OUT = 0; + + final int HID_SET_REPORT = 0x9; + final int HID_OUTPUT = (2 << 8); + + int write = usbConnection.controlTransfer( + LIBUSB_REQUEST_TYPE_CLASS | LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_OUT, + HID_SET_REPORT, + HID_OUTPUT | report_number, + index, + buf, size, + 1000); + + if (write < 0) + return 0; + + return write + 1; + } + + @Keep + public static boolean openAdapter() + { + // If the adapter is already open. Don't attempt to do it again + if (usbConnection != null && usbConnection.getFileDescriptor() != -1) + return true; + + HashMap devices = manager.getDeviceList(); + for (Map.Entry pair : devices.entrySet()) + { + UsbDevice dev = pair.getValue(); + if (dev.getProductId() == NINTENDO_WIIMOTE_PRODUCT_ID && + dev.getVendorId() == NINTENDO_VENDOR_ID) + { + if (manager.hasPermission(dev)) + { + usbConnection = manager.openDevice(dev); + UsbConfiguration conf = dev.getConfiguration(0); + + Log.info("Number of configurations: " + dev.getConfigurationCount()); + Log.info("Number of Interfaces: " + dev.getInterfaceCount()); + Log.info("Number of Interfaces from conf: " + conf.getInterfaceCount()); + + // Sometimes the interface count is returned as zero. + // Means the device needs to be unplugged and plugged back in again + if (dev.getInterfaceCount() > 0) + { + for (int i = 0; i < MAX_WIIMOTES; ++i) + { + // One interface per Wii Remote + usbInterface[i] = dev.getInterface(i); + usbConnection.claimInterface(usbInterface[i], true); + + // One endpoint per Wii Remote. Input only + // Output reports go through the control channel. + usbIn[i] = usbInterface[i].getEndpoint(0); + Log.info("Interface " + i + " endpoint count:" + usbInterface[i].getEndpointCount()); + } + return true; + } + else + { + // XXX: Message that the device was found, but it needs to be unplugged and plugged back in? + usbConnection.close(); + } + } + } + } + return false; + } +} -- cgit v1.2.3 From e2e33becc9934f9af850fc2e2b5c77cf0eb15683 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Thu, 2 Jan 2025 17:22:29 +0100 Subject: Android: Detect GCAdapter connection using BroadcastReceiver We can register a BroadcastReceiver to have Android tell us when a GC adapter gets connected instead of having a loop where we continuously call SleepCurrentThread(1000) and poll the current status. When waiting for a GC adapter to connect, this both reduces power usage and improves responsiveness. Note that I made openAdapter get the UsbDevice that's been stored by the hotplug code instead of having openAdapter find the UsbDevice on its own like before. This is only because I want to ensure that the UsbDevice being tracked for disconnection is the same as the UsbDevice actually being used, in case the user has multiple adapters connected. --- .../org/dolphinemu/dolphinemu/utils/GCAdapter.java | 182 ++++++++++++++++----- 1 file changed, 139 insertions(+), 43 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java index 924496e488..12ed20fec7 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java @@ -3,8 +3,10 @@ package org.dolphinemu.dolphinemu.utils; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.hardware.usb.UsbConfiguration; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; @@ -16,10 +18,12 @@ import android.os.Build; import android.widget.Toast; import androidx.annotation.Keep; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import org.dolphinemu.dolphinemu.BuildConfig; import org.dolphinemu.dolphinemu.DolphinApplication; import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.services.USBPermService; import java.util.HashMap; import java.util.Map; @@ -36,6 +40,21 @@ public class GCAdapter static UsbEndpoint usbIn; static UsbEndpoint usbOut; + private static final String ACTION_GC_ADAPTER_PERMISSION_GRANTED = + BuildConfig.APPLICATION_ID + ".GC_ADAPTER_PERMISSION_GRANTED"; + + private static final Object hotplugCallbackLock = new Object(); + private static boolean hotplugCallbackEnabled = false; + private static UsbDevice adapterDevice = null; + private static BroadcastReceiver hotplugBroadcastReceiver = new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) + { + onUsbDevicesChanged(); + } + }; + private static void requestPermission() { HashMap devices = manager.getDeviceList(); @@ -47,11 +66,11 @@ public class GCAdapter if (!manager.hasPermission(dev)) { Context context = DolphinApplication.getAppContext(); - Intent intent = new Intent(context, USBPermService.class); int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_IMMUTABLE : 0; - PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, flags); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, + new Intent(ACTION_GC_ADAPTER_PERMISSION_GRANTED), flags); manager.requestPermission(dev, pendingIntent); } @@ -71,7 +90,16 @@ public class GCAdapter } @Keep - public static boolean queryAdapter() + public static boolean isUsbDeviceAvailable() + { + synchronized (hotplugCallbackLock) + { + return adapterDevice != null; + } + } + + @Nullable + private static UsbDevice queryAdapter() { HashMap devices = manager.getDeviceList(); for (Map.Entry pair : devices.entrySet()) @@ -80,12 +108,12 @@ public class GCAdapter if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) { if (manager.hasPermission(dev)) - return true; + return dev; else requestPermission(); } } - return false; + return null; } public static void initAdapter() @@ -109,50 +137,118 @@ public class GCAdapter @Keep public static boolean openAdapter() { - HashMap devices = manager.getDeviceList(); - for (Map.Entry pair : devices.entrySet()) + UsbDevice dev; + synchronized (hotplugCallbackLock) { - UsbDevice dev = pair.getValue(); - if (dev.getProductId() == 0x0337 && dev.getVendorId() == 0x057e) + dev = adapterDevice; + } + + if (dev != null) + { + usbConnection = manager.openDevice(dev); + + Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); + Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); + + if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) { - if (manager.hasPermission(dev)) + UsbConfiguration conf = dev.getConfiguration(0); + usbInterface = conf.getInterface(0); + usbConnection.claimInterface(usbInterface, true); + + Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount()); + + if (usbInterface.getEndpointCount() == 2) { - usbConnection = manager.openDevice(dev); - - Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); - Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); - - if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) - { - UsbConfiguration conf = dev.getConfiguration(0); - usbInterface = conf.getInterface(0); - usbConnection.claimInterface(usbInterface, true); - - Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount()); - - if (usbInterface.getEndpointCount() == 2) - { - for (int i = 0; i < usbInterface.getEndpointCount(); ++i) - if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) - usbIn = usbInterface.getEndpoint(i); - else - usbOut = usbInterface.getEndpoint(i); - - initAdapter(); - return true; - } + for (int i = 0; i < usbInterface.getEndpointCount(); ++i) + if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) + usbIn = usbInterface.getEndpoint(i); else - { - usbConnection.releaseInterface(usbInterface); - } - } - - Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, - Toast.LENGTH_LONG).show(); - usbConnection.close(); + usbOut = usbInterface.getEndpoint(i); + + initAdapter(); + return true; + } + else + { + usbConnection.releaseInterface(usbInterface); } } + + Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, + Toast.LENGTH_LONG).show(); + usbConnection.close(); } return false; } + + @Keep + public static void enableHotplugCallback() + { + synchronized (hotplugCallbackLock) + { + if (hotplugCallbackEnabled) + { + throw new IllegalStateException("enableHotplugCallback was called when already enabled"); + } + + IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + filter.addAction(ACTION_GC_ADAPTER_PERMISSION_GRANTED); + + ContextCompat.registerReceiver(DolphinApplication.getAppContext(), hotplugBroadcastReceiver, + filter, ContextCompat.RECEIVER_EXPORTED); + + hotplugCallbackEnabled = true; + + onUsbDevicesChanged(); + } + } + + @Keep + public static void disableHotplugCallback() + { + synchronized (hotplugCallbackLock) + { + if (hotplugCallbackEnabled) + { + DolphinApplication.getAppContext().unregisterReceiver(hotplugBroadcastReceiver); + hotplugCallbackEnabled = false; + adapterDevice = null; + } + } + } + + public static void onUsbDevicesChanged() + { + synchronized (hotplugCallbackLock) + { + if (adapterDevice != null) + { + boolean adapterStillConnected = manager.getDeviceList().entrySet().stream() + .anyMatch(pair -> pair.getValue().getDeviceId() == adapterDevice.getDeviceId()); + + if (!adapterStillConnected) + { + adapterDevice = null; + onAdapterDisconnected(); + } + } + + if (adapterDevice == null) + { + UsbDevice newAdapter = queryAdapter(); + if (newAdapter != null) + { + adapterDevice = newAdapter; + onAdapterConnected(); + } + } + } + } + + private static native void onAdapterConnected(); + + private static native void onAdapterDisconnected(); } -- cgit v1.2.3 From 8524e725a8e883a0c763b6d791095aba5ff5e5ab Mon Sep 17 00:00:00 2001 From: JosJuice Date: Sun, 27 Jul 2025 11:37:45 +0200 Subject: Android: Add additional null check in GCAdapter Just in case, since the documentation says this could be null. --- .../org/dolphinemu/dolphinemu/utils/GCAdapter.java | 63 ++++++++++++---------- 1 file changed, 34 insertions(+), 29 deletions(-) (limited to 'Source/Android/app/src/main/java') diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java index 12ed20fec7..b9e67e654f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GCAdapter.java @@ -142,43 +142,48 @@ public class GCAdapter { dev = adapterDevice; } + if (dev == null) + { + return false; + } - if (dev != null) + usbConnection = manager.openDevice(dev); + if (usbConnection == null) { - usbConnection = manager.openDevice(dev); + return false; + } - Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); - Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); + Log.info("GCAdapter: Number of configurations: " + dev.getConfigurationCount()); + Log.info("GCAdapter: Number of interfaces: " + dev.getInterfaceCount()); - if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) - { - UsbConfiguration conf = dev.getConfiguration(0); - usbInterface = conf.getInterface(0); - usbConnection.claimInterface(usbInterface, true); + if (dev.getConfigurationCount() > 0 && dev.getInterfaceCount() > 0) + { + UsbConfiguration conf = dev.getConfiguration(0); + usbInterface = conf.getInterface(0); + usbConnection.claimInterface(usbInterface, true); - Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount()); + Log.info("GCAdapter: Number of endpoints: " + usbInterface.getEndpointCount()); - if (usbInterface.getEndpointCount() == 2) - { - for (int i = 0; i < usbInterface.getEndpointCount(); ++i) - if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) - usbIn = usbInterface.getEndpoint(i); - else - usbOut = usbInterface.getEndpoint(i); - - initAdapter(); - return true; - } - else - { - usbConnection.releaseInterface(usbInterface); - } + if (usbInterface.getEndpointCount() == 2) + { + for (int i = 0; i < usbInterface.getEndpointCount(); ++i) + if (usbInterface.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN) + usbIn = usbInterface.getEndpoint(i); + else + usbOut = usbInterface.getEndpoint(i); + + initAdapter(); + return true; + } + else + { + usbConnection.releaseInterface(usbInterface); } - - Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, - Toast.LENGTH_LONG).show(); - usbConnection.close(); } + + Toast.makeText(DolphinApplication.getAppContext(), R.string.replug_gc_adapter, + Toast.LENGTH_LONG).show(); + usbConnection.close(); return false; } -- cgit v1.2.3