Connect Android USB Accessory when screen is off

2019-06-10 02:05发布

问题:

Application Info

I've got an application that is able to communicate with a USB accessory when the screen is on.

The first thing I do, after installing the app, is turn the screen on and then plug in the USB accessory. I get the message "Open when this USB accessory is connected?". Additionally, there is a checkbox that says "Use by default for this USB accessory". I click the checkbox and press OK.[1]

Now, whenever I plug in the USB accessory, my application pops to the foreground. The reason this happens is because I have the following in my AndroidManifest.xml:

<intent-filter>
    <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" android:resource="@xml/accessory_filter" />

After this happens, I then call enable() that is defined here:

public boolean enable() {
    UsbManager deviceManager = null;
    UsbAccessory[] accessories = null;
    UsbAccessory accessory = null;

    deviceManager = (UsbManager) context
        .getSystemService(Context.USB_SERVICE);

    accessories = deviceManager.getAccessoryList();
    accessory = accessories[0];
    if (accessory == null) {
        return false;
    }

    logDRemote("Accessory is " + accessory);
    // If we have permission to access the accessory,
    if (deviceManager.hasPermission(accessory)) {
        parcelFileDescriptor = deviceManager
                .openAccessory(accessory);

        if (parcelFileDescriptor != null) {
            logDRemote("descriptor is not null");

            if(parcelFileDescriptor == null) {
                logDRemote("parcelFileDescriptor == null");
                return false;
            }

            outputStream = new FileOutputStream(
                    parcelFileDescriptor.getFileDescriptor());

            if(outputStream == null) {
                logDRemote( "outputStream == null");
                return false;
            }

            logDRemote("outputStream open");

            byte[] data = new byte[2];
            data[0] = (byte) APP_CONNECT;
            data[1] = 0;
            try {
                outputStream.write(data);
            } catch (IOException e) {
                logDRemote("Could not open accessory");
                closeAccessory();
                return RETURN_CODES.FILE_DESCRIPTOR_WOULD_NOT_OPEN;
            }

            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

When I call this after the activity pops to the foreground, I call this function and it returns true, showing that the USB device is properly communicating.

Failure with swipe up screen

The problem is, if I have my screen off without a PIN but I have to swipe up to unlock the phone, the following happens:

  • When I plug in the USB accessory, the screen turns on and says "swipe up"
  • My activity does not pop up until after I swipe up on the screen
  • If I never swipe on on the screen and try calling enable(), I get a failure at "Could not open accessory"
    • That is, the device is able to open properly, and the permissions exist. But it just can't actually write to the accessory when it tries.

Fix for swipe up screen failure

I found a way to fix this first issue. What I was able to do is open up the activity whenever I detect that a USB charger is present[3]. Now the following happens:

  • I start by installing the application with the USB unplugged
  • I turn off the screen
  • I plug in the USB device
  • The activity pops up
  • A second activity pops up. I believe that second activity popping up is the Android system opening the popup as a result of the USB attach event[2]
  • I call enable(), and the write succeeds.

Failure with pin lock

However, this fix does not work when I have a pin set on the phone. Now the following happens:

  • I start by installing the application with the USB unplugged.
  • I turn off the screen
  • I plug in the USB device
  • The activity pops up
  • A second activity does not pop up
  • I try calling enable(), and the write fails.

The Final Question

How do I connect to a USB device when my Android phone is off and has a pin set? I have root, if that helps.

Footnotes

  1. As an aside, this writes to /data/system/users/0/usb_device_manager.xml, as seen in the source here: http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#mSettingsFile

  2. http://androidxref.com/7.0.0_r1/xref/frameworks/base/services/usb/java/com/android/server/usb/UsbSettingsManager.java#775

  3. I'm putting the following in onCreate of the activity, so that it can show up while the screen is off:

-

  final Window win= getWindow();
    win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
    win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);