diff options
| author | 2015-06-30 17:57:12 -0700 | |
|---|---|---|
| committer | 2015-07-09 12:55:56 -0700 | |
| commit | 460a146eb8f827e4e70f2dd93d1ba852d0feb06b (patch) | |
| tree | 6255bfcddfa68711bcb8abd07cd673ffe7d1c66d | |
| parent | 6826b25c223c91d603f3a8e2b0d1ce5abe1e13e5 (diff) | |
Clean up USB Manager and fix ADB.
Moved functions which parse the USB functions list into one common
place on UsbManager.
Deleted the no longer supported USB_FUNCTION_MASS_STORAGE.
Ensured that the UserManager.DISALLOW_USB_FILE_TRANSFER rule is
consistently applied during user switch and when changing the
current USB functions and make sure it only affects MTP and PTP.
Collapsed the boot completed and user switched receivers to
ensure consistent ordering of side-effects.
Validate the list of functions passed to setCurrentFunction() so
that the separation of concerns is clearer. It was somewhat
ambiguous as to whether functions such as ADB could / should be
enabled through that interface. Improved the docs for clarity.
Fixed a bunch of broken stuff related to the USB config
persistent property (list of default functions) that could cause
ADB and other functions to not work at all. Added new failsafes
to ensure that we reliably get back into a happy state.
Bug: 22206076
Change-Id: I02915ddfce7193a8f67a14f0d76bab22fc575dfa
7 files changed, 321 insertions, 247 deletions
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 31a6a962e2b9..0fe112c33e27 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -82,6 +82,9 @@ interface IUsbManager /* Clears default preferences and permissions for the package */ void clearDefaults(String packageName, int userId); + /* Returns true if the specified USB function is enabled. */ + boolean isFunctionEnabled(String function); + /* Sets the current USB function. */ void setCurrentFunction(String function); diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index c83f4668b81a..f58b9d6dd14e 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -22,7 +22,6 @@ import android.content.Context; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.RemoteException; -import android.os.SystemProperties; import android.util.Log; import java.util.HashMap; @@ -54,8 +53,6 @@ public class UsbManager { * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected. * <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured. * currently zero if not configured, one for configured. - * <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the - * mass storage function is enabled * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the * adb function is enabled * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the @@ -152,12 +149,13 @@ public class UsbManager { public static final String USB_DATA_UNLOCKED = "unlocked"; /** - * Name of the USB mass storage USB function. - * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * A placeholder indicating that no USB function is being specified. + * Used to distinguish between selecting no function vs. the default function in + * {@link #setCurrentFunction(String)}. * * {@hide} */ - public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage"; + public static final String USB_FUNCTION_NONE = "none"; /** * Name of the adb USB function. @@ -218,15 +216,14 @@ public class UsbManager { /** * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts - * containing the UsbDevice object for the device. + * containing the {@link UsbDevice} object for the device. */ - public static final String EXTRA_DEVICE = "device"; /** * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts - * containing the UsbAccessory object for the accessory. + * containing the {@link UsbAccessory} object for the accessory. */ public static final String EXTRA_ACCESSORY = "accessory"; @@ -238,23 +235,6 @@ public class UsbManager { */ public static final String EXTRA_PERMISSION_GRANTED = "permission"; - /** - * The persistent property which stores whether adb is enabled or not. Other values are ignored. - * Previously this value stored non-adb settings, but not anymore. - * TODO: rename this to something adb specific, rather than using usb. - * - * {@hide} - */ - public static final String ADB_PERSISTENT_PROPERTY = "persist.sys.usb.config"; - - /** - * The non-persistent property which stores the current USB settings. - * - * {@hide} - */ - public static final String USB_SETTINGS_PROPERTY = "sys.usb.config"; - - private final Context mContext; private final IUsbManager mService; @@ -437,31 +417,44 @@ public class UsbManager { } } - private static boolean propertyContainsFunction(String property, String function) { - String functions = SystemProperties.get(property, ""); - int index = functions.indexOf(function); - if (index < 0) return false; - if (index > 0 && functions.charAt(index - 1) != ',') return false; - int charAfter = index + function.length(); - if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; - return true; - } - /** - * Returns true if the specified USB function is currently enabled. + * Returns true if the specified USB function is currently enabled when in device mode. + * <p> + * USB functions represent interfaces which are published to the host to access + * services offered by the device. + * </p> * * @param function name of the USB function - * @return true if the USB function is enabled. + * @return true if the USB function is enabled * * {@hide} */ public boolean isFunctionEnabled(String function) { - return propertyContainsFunction(USB_SETTINGS_PROPERTY, function); + try { + return mService.isFunctionEnabled(function); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in setCurrentFunction", e); + return false; + } } /** - * Sets the current USB function. - * If function is null, then the current function is set to the default function. + * Sets the current USB function when in device mode. + * <p> + * USB functions represent interfaces which are published to the host to access + * services offered by the device. + * </p><p> + * This method is intended to select among primary USB functions. The system may + * automatically activate additional functions such as {@link #USB_FUNCTION_ADB} + * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states. + * </p><p> + * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE}, + * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, + * or {@link #USB_FUNCTION_RNDIS}. + * </p><p> + * Note: This function is asynchronous and may fail silently without applying + * the requested changes. + * </p> * * @param function name of the USB function, or null to restore the default function * @@ -477,8 +470,9 @@ public class UsbManager { /** * Sets whether USB data (for example, MTP exposed pictures) should be made available - * on the USB connection. Unlocking usb data should only be done with user involvement, - * since exposing pictures or other data could leak sensitive user information. + * on the USB connection when in device mode. Unlocking usb data should only be done with + * user involvement, since exposing pictures or other data could leak sensitive + * user information. * * {@hide} */ @@ -491,7 +485,8 @@ public class UsbManager { } /** - * Returns {@code true} iff access to sensitive USB data is currently allowed. + * Returns {@code true} iff access to sensitive USB data is currently allowed when + * in device mode. * * {@hide} */ @@ -504,4 +499,51 @@ public class UsbManager { return false; } + /** @hide */ + public static String addFunction(String functions, String function) { + if ("none".equals(functions)) { + return function; + } + if (!containsFunction(functions, function)) { + if (functions.length() > 0) { + functions += ","; + } + functions += function; + } + return functions; + } + + /** @hide */ + public static String removeFunction(String functions, String function) { + String[] split = functions.split(","); + for (int i = 0; i < split.length; i++) { + if (function.equals(split[i])) { + split[i] = null; + } + } + if (split.length == 1 && split[0] == null) { + return "none"; + } + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < split.length; i++) { + String s = split[i]; + if (s != null) { + if (builder.length() > 0) { + builder.append(","); + } + builder.append(s); + } + } + return builder.toString(); + } + + /** @hide */ + public static boolean containsFunction(String functions, String function) { + int index = functions.indexOf(function); + if (index < 0) return false; + if (index > 0 && functions.charAt(index - 1) != ',') return false; + int charAfter = index + function.length(); + if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; + return true; + } } diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 680a1ab7d564..f6cdb3131cfb 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2879,8 +2879,6 @@ <string name="usb_ptp_notification_title">USB for photo transfer</string> <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in MIDI mode. This is the title --> <string name="usb_midi_notification_title">USB for MIDI</string> - <!-- USB_PREFERENCES: Notification for when the user connects the phone to a computer via USB in mass storage mode (for installer CD image). This is the title --> - <string name="usb_cd_installer_notification_title">Connected as an installer</string> <!-- USB_PREFERENCES: Notification for when a USB accessory is attached. This is the title --> <string name="usb_accessory_notification_title">Connected to a USB accessory</string> <!-- See USB_PREFERENCES. This is the message. --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 5de010d222cc..fcdaba29c02f 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1799,7 +1799,6 @@ <java-symbol type="string" name="tethered_notification_message" /> <java-symbol type="string" name="tethered_notification_title" /> <java-symbol type="string" name="usb_accessory_notification_title" /> - <java-symbol type="string" name="usb_cd_installer_notification_title" /> <java-symbol type="string" name="usb_mtp_notification_title" /> <java-symbol type="string" name="usb_charging_notification_title" /> <java-symbol type="string" name="usb_notification_message" /> diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 5040bd036a18..c3174a398f83 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -60,7 +60,6 @@ import android.content.pm.ServiceInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; @@ -5434,10 +5433,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 0, userHandle); - } else if (UserManager.DISALLOW_USB_FILE_TRANSFER.equals(key)) { - UsbManager manager = - (UsbManager) mContext.getSystemService(Context.USB_SERVICE); - manager.setCurrentFunction("none"); } else if (UserManager.DISALLOW_SHARE_LOCATION.equals(key)) { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index e7716c234d40..81b4857351c0 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -19,12 +19,10 @@ package com.android.server.usb; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -54,7 +52,6 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -67,9 +64,25 @@ import java.util.Scanner; */ public class UsbDeviceManager { - private static final String TAG = UsbDeviceManager.class.getSimpleName(); + private static final String TAG = "UsbDeviceManager"; private static final boolean DEBUG = false; + /** + * The persistent property which stores whether adb is enabled or not. + * May also contain vendor-specific default functions for testing purposes. + */ + private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config"; + + /** + * The non-persistent property which stores the current USB settings. + */ + private static final String USB_CONFIG_PROPERTY = "sys.usb.config"; + + /** + * The non-persistent property which stores the current USB actual state. + */ + private static final String USB_STATE_PROPERTY = "sys.usb.state"; + private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = @@ -92,6 +105,7 @@ public class UsbDeviceManager { private static final int MSG_BOOT_COMPLETED = 4; private static final int MSG_USER_SWITCHED = 5; private static final int MSG_SET_USB_DATA_UNLOCKED = 6; + private static final int MSG_UPDATE_USER_RESTRICTIONS = 7; private static final int AUDIO_MODE_SOURCE = 1; @@ -184,12 +198,6 @@ public class UsbDeviceManager { } } - public void setCurrentSettings(UsbSettingsManager settings) { - synchronized (mLock) { - mCurrentSettings = settings; - } - } - private UsbSettingsManager getCurrentSettings() { synchronized (mLock) { return mCurrentSettings; @@ -221,6 +229,22 @@ public class UsbDeviceManager { mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } + public void bootCompleted() { + if (DEBUG) Slog.d(TAG, "boot completed"); + mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); + } + + public void setCurrentUser(int userId, UsbSettingsManager settings) { + synchronized (mLock) { + mCurrentSettings = settings; + mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); + } + } + + public void updateUserRestrictions() { + mHandler.sendEmptyMessage(MSG_UPDATE_USER_RESTRICTIONS); + } + private void startAccessoryMode() { if (!mHasUsbAccessory) return; @@ -270,46 +294,6 @@ public class UsbDeviceManager { } } - private static String addFunction(String functions, String function) { - if ("none".equals(functions)) { - return function; - } - if (!containsFunction(functions, function)) { - if (functions.length() > 0) { - functions += ","; - } - functions += function; - } - return functions; - } - - private static String removeFunction(String functions, String function) { - String[] split = functions.split(","); - for (int i = 0; i < split.length; i++) { - if (function.equals(split[i])) { - split[i] = null; - } - } - if (split.length == 1 && split[0] == null) { - return "none"; - } - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < split.length; i++) { - String s = split[i]; - if (s != null) { - if (builder.length() > 0) { - builder.append(","); - } - builder.append(s); - } - } - return builder.toString(); - } - - private static boolean containsFunction(String functions, String function) { - return Arrays.asList(functions.split(",")).contains(function); - } - private final class UsbHandler extends Handler { // current USB state @@ -317,49 +301,24 @@ public class UsbDeviceManager { private boolean mConfigured; private boolean mUsbDataUnlocked; private String mCurrentFunctions; + private boolean mCurrentFunctionsApplied; private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; - private String mDefaultFunctions; private boolean mAdbNotificationShown; private int mCurrentUser = UserHandle.USER_NULL; - private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) Slog.d(TAG, "boot completed"); - mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); - } - }; - - private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); - } - }; - public UsbHandler(Looper looper) { super(looper); try { - // TODO: rename persist.sys.usb.config to something more descriptive. - // persist.sys.usb.config should never be unset. But if it is, set it to "adb" - // so we have a chance of debugging what happened. - mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb"); - - // sanity check the sys.usb.config system property - // this may be necessary if we crashed while switching USB configurations - String config = SystemProperties.get("sys.usb.config", "none"); - if (!config.equals(mDefaultFunctions)) { - Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions); - SystemProperties.set("sys.usb.config", mDefaultFunctions); - } - - mAdbEnabled = containsFunction( - SystemProperties.get(UsbManager.ADB_PERSISTENT_PROPERTY, "adb"), + // Restore default functions. + mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_NONE); + mCurrentFunctionsApplied = mCurrentFunctions.equals( + SystemProperties.get(USB_STATE_PROPERTY)); + mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(), UsbManager.USB_FUNCTION_ADB); + setEnabledFunctions(null, false); - mCurrentFunctions = getDefaultFunctions(); String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); @@ -371,12 +330,6 @@ public class UsbDeviceManager { // Watch for USB configuration changes mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); - - IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); - filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiver(mBootCompletedReceiver, filter); - mContext.registerReceiver( - mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } @@ -420,34 +373,26 @@ public class UsbDeviceManager { sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0); } - private void updatePersistentProperty() { - String newValue = getDefaultFunctions(); - String value = SystemProperties.get(UsbManager.ADB_PERSISTENT_PROPERTY); - if (DEBUG) { Slog.d(TAG, "updatePersistentProperty newValue=" + newValue + " value=" + value); } - if (!newValue.equals(value)) { - SystemProperties.set(UsbManager.ADB_PERSISTENT_PROPERTY, getDefaultFunctions()); - } - waitForState(newValue); - } - private boolean waitForState(String state) { // wait for the transition to complete. // give up after 1 second. + String value = null; for (int i = 0; i < 20; i++) { // State transition is done when sys.usb.state is set to the new configuration - if (state.equals(SystemProperties.get("sys.usb.state"))) return true; + value = SystemProperties.get(USB_STATE_PROPERTY); + if (state.equals(value)) return true; SystemClock.sleep(50); } - Slog.e(TAG, "waitForState(" + state + ") FAILED"); + Slog.e(TAG, "waitForState(" + state + ") FAILED: got " + value); return false; } private boolean setUsbConfig(String config) { if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration - String oldConfig = SystemProperties.get(UsbManager.USB_SETTINGS_PROPERTY); + String oldConfig = SystemProperties.get(USB_CONFIG_PROPERTY); if (!config.equals(oldConfig)) { - SystemProperties.set(UsbManager.USB_SETTINGS_PROPERTY, config); + SystemProperties.set(USB_CONFIG_PROPERTY, config); } return waitForState(config); } @@ -456,54 +401,110 @@ public class UsbDeviceManager { if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; + // Due to the persist.sys.usb.config property trigger, changing adb state requires // persisting default function - updatePersistentProperty(); + String oldFunctions = getDefaultFunctions(); + String newFunctions = applyAdbFunction(oldFunctions); + if (!oldFunctions.equals(newFunctions)) { + SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunctions); + } + // After persisting them use the lock-down aware function set - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(mCurrentFunctions, false); updateAdbNotification(); } + if (mDebuggingManager != null) { mDebuggingManager.setAdbEnabled(mAdbEnabled); } } /** - * Stop and start the USB driver. This is needed to close all outstanding - * USB connections. + * Evaluates USB function policies and applies the change accordingly. */ - private void restartCurrentFunction() { - setUsbConfig("none"); - setUsbConfig(mCurrentFunctions); - } + private void setEnabledFunctions(String functions, boolean forceRestart) { + if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", " + + "forceRestart=" + forceRestart); + + // Try to set the enabled functions. + final String oldFunctions = mCurrentFunctions; + final boolean oldFunctionsApplied = mCurrentFunctionsApplied; + if (trySetEnabledFunctions(functions, forceRestart)) { + return; + } - private void setEnabledFunctions(String functions) { - if (DEBUG) Slog.d(TAG, "setEnabledFunctions " + functions); + // Didn't work. Try to revert changes. + // We always reapply the policy in case certain constraints changed such as + // user restrictions independently of any other new functions we were + // trying to activate. + if (oldFunctionsApplied && !oldFunctions.equals(functions)) { + Slog.e(TAG, "Failsafe 1: Restoring previous USB functions."); + if (trySetEnabledFunctions(oldFunctions, false)) { + return; + } + } + // Still didn't work. Try to restore the default functions. + Slog.e(TAG, "Failsafe 2: Restoring default USB functions."); + if (trySetEnabledFunctions(null, false)) { + return; + } + + // Now we're desperate. Ignore the default functions. + // Try to get ADB working if enabled. + Slog.e(TAG, "Failsafe 3: Restoring empty function list (with ADB if enabled)."); + if (trySetEnabledFunctions(UsbManager.USB_FUNCTION_NONE, false)) { + return; + } + + // Ouch. + Slog.e(TAG, "Unable to set any USB functions!"); + } + + private boolean trySetEnabledFunctions(String functions, boolean forceRestart) { if (functions == null) { functions = getDefaultFunctions(); } + functions = applyAdbFunction(functions); + functions = applyUserRestrictions(functions); + + if (!mCurrentFunctions.equals(functions) || !mCurrentFunctionsApplied + || forceRestart) { + Slog.i(TAG, "Setting USB config to " + functions); + mCurrentFunctions = functions; + mCurrentFunctionsApplied = false; + + // Kick the USB stack to close existing connections. + setUsbConfig(UsbManager.USB_FUNCTION_NONE); + + // Set the new USB configuration. + if (!setUsbConfig(functions)) { + Slog.e(TAG, "Failed to switch USB config to " + functions); + return false; + } + + mCurrentFunctionsApplied = true; + } + return true; + } + private String applyAdbFunction(String functions) { if (mAdbEnabled) { - functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); + functions = UsbManager.addFunction(functions, UsbManager.USB_FUNCTION_ADB); } else { - functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_ADB); } - if (!mCurrentFunctions.equals(functions)) { - if (!setUsbConfig("none")) { - Slog.e(TAG, "Failed to disable USB"); - // revert to previous configuration if we fail - setUsbConfig(mCurrentFunctions); - return; - } - if (setUsbConfig(functions)) { - mCurrentFunctions = functions; - } else { - Slog.e(TAG, "Failed to switch USB config to " + functions); - // revert to previous configuration if we fail - setUsbConfig(mCurrentFunctions); - } + return functions; + } + + private String applyUserRestrictions(String functions) { + UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_MTP); + functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_PTP); } + return functions; } private void updateCurrentAccessory() { @@ -523,7 +524,7 @@ public class UsbDeviceManager { // defer accessoryAttached if system is not ready if (mBootCompleted) { getCurrentSettings().accessoryAttached(mCurrentAccessory); - } // else handle in mBootCompletedReceiver + } // else handle in boot completed } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); } @@ -531,7 +532,7 @@ public class UsbDeviceManager { // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(null, false); if (mCurrentAccessory != null) { if (mBootCompleted) { @@ -543,10 +544,11 @@ public class UsbDeviceManager { } } - private void updateUsbState() { + private void updateUsbStateBroadcast() { // send a sticky broadcast containing current USB state Intent intent = new Intent(UsbManager.ACTION_USB_STATE); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING + | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(UsbManager.USB_CONNECTED, mConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); intent.putExtra(UsbManager.USB_DATA_UNLOCKED, mUsbDataUnlocked); @@ -563,8 +565,13 @@ public class UsbDeviceManager { mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } + private void updateUsbFunctions() { + updateAudioSourceFunction(); + updateMidiFunction(); + } + private void updateAudioSourceFunction() { - boolean enabled = containsFunction(mCurrentFunctions, + boolean enabled = UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_AUDIO_SOURCE); if (enabled != mAudioSourceEnabled) { int card = -1; @@ -590,7 +597,8 @@ public class UsbDeviceManager { } private void updateMidiFunction() { - boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI); + boolean enabled = UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MIDI); if (enabled != mMidiEnabled) { if (enabled) { Scanner scanner = null; @@ -624,17 +632,16 @@ public class UsbDeviceManager { } updateUsbNotification(); updateAdbNotification(); - if (containsFunction(mCurrentFunctions, + if (UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected - setEnabledFunctions(getDefaultFunctions()); + setEnabledFunctions(null, false); } if (mBootCompleted) { - updateUsbState(); - updateAudioSourceFunction(); - updateMidiFunction(); + updateUsbStateBroadcast(); + updateUsbFunctions(); } break; case MSG_ENABLE_ADB: @@ -642,26 +649,25 @@ public class UsbDeviceManager { break; case MSG_SET_CURRENT_FUNCTIONS: String functions = (String)msg.obj; - setEnabledFunctions(functions); + setEnabledFunctions(functions, false); + break; + case MSG_UPDATE_USER_RESTRICTIONS: + setEnabledFunctions(mCurrentFunctions, false); break; case MSG_SET_USB_DATA_UNLOCKED: mUsbDataUnlocked = (msg.arg1 == 1); updateUsbNotification(); - updateUsbState(); - restartCurrentFunction(); + updateUsbStateBroadcast(); + setEnabledFunctions(mCurrentFunctions, true); break; case MSG_SYSTEM_READY: - setUsbConfig(mCurrentFunctions); - updatePersistentProperty(); updateUsbNotification(); updateAdbNotification(); - updateUsbState(); - updateAudioSourceFunction(); - updateMidiFunction(); + updateUsbStateBroadcast(); + updateUsbFunctions(); break; case MSG_BOOT_COMPLETED: mBootCompleted = true; - setUsbConfig(mCurrentFunctions); if (mCurrentAccessory != null) { getCurrentSettings().accessoryAttached(mCurrentAccessory); } @@ -670,27 +676,19 @@ public class UsbDeviceManager { } break; case MSG_USER_SWITCHED: { - UserManager userManager = - (UserManager) mContext.getSystemService(Context.USER_SERVICE); - UserHandle userHandle = new UserHandle(msg.arg1); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, - userHandle)) { - Slog.v(TAG, "Switched to user " + msg.arg1 + - " with DISALLOW_USB_FILE_TRANSFER restriction; disabling USB."); - setUsbConfig("none"); + if (mCurrentUser != msg.arg1) { + // Restart the USB stack and re-apply user restrictions for MTP or PTP. + final boolean active = UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MTP) + || UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_PTP); + if (active && mCurrentUser != UserHandle.USER_NULL) { + Slog.v(TAG, "Current user switched to " + mCurrentUser + + "; resetting USB host stack for MTP or PTP"); + setEnabledFunctions(mCurrentFunctions, true); + } mCurrentUser = msg.arg1; - break; - } - - final boolean mtpActive = - containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP) - || containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP); - if (mtpActive && mCurrentUser != UserHandle.USER_NULL) { - Slog.v(TAG, "Current user switched; resetting USB host stack for MTP"); - setUsbConfig("none"); - setUsbConfig(mCurrentFunctions); } - mCurrentUser = msg.arg1; break; } } @@ -707,16 +705,17 @@ public class UsbDeviceManager { if (mConnected) { if (!mUsbDataUnlocked) { id = com.android.internal.R.string.usb_charging_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MTP)) { id = com.android.internal.R.string.usb_mtp_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_PTP)) { id = com.android.internal.R.string.usb_ptp_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MIDI)) { id = com.android.internal.R.string.usb_midi_notification_title; - } else if (containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MASS_STORAGE)) { - id = com.android.internal.R.string.usb_cd_installer_notification_title; - } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { + } else if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_ACCESSORY)) { id = com.android.internal.R.string.usb_accessory_notification_title; } else { id = com.android.internal.R.string.usb_charging_notification_title; @@ -804,17 +803,14 @@ public class UsbDeviceManager { } private String getDefaultFunctions() { - UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE); - if(userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, - new UserHandle(mCurrentUser))) { - return "none"; - } - return mDefaultFunctions; + return SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_ADB); } public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); - pw.println(" Current Functions: " + mCurrentFunctions); + pw.println(" mCurrentFunctions: " + mCurrentFunctions); + pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); pw.println(" mCurrentAccessory: " + mCurrentAccessory); @@ -850,6 +846,10 @@ public class UsbDeviceManager { return nativeOpenAccessory(); } + public boolean isFunctionEnabled(String function) { + return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function); + } + public void setCurrentFunctions(String functions) { if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")"); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 7a3426c5e18e..f82a4dd03158 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -17,6 +17,7 @@ package com.android.server.usb; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -25,10 +26,11 @@ import android.content.pm.PackageManager; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.UserHandle; -import android.os.UserManager; +import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -63,6 +65,8 @@ public class UsbService extends IUsbManager.Stub { public void onBootPhase(int phase) { if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mUsbService.systemReady(); + } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { + mUsbService.bootCompleted(); } } } @@ -108,13 +112,15 @@ public class UsbService extends IUsbManager.Stub { setCurrentUser(UserHandle.USER_OWNER); - final IntentFilter userFilter = new IntentFilter(); - userFilter.addAction(Intent.ACTION_USER_SWITCHED); - userFilter.addAction(Intent.ACTION_USER_STOPPED); - mContext.registerReceiver(mUserReceiver, userFilter, null, null); + final IntentFilter filter = new IntentFilter(); + filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + filter.addAction(Intent.ACTION_USER_SWITCHED); + filter.addAction(Intent.ACTION_USER_STOPPED); + filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); + mContext.registerReceiver(mReceiver, filter, null, null); } - private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { + private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); @@ -125,6 +131,11 @@ public class UsbService extends IUsbManager.Stub { synchronized (mLock) { mSettingsByUser.remove(userId); } + } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED + .equals(action)) { + if (mDeviceManager != null) { + mDeviceManager.updateUserRestrictions(); + } } } }; @@ -135,7 +146,7 @@ public class UsbService extends IUsbManager.Stub { mHostManager.setCurrentSettings(userSettings); } if (mDeviceManager != null) { - mDeviceManager.setCurrentSettings(userSettings); + mDeviceManager.setCurrentUser(userId, userSettings); } } @@ -150,6 +161,12 @@ public class UsbService extends IUsbManager.Stub { } } + public void bootCompleted() { + if (mDeviceManager != null) { + mDeviceManager.bootCompleted(); + } + } + /* Returns a list of all currently attached USB devices (host mdoe) */ @Override public void getDeviceList(Bundle devices) { @@ -252,15 +269,19 @@ public class UsbService extends IUsbManager.Stub { } @Override + public boolean isFunctionEnabled(String function) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + return mDeviceManager != null && mDeviceManager.isFunctionEnabled(function); + } + + @Override public void setCurrentFunction(String function) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - // If attempt to change USB function while file transfer is restricted, ensure that - // the current function is set to "none", and return. - UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { - if (mDeviceManager != null) mDeviceManager.setCurrentFunctions("none"); - return; + if (!isSupportedCurrentFunction(function)) { + Slog.w(TAG, "Caller of setCurrentFunction() requested unsupported USB function: " + + function); + function = UsbManager.USB_FUNCTION_NONE; } if (mDeviceManager != null) { @@ -270,6 +291,22 @@ public class UsbService extends IUsbManager.Stub { } } + private static boolean isSupportedCurrentFunction(String function) { + if (function == null) return true; + + switch (function) { + case UsbManager.USB_FUNCTION_NONE: + case UsbManager.USB_FUNCTION_AUDIO_SOURCE: + case UsbManager.USB_FUNCTION_MIDI: + case UsbManager.USB_FUNCTION_MTP: + case UsbManager.USB_FUNCTION_PTP: + case UsbManager.USB_FUNCTION_RNDIS: + return true; + } + + return false; + } + @Override public void setUsbDataUnlocked(boolean unlocked) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); |