OSDN Git Service

UsbDeviceManager: Fix race condition entering USB accessory mode
authorMike Lockwood <lockwood@google.com>
Wed, 19 Feb 2014 19:18:02 +0000 (11:18 -0800)
committerMike Lockwood <lockwood@google.com>
Wed, 19 Feb 2014 19:23:19 +0000 (11:23 -0800)
When switching USB modes there are often spurious connect and disconnect events
that occur before reenumeration is complete.  There is currently a 1000ms timer
to "debounce" the disconnect events. But with some USB accessories, this timeout
is not long enough, which results in an endless cycle of attempts to enter
USB accessory mode when the phone is connected.

To fix this, we now wait up to 10 seconds for the host to successfully configure
the device when entering USB accessory mode before giving up.
This is separate from the existing debounce timer, so the behavior of the
USB state change broadcasts are not affected.

Bug: 12877769
Change-Id: I7aa61f8a618546d749a7ddfc97bf103029a73d03

services/java/com/android/server/usb/UsbDeviceManager.java

index 5a60de0..8a5e291 100644 (file)
@@ -98,6 +98,13 @@ public class UsbDeviceManager {
     // which need debouncing.
     private static final int UPDATE_DELAY = 1000;
 
+    // Time we received a request to enter USB accessory mode
+    private long mAccessoryModeRequestTime = 0;
+
+    // Timeout for entering USB request mode.
+    // Request is cancelled if host does not configure device within 10 seconds.
+    private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000;
+
     private static final String BOOT_MODE_PROPERTY = "ro.bootmode";
 
     private UsbHandler mHandler;
@@ -205,6 +212,8 @@ public class UsbDeviceManager {
     }
 
     private void startAccessoryMode() {
+        if (!mHasUsbAccessory) return;
+
         mAccessoryStrings = nativeGetAccessoryStrings();
         boolean enableAudio = (nativeGetAudioMode() == AUDIO_MODE_SOURCE);
         // don't start accessory mode if our mandatory strings have not been set
@@ -223,6 +232,7 @@ public class UsbDeviceManager {
         }
 
         if (functions != null) {
+            mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
             setCurrentFunctions(functions, false);
         }
     }
@@ -456,6 +466,8 @@ public class UsbDeviceManager {
         }
 
         private void setEnabledFunctions(String functions, boolean makeDefault) {
+            if (DEBUG) Slog.d(TAG, "setEnabledFunctions " + functions
+                    + " makeDefault: " + makeDefault);
 
             // Do not update persystent.sys.usb.config if the device is booted up
             // with OEM specific mode.
@@ -517,9 +529,17 @@ public class UsbDeviceManager {
         }
 
         private void updateCurrentAccessory() {
-            if (!mHasUsbAccessory) return;
+            // We are entering accessory mode if we have received a request from the host
+            // and the request has not timed out yet.
+            boolean enteringAccessoryMode =
+                    mAccessoryModeRequestTime > 0 &&
+                        SystemClock.elapsedRealtime() <
+                            mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT;
+
+            if (mConfigured && enteringAccessoryMode) {
+                // successfully entered accessory mode
+                mAccessoryModeRequestTime = 0;
 
-            if (mConfigured) {
                 if (mAccessoryStrings != null) {
                     mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
                     Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
@@ -530,7 +550,7 @@ public class UsbDeviceManager {
                 } else {
                     Slog.e(TAG, "nativeGetAccessoryStrings failed");
                 }
-            } else if (!mConnected) {
+            } else if (!enteringAccessoryMode) {
                 // make sure accessory mode is off
                 // and restore default functions
                 Slog.d(TAG, "exited USB accessory mode");
@@ -560,6 +580,8 @@ public class UsbDeviceManager {
                 }
             }
 
+            if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " connected: " + mConnected
+                                    + " configured: " + mConfigured);
             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
         }
 
@@ -599,9 +621,7 @@ public class UsbDeviceManager {
                     if (containsFunction(mCurrentFunctions,
                             UsbManager.USB_FUNCTION_ACCESSORY)) {
                         updateCurrentAccessory();
-                    }
-
-                    if (!mConnected) {
+                    } else if (!mConnected) {
                         // restore defaults when USB is disconnected
                         setEnabledFunctions(mDefaultFunctions, false);
                     }