diff --git a/api/system-current.txt b/api/system-current.txt index e931fe432ba4b..1bd28fca0bfe1 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3673,8 +3673,13 @@ package android.hardware.usb { method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget(); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); + field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE"; field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; + field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END"; + field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START"; + field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT"; + field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME"; field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L field public static final long FUNCTION_ADB = 1L; // 0x1L field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java index a94266b811d5f..f4cfc74aa77d0 100644 --- a/core/java/android/hardware/usb/UsbAccessory.java +++ b/core/java/android/hardware/usb/UsbAccessory.java @@ -24,7 +24,6 @@ import android.os.Parcelable; import android.os.RemoteException; import com.android.internal.util.Preconditions; -import java.util.Objects; import java.util.Objects; diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index d16f070f72093..ef305e2c1867a 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -159,6 +159,22 @@ public class UsbManager { public static final String ACTION_USB_ACCESSORY_DETACHED = "android.hardware.usb.action.USB_ACCESSORY_DETACHED"; + /** + * Broadcast Action: A broadcast for USB accessory handshaking information delivery + * + * This intent is sent when a USB accessory connect attempt + * + *

For more information about communicating with USB accessory handshake, refer to + * AOA developer guide.

+ * + * {@hide} + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SystemApi + @RequiresPermission(Manifest.permission.MANAGE_USB) + public static final String ACTION_USB_ACCESSORY_HANDSHAKE = + "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE"; + /** * Boolean extra indicating whether USB is connected or disconnected. * Used in extras for the {@link #ACTION_USB_STATE} broadcast. @@ -302,6 +318,52 @@ public class UsbManager { */ public static final String EXTRA_ACCESSORY = "accessory"; + /** + * A long extra indicating ms from boot to get get_protocol UEvent + * This is obtained with SystemClock.elapsedRealtime() + * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts. + * + * {@hide} + */ + @SystemApi + public static final String EXTRA_ACCESSORY_UEVENT_TIME = + "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME"; + + /** + * An integer extra indicating numbers of send string during handshake + * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts + * + *

For more information about control request with identifying string information + * between communicating with USB accessory handshake, refer to + * AOA developer guide.

+ * + * {@hide} + */ + @SystemApi + public static final String EXTRA_ACCESSORY_STRING_COUNT = + "android.hardware.usb.extra.ACCESSORY_STRING_COUNT"; + + /** + * Boolean extra indicating whether got start accessory or not + * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts. + * + * {@hide} + */ + @SystemApi + public static final String EXTRA_ACCESSORY_START = + "android.hardware.usb.extra.ACCESSORY_START"; + + /** + * A long extra indicating ms from boot to sent {@link #ACTION_USB_ACCESSORY_HANDSHAKE} + * This is obtained with SystemClock.elapsedRealtime() + * Used in extras for {@link #ACTION_USB_ACCESSORY_HANDSHAKE} broadcasts. + * + * {@hide} + */ + @SystemApi + public static final String EXTRA_ACCESSORY_HANDSHAKE_END = + "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END"; + /** * Name of extra added to the {@link android.app.PendingIntent} * passed into {@link #requestPermission(UsbDevice, PendingIntent)} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e87b82a0fd0fb..2276252a4e8ee 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -263,6 +263,7 @@ + diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 53499f8165a21..4a6a0ee92301d 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -3613,8 +3613,13 @@ package android.hardware.usb { method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget(); method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); + field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_ACCESSORY_HANDSHAKE = "android.hardware.usb.action.USB_ACCESSORY_HANDSHAKE"; field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; + field public static final String EXTRA_ACCESSORY_HANDSHAKE_END = "android.hardware.usb.extra.ACCESSORY_HANDSHAKE_END"; + field public static final String EXTRA_ACCESSORY_START = "android.hardware.usb.extra.ACCESSORY_START"; + field public static final String EXTRA_ACCESSORY_STRING_COUNT = "android.hardware.usb.extra.ACCESSORY_STRING_COUNT"; + field public static final String EXTRA_ACCESSORY_UEVENT_TIME = "android.hardware.usb.extra.ACCESSORY_UEVENT_TIME"; field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L field public static final long FUNCTION_ADB = 1L; // 0x1L field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 7595e3f249ced..57345f1c0e218 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -164,6 +164,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17; private static final int MSG_GADGET_HAL_REGISTERED = 18; private static final int MSG_RESET_USB_GADGET = 19; + private static final int MSG_ACCESSORY_HANDSHAKE_TIMEOUT = 20; + private static final int MSG_INCREASE_SENDSTRING_COUNT = 21; private static final int AUDIO_MODE_SOURCE = 1; @@ -176,6 +178,13 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser // Request is cancelled if host does not configure device within 10 seconds. private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000; + /** + * Timeout for receiving USB accessory request + * Reset when receive next control request + * Broadcast intent if not receive next control request or enter next step. + */ + private static final int ACCESSORY_HANDSHAKE_TIMEOUT = 10 * 1000; + private static final String BOOT_MODE_PROPERTY = "ro.bootmode"; private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv"; @@ -222,8 +231,19 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); + } else if ("GETPROTOCOL".equals(accessory)) { + if (DEBUG) Slog.d(TAG, "got accessory get protocol"); + long elapsedRealtime = SystemClock.elapsedRealtime(); + mHandler.setAccessoryUEventTime(elapsedRealtime); + resetAccessoryHandshakeTimeoutHandler(); + } else if ("SENDSTRING".equals(accessory)) { + if (DEBUG) Slog.d(TAG, "got accessory send string"); + mHandler.sendEmptyMessage(MSG_INCREASE_SENDSTRING_COUNT); + resetAccessoryHandshakeTimeoutHandler(); } else if ("START".equals(accessory)) { if (DEBUG) Slog.d(TAG, "got accessory start"); + mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT); + mHandler.setStartAccessoryTrue(); startAccessoryMode(); } } @@ -395,6 +415,23 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS); } + /* + * Start the timeout-timer upon receiving "get_protocol" uevent. + * Restart the timer every time if any succeeding uevnet received. + * (Only when USB is under accessory mode) + *

About the details of the related control request and sequence ordering, refer to + * AOA developer guide.

+ */ + private void resetAccessoryHandshakeTimeoutHandler() { + long functions = getCurrentFunctions(); + // some accesories send get_protocol after start accessory + if ((functions & UsbManager.FUNCTION_ACCESSORY) == 0) { + mHandler.removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT), + ACCESSORY_HANDSHAKE_TIMEOUT); + } + } + private void startAccessoryMode() { if (!mHasUsbAccessory) return; @@ -416,6 +453,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser if (functions != UsbManager.FUNCTION_NONE) { mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_MODE_ENTER_TIMEOUT), ACCESSORY_REQUEST_TIMEOUT); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_ACCESSORY_HANDSHAKE_TIMEOUT), + ACCESSORY_HANDSHAKE_TIMEOUT); setCurrentFunctions(functions); } } @@ -468,6 +507,15 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser private int mMidiCard; private int mMidiDevice; + /** + * mAccessoryConnectionStartTime record the timing of start_accessory + * mSendStringCount count the number of receiving uevent send_string + * mStartAccessory record whether start_accessory is received + */ + private long mAccessoryConnectionStartTime = 0L; + private int mSendStringCount = 0; + private boolean mStartAccessory = false; + private final Context mContext; private final UsbAlsaManager mUsbAlsaManager; private final UsbPermissionManager mPermissionManager; @@ -645,6 +693,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser // defer accessoryAttached if system is not ready if (mBootCompleted) { mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory); + removeMessages(MSG_ACCESSORY_HANDSHAKE_TIMEOUT); + broadcastUsbAccessoryHandshake(); } // else handle in boot completed } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); @@ -701,6 +751,30 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser return false; } + private void broadcastUsbAccessoryHandshake() { + long elapsedRealtime = SystemClock.elapsedRealtime(); + long accessoryHandShakeEnd = elapsedRealtime; + + // send a sticky broadcast containing USB accessory handshake information + Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_HANDSHAKE) + .putExtra(UsbManager.EXTRA_ACCESSORY_UEVENT_TIME, + mAccessoryConnectionStartTime) + .putExtra(UsbManager.EXTRA_ACCESSORY_STRING_COUNT, + mSendStringCount) + .putExtra(UsbManager.EXTRA_ACCESSORY_START, + mStartAccessory) + .putExtra(UsbManager.EXTRA_ACCESSORY_HANDSHAKE_END, + accessoryHandShakeEnd); + + if (DEBUG) { + Slog.d(TAG, "broadcasting " + intent + " extras: " + intent.getExtras()); + } + + sendStickyBroadcast(intent); + resetUsbAccessoryHandshakeDebuggingInfo(); + accessoryHandShakeEnd = 0L; + } + protected void updateUsbStateBroadcastIfNeeded(long functions) { // send a sticky broadcast containing current USB state Intent intent = new Intent(UsbManager.ACTION_USB_STATE); @@ -998,6 +1072,16 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } break; } + case MSG_ACCESSORY_HANDSHAKE_TIMEOUT: { + if (DEBUG) { + Slog.v(TAG, "Accessory handshake timeout"); + } + broadcastUsbAccessoryHandshake(); + break; + } + case MSG_INCREASE_SENDSTRING_COUNT: { + mSendStringCount = mSendStringCount + 1; + } } } @@ -1015,6 +1099,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser } if (mCurrentAccessory != null) { mUsbDeviceManager.getCurrentSettings().accessoryAttached(mCurrentAccessory); + broadcastUsbAccessoryHandshake(); } updateUsbNotification(false); @@ -1301,6 +1386,20 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser * Evaluates USB function policies and applies the change accordingly. */ protected abstract void setEnabledFunctions(long functions, boolean forceRestart); + + public void setAccessoryUEventTime(long accessoryConnectionStartTime) { + mAccessoryConnectionStartTime = accessoryConnectionStartTime; + } + + public void setStartAccessoryTrue() { + mStartAccessory = true; + } + + public void resetUsbAccessoryHandshakeDebuggingInfo() { + mAccessoryConnectionStartTime = 0L; + mSendStringCount = 0; + mStartAccessory = false; + } } private static final class UsbHandlerLegacy extends UsbHandler {