diff options
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 31a6a96..0fe112c 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 c83f466..f58b9d6 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 7c5ec4d..a6c4fcc 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2884,8 +2884,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 5de010d..fcdaba2 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 ed4fe24..5d05f32 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; @@ -5446,10 +5445,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 e7716c2..81b4857 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 987a79f..d90ade8 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); |