diff options
author | Jerry Zhang <zhangjerry@google.com> | 2017-11-30 19:10:55 -0800 |
---|---|---|
committer | Jerry Zhang <zhangjerry@google.com> | 2018-01-09 15:36:35 -0800 |
commit | 30b9adfad1c5055044f0b585782b10f764e0ddf9 (patch) | |
tree | 2501d4b4057bb311aede24739f1f5fdf95107cfa | |
parent | 03033385afd531807c5f96a577f76f12a9a08431 (diff) |
Add setScreenUnlockedFunctions method to UsbManager
The screen unlocked functions save effort on setting
the usb config during each connection. These
functions persist between connections and between
boots. When the screen is unlocked and these
functions are set, the current functions will
be automatically set to the screen unlocked functions.
Also added svc command for this so it can be
used and tested while the UI is worked on.
Bug: 62876645
Test: svc usb setScreenUnlockedFunctions mtp
Test: Test functions with locking, unlocking, and
disconnecting, with no lockscreen, swipe, and pattern
Change-Id: Ia05e095917166d25398c4d310b02971e3a1bb12a
5 files changed, 219 insertions, 25 deletions
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index adbe9d015626..34f6d7de0cc9 100644 --- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java +++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java @@ -18,6 +18,7 @@ package com.android.commands.svc; import android.content.Context; import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -38,6 +39,9 @@ public class UsbCommand extends Svc.Command { + "\n" + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n" + " Set the current usb function and optionally the data lock state.\n\n" + + " svc usb setScreenUnlockedFunctions [function]\n" + + " Sets the functions which, if the device was charging," + + " become current on screen unlock.\n" + " svc usb getFunction\n" + " Gets the list of currently enabled functions\n"; } @@ -62,6 +66,16 @@ public class UsbCommand extends Svc.Command { } else if ("getFunction".equals(args[1])) { System.err.println(SystemProperties.get("sys.usb.config")); return; + } else if ("setScreenUnlockedFunctions".equals(args[1])) { + IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( + Context.USB_SERVICE)); + try { + usbMgr.setScreenUnlockedFunctions((args.length >= 3 ? args[2] : + UsbManager.USB_FUNCTION_NONE)); + } catch (RemoteException e) { + System.err.println("Error communicating with UsbManager: " + e); + } + return; } } System.err.println(longHelp()); diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 151e62de7b70..398dda174ba1 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -96,6 +96,11 @@ interface IUsbManager */ void setCurrentFunction(String function, boolean usbDataUnlocked); + /* Sets the screen unlocked USB function(s), which will be set automatically + * when the screen is unlocked. + */ + void setScreenUnlockedFunctions(String function); + /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. */ diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index bdb90bcca4f8..7617c2bd196f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -601,6 +601,32 @@ public class UsbManager { } /** + * Sets the screen unlocked functions, which are persisted and set as the current functions + * whenever the screen is unlocked. + * <p> + * The allowed values are: {@link #USB_FUNCTION_NONE}, + * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP}, + * or {@link #USB_FUNCTION_RNDIS}. + * {@link #USB_FUNCTION_NONE} has the effect of switching off this feature, so functions + * no longer change on screen unlock. + * </p><p> + * Note: When the screen is on, this method will apply given functions as current functions, + * which is asynchronous and may fail silently without applying the requested changes. + * </p> + * + * @param function function to set as default + * + * {@hide} + */ + public void setScreenUnlockedFunctions(String function) { + try { + mService.setScreenUnlockedFunctions(function); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns a list of physical USB ports on the device. * <p> * This list is guaranteed to contain all dual-role USB Type C ports but it might diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 1b057f9b9681..4a7072d03067 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -16,6 +16,9 @@ package com.android.server.usb; +import android.app.ActivityManager; +import android.app.ActivityManagerInternal; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -26,6 +29,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; @@ -38,6 +42,7 @@ import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; import android.os.BatteryManager; +import android.os.Environment; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; @@ -60,6 +65,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.SomeArgs; import com.android.internal.util.IndentingPrintWriter; import com.android.server.FgThread; +import com.android.server.LocalServices; import java.io.File; import java.io.FileNotFoundException; @@ -75,7 +81,7 @@ import java.util.Set; /** * UsbDeviceManager manages USB state in device mode. */ -public class UsbDeviceManager { +public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver { private static final String TAG = "UsbDeviceManager"; private static final boolean DEBUG = false; @@ -97,6 +103,12 @@ public class UsbDeviceManager { private static final String USB_STATE_PROPERTY = "sys.usb.state"; /** + * The SharedPreference setting per user that stores the screen unlocked functions between + * sessions. + */ + private static final String UNLOCKED_CONFIG_PREF = "usb-screen-unlocked-config-%d"; + + /** * ro.bootmode value when phone boots into usual Android. */ private static final String NORMAL_BOOT = "normal"; @@ -128,6 +140,8 @@ public class UsbDeviceManager { private static final int MSG_UPDATE_CHARGING_STATE = 9; private static final int MSG_UPDATE_HOST_STATE = 10; private static final int MSG_LOCALE_CHANGED = 11; + private static final int MSG_SET_SCREEN_UNLOCKED_FUNCTIONS = 12; + private static final int MSG_UPDATE_SCREEN_LOCK = 13; private static final int AUDIO_MODE_SOURCE = 1; @@ -169,6 +183,7 @@ public class UsbDeviceManager { private Intent mBroadcastedIntent; private boolean mPendingBootBroadcast; private static Set<Integer> sBlackListedInterfaces; + private SharedPreferences mSettings; static { sBlackListedInterfaces = new HashSet<>(); @@ -217,6 +232,31 @@ public class UsbDeviceManager { } }; + @Override + public void onKeyguardStateChanged(boolean isShowing) { + int userHandle = ActivityManager.getCurrentUser(); + boolean secure = mContext.getSystemService(KeyguardManager.class) + .isDeviceSecure(userHandle); + boolean unlocking = mContext.getSystemService(UserManager.class) + .isUserUnlockingOrUnlocked(userHandle); + if (DEBUG) { + Slog.v(TAG, "onKeyguardStateChanged: isShowing:" + isShowing + " secure:" + secure + + " unlocking:" + unlocking + " user:" + userHandle); + } + // We are unlocked when the keyguard is down or non-secure, and user storage is unlocked. + mHandler.sendMessage(MSG_UPDATE_SCREEN_LOCK, (isShowing && secure) || !unlocking); + } + + @Override + public void onAwakeStateChanged(boolean isAwake) { + // ignore + } + + /** Called when a user is unlocked. */ + public void onUnlockUser(int userHandle) { + onKeyguardStateChanged(false); + } + public UsbDeviceManager(Context context, UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) { mContext = context; @@ -303,6 +343,8 @@ public class UsbDeviceManager { public void systemReady() { if (DEBUG) Slog.d(TAG, "systemReady"); + LocalServices.getService(ActivityManagerInternal.class).registerScreenObserver(this); + mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -407,6 +449,14 @@ public class UsbDeviceManager { return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); } + private SharedPreferences getPinnedSharedPrefs(Context context) { + final File prefsFile = new File(new File( + Environment.getDataUserCePackageDirectory(StorageManager.UUID_PRIVATE_INTERNAL, + context.getUserId(), context.getPackageName()), "shared_prefs"), + UsbDeviceManager.class.getSimpleName() + ".xml"); + return context.getSharedPreferences(prefsFile, Context.MODE_PRIVATE); + } + private final class UsbHandler extends Handler { // current USB state @@ -423,11 +473,13 @@ public class UsbDeviceManager { private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; private boolean mAdbNotificationShown; - private int mCurrentUser = UserHandle.USER_NULL; + private int mCurrentUser; private boolean mUsbCharging; private String mCurrentOemFunctions; private boolean mHideUsbNotification; private boolean mSupportsAllCombinations; + private String mScreenUnlockedFunctions = UsbManager.USB_FUNCTION_NONE; + private boolean mScreenLocked; public UsbHandler(Looper looper) { super(looper); @@ -449,6 +501,9 @@ public class UsbDeviceManager { SystemProperties.get(USB_STATE_PROPERTY)); } + mCurrentUser = ActivityManager.getCurrentUser(); + mScreenLocked = true; + /* * Use the normal bootmode persistent prop to maintain state of adb across * all boot modes. @@ -653,7 +708,7 @@ public class UsbDeviceManager { private boolean trySetEnabledFunctions(String functions, boolean forceRestart) { if (functions == null || applyAdbFunction(functions) .equals(UsbManager.USB_FUNCTION_NONE)) { - functions = getDefaultFunctions(); + functions = getChargingFunctions(); } functions = applyAdbFunction(functions); @@ -876,6 +931,14 @@ public class UsbDeviceManager { mMidiEnabled && mConfigured, mMidiCard, mMidiDevice); } + private void setScreenUnlockedFunctions() { + setEnabledFunctions(mScreenUnlockedFunctions, false, + UsbManager.containsFunction(mScreenUnlockedFunctions, + UsbManager.USB_FUNCTION_MTP) + || UsbManager.containsFunction(mScreenUnlockedFunctions, + UsbManager.USB_FUNCTION_PTP)); + } + @Override public void handleMessage(Message msg) { switch (msg.what) { @@ -895,7 +958,13 @@ public class UsbDeviceManager { if (mBootCompleted) { if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)) { // restore defaults when USB is disconnected - setEnabledFunctions(null, !mAdbEnabled, false); + if (!mScreenLocked + && !UsbManager.USB_FUNCTION_NONE.equals( + mScreenUnlockedFunctions)) { + setScreenUnlockedFunctions(); + } else { + setEnabledFunctions(null, !mAdbEnabled, false); + } } updateUsbFunctions(); } else { @@ -978,6 +1047,47 @@ public class UsbDeviceManager { String functions = (String) msg.obj; setEnabledFunctions(functions, false, msg.arg1 == 1); break; + case MSG_SET_SCREEN_UNLOCKED_FUNCTIONS: + mScreenUnlockedFunctions = (String) msg.obj; + SharedPreferences.Editor editor = mSettings.edit(); + editor.putString(String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, + mCurrentUser), mScreenUnlockedFunctions); + editor.commit(); + if (!mScreenLocked && !UsbManager.USB_FUNCTION_NONE.equals( + mScreenUnlockedFunctions)) { + // If the screen is unlocked, also set current functions. + setScreenUnlockedFunctions(); + } + break; + case MSG_UPDATE_SCREEN_LOCK: + if (msg.arg1 == 1 == mScreenLocked) { + break; + } + mScreenLocked = msg.arg1 == 1; + if (mSettings == null && !mScreenLocked) { + // Shared preferences aren't accessible until the user has been unlocked. + mSettings = getPinnedSharedPrefs(mContext); + mScreenUnlockedFunctions = mSettings.getString( + String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser), + UsbManager.USB_FUNCTION_NONE); + } + if (!mBootCompleted) { + break; + } + if (mScreenLocked) { + if (!mConnected) { + setEnabledFunctions(null, false, false); + } + } else { + if (!UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions) + && (UsbManager.USB_FUNCTION_ADB.equals(mCurrentFunctions) + || (UsbManager.USB_FUNCTION_MTP.equals(mCurrentFunctions) + && !mUsbDataUnlocked))) { + // Set the screen unlocked functions if current function is charging. + setScreenUnlockedFunctions(); + } + } + break; case MSG_UPDATE_USER_RESTRICTIONS: // Restart the USB stack if USB transfer is enabled but no longer allowed. final boolean forceRestart = mUsbDataUnlocked @@ -1001,7 +1111,13 @@ public class UsbDeviceManager { updateUsbStateBroadcastIfNeeded(false); mPendingBootBroadcast = false; } - setEnabledFunctions(null, false, false); + + if (!mScreenLocked + && !UsbManager.USB_FUNCTION_NONE.equals(mScreenUnlockedFunctions)) { + setScreenUnlockedFunctions(); + } else { + setEnabledFunctions(null, false, false); + } if (mCurrentAccessory != null) { getCurrentSettings().accessoryAttached(mCurrentAccessory); } @@ -1011,16 +1127,15 @@ public class UsbDeviceManager { break; case MSG_USER_SWITCHED: { if (mCurrentUser != msg.arg1) { - // Restart the USB stack and re-apply user restrictions for MTP or PTP. - if (mUsbDataUnlocked - && isUsbDataTransferActive() - && mCurrentUser != UserHandle.USER_NULL) { - Slog.v(TAG, "Current user switched to " + msg.arg1 - + "; resetting USB host stack for MTP or PTP"); - // avoid leaking sensitive data from previous user - setEnabledFunctions(null, true, false); + if (DEBUG) { + Slog.v(TAG, "Current user switched to " + msg.arg1); } mCurrentUser = msg.arg1; + mScreenLocked = true; + mScreenUnlockedFunctions = mSettings.getString( + String.format(Locale.ENGLISH, UNLOCKED_CONFIG_PREF, mCurrentUser), + UsbManager.USB_FUNCTION_NONE); + setEnabledFunctions(null, false, false); } break; } @@ -1072,20 +1187,12 @@ public class UsbDeviceManager { titleRes = com.android.internal.R.string.usb_unsupported_audio_accessory_title; id = SystemMessage.NOTE_USB_AUDIO_ACCESSORY_NOT_SUPPORTED; } else if (mConnected) { - if (!mUsbDataUnlocked) { - if (mSourcePower) { - titleRes = com.android.internal.R.string.usb_supplying_notification_title; - id = SystemMessage.NOTE_USB_SUPPLYING; - } else { - titleRes = com.android.internal.R.string.usb_charging_notification_title; - id = SystemMessage.NOTE_USB_CHARGING; - } - } else if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_MTP)) { + if (UsbManager.containsFunction(mCurrentFunctions, + UsbManager.USB_FUNCTION_MTP) && mUsbDataUnlocked) { titleRes = com.android.internal.R.string.usb_mtp_notification_title; id = SystemMessage.NOTE_USB_MTP; } else if (UsbManager.containsFunction(mCurrentFunctions, - UsbManager.USB_FUNCTION_PTP)) { + UsbManager.USB_FUNCTION_PTP) && mUsbDataUnlocked) { titleRes = com.android.internal.R.string.usb_ptp_notification_title; id = SystemMessage.NOTE_USB_PTP; } else if (UsbManager.containsFunction(mCurrentFunctions, @@ -1236,7 +1343,7 @@ public class UsbDeviceManager { } } - private String getDefaultFunctions() { + private String getChargingFunctions() { String func = SystemProperties.get(getPersistProp(true), UsbManager.USB_FUNCTION_NONE); // if ADB is enabled, reset functions to ADB @@ -1253,6 +1360,8 @@ public class UsbDeviceManager { pw.println(" mCurrentFunctions: " + mCurrentFunctions); pw.println(" mCurrentOemFunctions: " + mCurrentOemFunctions); pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied); + pw.println(" mScreenUnlockedFunctions: " + mScreenUnlockedFunctions); + pw.println(" mScreenLocked: " + mScreenLocked); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked); @@ -1309,6 +1418,17 @@ public class UsbDeviceManager { mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked); } + /** + * Sets the functions which are set when the screen is unlocked. + * @param functions Functions to set. + */ + public void setScreenUnlockedFunctions(String functions) { + if (DEBUG) { + Slog.d(TAG, "setScreenUnlockedFunctions(" + functions + ")"); + } + mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions); + } + private void readOemUsbOverrideConfig() { String[] configList = mContext.getResources().getStringArray( com.android.internal.R.array.config_oemUsbModeOverride); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index 8554cf7b186f..1a20819b9a80 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -87,6 +87,11 @@ public class UsbService extends IUsbManager.Stub { public void onStopUser(int userHandle) { mUsbService.onStopUser(UserHandle.of(userHandle)); } + + @Override + public void onUnlockUser(int userHandle) { + mUsbService.onUnlockUser(userHandle); + } } private static final String TAG = "UsbService"; @@ -205,6 +210,13 @@ public class UsbService extends IUsbManager.Stub { } } + /** Called when a user is unlocked. */ + public void onUnlockUser(int user) { + if (mDeviceManager != null) { + mDeviceManager.onUnlockUser(user); + } + } + /* Returns a list of all currently attached USB devices (host mdoe) */ @Override public void getDeviceList(Bundle devices) { @@ -392,6 +404,23 @@ public class UsbService extends IUsbManager.Stub { } } + @Override + public void setScreenUnlockedFunctions(String function) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + + if (!isSupportedCurrentFunction(function)) { + Slog.w(TAG, "Caller of setScreenUnlockedFunctions() requested unsupported USB function:" + + function); + function = UsbManager.USB_FUNCTION_NONE; + } + + if (mDeviceManager != null) { + mDeviceManager.setScreenUnlockedFunctions(function); + } else { + throw new IllegalStateException("USB device mode not supported"); + } + } + private static boolean isSupportedCurrentFunction(String function) { if (function == null) return true; |