diff options
11 files changed, 665 insertions, 80 deletions
diff --git a/api/current.txt b/api/current.txt index 96fbd31af65b..665726dfc1da 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6328,6 +6328,7 @@ package android.app.admin { method public java.lang.CharSequence getDeviceOwnerLockScreenInfo(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); + method public int getLockTaskFeatures(android.content.ComponentName); method public java.lang.String[] getLockTaskPackages(android.content.ComponentName); method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName); method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName); @@ -6414,6 +6415,7 @@ package android.app.admin { method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); + method public void setLockTaskFeatures(android.content.ComponentName, int); method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException; method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence); method public void setMasterVolumeMuted(android.content.ComponentName, boolean); @@ -6530,6 +6532,13 @@ package android.app.admin { field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10 field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8 field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1 + field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10 + field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4 + field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20 + field public static final int LOCK_TASK_FEATURE_NONE = 0; // 0x0 + field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2 + field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8 + field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000 field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000 diff --git a/api/system-current.txt b/api/system-current.txt index 4f2c2ba7c2b6..f7fb102e5817 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -6550,6 +6550,7 @@ package android.app.admin { method public java.lang.CharSequence getDeviceOwnerOrganizationName(); method public java.util.List<byte[]> getInstalledCaCerts(android.content.ComponentName); method public int getKeyguardDisabledFeatures(android.content.ComponentName); + method public int getLockTaskFeatures(android.content.ComponentName); method public java.lang.String[] getLockTaskPackages(android.content.ComponentName); method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName); method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName); @@ -6649,6 +6650,7 @@ package android.app.admin { method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); + method public void setLockTaskFeatures(android.content.ComponentName, int); method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException; method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence); method public void setMasterVolumeMuted(android.content.ComponentName, boolean); @@ -6776,6 +6778,13 @@ package android.app.admin { field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10 field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8 field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1 + field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10 + field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4 + field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20 + field public static final int LOCK_TASK_FEATURE_NONE = 0; // 0x0 + field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2 + field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8 + field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000 field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000 diff --git a/api/test-current.txt b/api/test-current.txt index 7930c47529b9..3a5165de8dd7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -6393,6 +6393,7 @@ package android.app.admin { method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); method public long getLastSecurityLogRetrievalTime(); + method public int getLockTaskFeatures(android.content.ComponentName); method public java.lang.String[] getLockTaskPackages(android.content.ComponentName); method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName); method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName); @@ -6482,6 +6483,7 @@ package android.app.admin { method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String); method public boolean setKeyguardDisabled(android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(android.content.ComponentName, int); + method public void setLockTaskFeatures(android.content.ComponentName, int); method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException; method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence); method public void setMasterVolumeMuted(android.content.ComponentName, boolean); @@ -6601,6 +6603,13 @@ package android.app.admin { field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10 field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8 field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1 + field public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 16; // 0x10 + field public static final int LOCK_TASK_FEATURE_HOME = 4; // 0x4 + field public static final int LOCK_TASK_FEATURE_KEYGUARD = 32; // 0x20 + field public static final int LOCK_TASK_FEATURE_NONE = 0; // 0x0 + field public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 2; // 0x2 + field public static final int LOCK_TASK_FEATURE_RECENTS = 8; // 0x8 + field public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; // 0x1 field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning"; field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000 field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000 diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 76a56129496a..86b8402338e9 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -654,7 +654,10 @@ interface IActivityManager { /** * Add a bare uid to the background restrictions whitelist. Only the system uid may call this. */ - void backgroundWhitelistUid(int uid); + void backgroundWhitelistUid(int uid); + + // Start of P transactions + void updateLockTaskFeatures(int userId, int flags); // WARNING: when these transactions are updated, check if they are any callers on the native // side. If so, make sure they are using the correct transaction ids and arguments. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index ab8edee724f2..772c6d60856f 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1542,6 +1542,92 @@ public class DevicePolicyManager { public @interface ProvisioningPreCondition {} /** + * Disable all configurable SystemUI features during LockTask mode. This includes, + * <ul> + * <li>system info area in the status bar (connectivity icons, clock, etc.) + * <li>notifications (including alerts, icons, and the notification shade) + * <li>Home button + * <li>Recents button and UI + * <li>global actions menu (i.e. power button menu) + * <li>keyguard + * </ul> + * + * This is the default configuration for LockTask. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_NONE = 0; + + /** + * Enable the system info area in the status bar during LockTask mode. The system info area + * usually occupies the right side of the status bar (although this can differ across OEMs). It + * includes all system information indicators, such as date and time, connectivity, battery, + * vibration mode, etc. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_SYSTEM_INFO = 1; + + /** + * Enable notifications during LockTask mode. This includes notification icons on the status + * bar, heads-up notifications, and the expandable notification shade. Note that the Quick + * Settings panel will still be disabled. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_NOTIFICATIONS = 1 << 1; + + /** + * Enable the Home button during LockTask mode. Note that if a custom launcher is used, it has + * to be registered as the default launcher with + * {@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}, and its + * package needs to be whitelisted for LockTask with + * {@link #setLockTaskPackages(ComponentName, String[])}. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_HOME = 1 << 2; + + /** + * Enable the Recents button and the Recents screen during LockTask mode. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_RECENTS = 1 << 3; + + /** + * Enable the global actions dialog during LockTask mode. This is the dialog that shows up when + * the user long-presses the power button, for example. Note that the user may not be able to + * power off the device if this flag is not set. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 1 << 4; + + /** + * Enable the keyguard during LockTask mode. Note that if the keyguard is already disabled with + * {@link #setKeyguardDisabled(ComponentName, boolean)}, setting this flag will have no effect. + * If this flag is not set, the keyguard will not be shown even if the user has a lock screen + * credential. + * + * @see #setLockTaskFeatures(ComponentName, int) + */ + public static final int LOCK_TASK_FEATURE_KEYGUARD = 1 << 5; + + /** + * Flags supplied to {@link #setLockTaskFeatures(ComponentName, int)}. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, + value = {LOCK_TASK_FEATURE_NONE, LOCK_TASK_FEATURE_SYSTEM_INFO, + LOCK_TASK_FEATURE_NOTIFICATIONS, LOCK_TASK_FEATURE_HOME, + LOCK_TASK_FEATURE_RECENTS, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, + LOCK_TASK_FEATURE_KEYGUARD}) + public @interface LockTaskFeature {} + + /** * Service action: Action for a service that device owner and profile owner can optionally * own. If a device owner or a profile owner has such a service, the system tries to keep * a bound connection to it, in order to keep their process always running. @@ -6484,6 +6570,61 @@ public class DevicePolicyManager { } /** + * Sets which system features to enable for LockTask mode. + * <p> + * Feature flags set through this method will only take effect for the duration when the device + * is in LockTask mode. If this method is not called, none of the features listed here will be + * enabled. + * <p> + * This function can only be called by the device owner or by a profile owner of a user/profile + * that is affiliated with the device owner user. See {@link #setAffiliationIds}. Any features + * set via this method will be cleared if the user becomes unaffiliated. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param flags Bitfield of feature flags: + * {@link #LOCK_TASK_FEATURE_NONE} (default), + * {@link #LOCK_TASK_FEATURE_SYSTEM_INFO}, + * {@link #LOCK_TASK_FEATURE_NOTIFICATIONS}, + * {@link #LOCK_TASK_FEATURE_HOME}, + * {@link #LOCK_TASK_FEATURE_RECENTS}, + * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}, + * {@link #LOCK_TASK_FEATURE_KEYGUARD} + * @throws SecurityException if {@code admin} is not the device owner, or the profile owner of + * an affiliated user or profile. + */ + public void setLockTaskFeatures(@NonNull ComponentName admin, @LockTaskFeature int flags) { + throwIfParentInstance("setLockTaskFeatures"); + if (mService != null) { + try { + mService.setLockTaskFeatures(admin, flags); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Gets which system features are enabled for LockTask mode. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return bitfield of flags. See {@link #setLockTaskFeatures(ComponentName, int)} for a list. + * @throws SecurityException if {@code admin} is not the device owner, or the profile owner of + * an affiliated user or profile. + * @see #setLockTaskFeatures(ComponentName, int) + */ + public @LockTaskFeature int getLockTaskFeatures(@NonNull ComponentName admin) { + throwIfParentInstance("getLockTaskFeatures"); + if (mService != null) { + try { + return mService.getLockTaskFeatures(admin); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + return 0; + } + + /** * Called by device owners to update {@link android.provider.Settings.Global} settings. * Validation that the value of the setting is in the correct form for the setting type should * be performed by the caller. @@ -6901,6 +7042,12 @@ public class DevicePolicyManager { * Called by device owner to disable the status bar. Disabling the status bar blocks * notifications, quick settings and other screen overlays that allow escaping from a single use * device. + * <p> + * <strong>Note:</strong> This method has no effect for LockTask mode. The behavior of the + * status bar in LockTask mode can be configured with + * {@link #setLockTaskFeatures(ComponentName, int)}. Calls to this method when the device is in + * LockTask mode will be registered, but will only take effect when the device leaves LockTask + * mode. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param disabled {@code true} disables the status bar, {@code false} reenables it. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e77c18636594..be0b9205bcdd 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -226,6 +226,9 @@ interface IDevicePolicyManager { String[] getLockTaskPackages(in ComponentName who); boolean isLockTaskPermitted(in String pkg); + void setLockTaskFeatures(in ComponentName who, int flags); + int getLockTaskFeatures(in ComponentName who); + void setGlobalSetting(in ComponentName who, in String setting, in String value); void setSecureSetting(in ComponentName who, in String setting, in String value); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5b51b854aac2..f2e049325497 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10792,6 +10792,20 @@ public class ActivityManagerService extends IActivityManager.Stub } } + @Override + public void updateLockTaskFeatures(int userId, int flags) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != 0 && callingUid != SYSTEM_UID) { + enforceCallingPermission(android.Manifest.permission.UPDATE_LOCK_TASK_PACKAGES, + "updateLockTaskFeatures()"); + } + synchronized (this) { + if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" + + Integer.toHexString(flags)); + mLockTaskController.updateLockTaskFeatures(userId, flags); + } + } + private void startLockTaskModeLocked(@Nullable TaskRecord task, boolean isAppPinning) { if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task); if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) { diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java index 11daf3f9138b..4b2a08439f2d 100644 --- a/services/core/java/com/android/server/am/LockTaskController.java +++ b/services/core/java/com/android/server/am/LockTaskController.java @@ -19,17 +19,11 @@ package com.android.server.am; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; -import static android.app.StatusBarManager.DISABLE_BACK; -import static android.app.StatusBarManager.DISABLE_HOME; -import static android.app.StatusBarManager.DISABLE_MASK; -import static android.app.StatusBarManager.DISABLE_NONE; -import static android.app.StatusBarManager.DISABLE_RECENT; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Context.DEVICE_POLICY_SERVICE; import static android.content.Context.STATUS_BAR_SERVICE; import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_CURRENT; -import static android.provider.Settings.Secure.LOCK_TO_APP_EXIT_LOCKED; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK; @@ -46,7 +40,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.StatusBarManager; +import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; +import android.content.ComponentName; import android.content.Context; import android.os.Binder; import android.os.Debug; @@ -55,6 +52,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -84,13 +82,39 @@ public class LockTaskController { private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK; @VisibleForTesting - static final int STATUS_BAR_MASK_LOCKED = DISABLE_MASK - & (~DISABLE_BACK); + static final int STATUS_BAR_MASK_LOCKED = StatusBarManager.DISABLE_MASK + & (~StatusBarManager.DISABLE_EXPAND) + & (~StatusBarManager.DISABLE_NOTIFICATION_TICKER) + & (~StatusBarManager.DISABLE_SYSTEM_INFO) + & (~StatusBarManager.DISABLE_BACK); @VisibleForTesting - static final int STATUS_BAR_MASK_PINNED = DISABLE_MASK - & (~DISABLE_BACK) - & (~DISABLE_HOME) - & (~DISABLE_RECENT); + static final int STATUS_BAR_MASK_PINNED = StatusBarManager.DISABLE_MASK + & (~StatusBarManager.DISABLE_BACK) + & (~StatusBarManager.DISABLE_HOME) + & (~StatusBarManager.DISABLE_RECENT); + + private static final SparseArray<Pair<Integer, Integer>> STATUS_BAR_FLAG_MAP_LOCKED; + static { + STATUS_BAR_FLAG_MAP_LOCKED = new SparseArray<>(); + + STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO, + new Pair<>(StatusBarManager.DISABLE_CLOCK, StatusBarManager.DISABLE2_SYSTEM_ICONS)); + + STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS, + new Pair<>(StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_ALERTS, + StatusBarManager.DISABLE2_NOTIFICATION_SHADE)); + + STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_HOME, + new Pair<>(StatusBarManager.DISABLE_HOME, StatusBarManager.DISABLE2_NONE)); + + STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS, + new Pair<>(StatusBarManager.DISABLE_RECENT, StatusBarManager.DISABLE2_NONE)); + + STATUS_BAR_FLAG_MAP_LOCKED.append(DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS, + new Pair<>(StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_GLOBAL_ACTIONS)); + } /** Tag used for disabling of keyguard */ private static final String LOCK_TASK_TAG = "Lock-to-App"; @@ -131,6 +155,11 @@ public class LockTaskController { private final SparseArray<String[]> mLockTaskPackages = new SparseArray<>(); /** + * Features that are allowed by DPC to show during LockTask mode. + */ + private final SparseArray<Integer> mLockTaskFeatures = new SparseArray<>(); + + /** * Store the current lock task mode. Possible values: * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED}, * {@link ActivityManager#LOCK_TASK_MODE_PINNED} @@ -319,30 +348,17 @@ public class LockTaskController { private void performStopLockTask(int userId) { // When lock task ends, we enable the status bars. try { - if (getStatusBarService() != null) { - getStatusBarService().disable(DISABLE_NONE, mToken, - mContext.getPackageName()); + setStatusBarState(LOCK_TASK_MODE_NONE, userId); + setKeyguardState(LOCK_TASK_MODE_NONE, userId); + if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { + lockKeyguardIfNeeded(); } - mWindowManager.reenableKeyguard(mToken); if (getDevicePolicyManager() != null) { getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId); } if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { getLockTaskNotify().showPinningExitToast(); } - try { - boolean shouldLockKeyguard = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - LOCK_TO_APP_EXIT_LOCKED, - USER_CURRENT) != 0; - if (mLockTaskModeState == LOCK_TASK_MODE_PINNED && shouldLockKeyguard) { - mWindowManager.lockNow(null); - mWindowManager.dismissKeyguard(null /* callback */); - getLockPatternUtils().requireCredentialEntry(USER_ALL); - } - } catch (Settings.SettingNotFoundException e) { - // No setting, don't lock. - } } catch (RemoteException ex) { throw new RuntimeException(ex); } finally { @@ -448,16 +464,8 @@ public class LockTaskController { getLockTaskNotify().showPinningStartToast(); } mLockTaskModeState = lockTaskModeState; - if (getStatusBarService() != null) { - int flags = 0; - if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) { - flags = STATUS_BAR_MASK_LOCKED; - } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) { - flags = STATUS_BAR_MASK_PINNED; - } - getStatusBarService().disable(flags, mToken, mContext.getPackageName()); - } - mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); + setStatusBarState(lockTaskModeState, userId); + setKeyguardState(lockTaskModeState, userId); if (getDevicePolicyManager() != null) { getDevicePolicyManager().notifyLockTaskModeChanged(true, packageName, userId); } @@ -536,6 +544,135 @@ public class LockTaskController { } /** + * Update the UI features that are enabled for LockTask mode. + * @param userId Which user these feature flags are associated with + * @param flags Bitfield of feature flags + * @see DevicePolicyManager#setLockTaskFeatures(ComponentName, int) + */ + void updateLockTaskFeatures(int userId, int flags) { + int oldFlags = getLockTaskFeaturesForUser(userId); + if (flags == oldFlags) { + return; + } + + mLockTaskFeatures.put(userId, flags); + TaskRecord lockedTask = getLockedTask(); + if (lockedTask != null && userId == lockedTask.userId) { + mHandler.post(() -> { + if (mLockTaskModeState == LOCK_TASK_MODE_LOCKED) { + setStatusBarState(mLockTaskModeState, userId); + setKeyguardState(mLockTaskModeState, userId); + } + }); + } + } + + /** + * Helper method for configuring the status bar disabled state. + * Should only be called on the handler thread to avoid race. + */ + private void setStatusBarState(int lockTaskModeState, int userId) { + IStatusBarService statusBar = getStatusBarService(); + if (statusBar == null) { + Slog.e(TAG, "Can't find StatusBarService"); + return; + } + + // Default state, when lockTaskModeState == LOCK_TASK_MODE_NONE + int flags1 = StatusBarManager.DISABLE_NONE; + int flags2 = StatusBarManager.DISABLE2_NONE; + + if (lockTaskModeState == LOCK_TASK_MODE_PINNED) { + flags1 = STATUS_BAR_MASK_PINNED; + + } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) { + int lockTaskFeatures = getLockTaskFeaturesForUser(userId); + Pair<Integer, Integer> statusBarFlags = getStatusBarDisableFlags(lockTaskFeatures); + flags1 = statusBarFlags.first; + flags2 = statusBarFlags.second; + } + + try { + statusBar.disable(flags1, mToken, mContext.getPackageName()); + statusBar.disable2(flags2, mToken, mContext.getPackageName()); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to set status bar flags", e); + } + } + + /** + * Helper method for configuring the keyguard disabled state. + * Should only be called on the handler thread to avoid race. + */ + private void setKeyguardState(int lockTaskModeState, int userId) { + if (lockTaskModeState == LOCK_TASK_MODE_NONE) { + mWindowManager.reenableKeyguard(mToken); + + } else if (lockTaskModeState == LOCK_TASK_MODE_LOCKED) { + int lockTaskFeatures = getLockTaskFeaturesForUser(userId); + if ((DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD & lockTaskFeatures) == 0) { + mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); + } else { + mWindowManager.reenableKeyguard(mToken); + } + + } else { // lockTaskModeState == LOCK_TASK_MODE_PINNED + mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG); + } + } + + /** + * Helper method for locking the device immediately. This may be necessary when the device + * leaves the pinned mode. + */ + private void lockKeyguardIfNeeded() { + try { + boolean shouldLockKeyguard = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, + USER_CURRENT) != 0; + if (shouldLockKeyguard) { + mWindowManager.lockNow(null); + mWindowManager.dismissKeyguard(null /* callback */); + getLockPatternUtils().requireCredentialEntry(USER_ALL); + } + } catch (Settings.SettingNotFoundException e) { + // No setting, don't lock. + } + } + + /** + * Translates from LockTask feature flags to StatusBarManager disable and disable2 flags. + * @param lockTaskFlags Bitfield of flags as per + * {@link DevicePolicyManager#setLockTaskFeatures(ComponentName, int)} + * @return A {@link Pair} of {@link StatusBarManager#disable(int)} and + * {@link StatusBarManager#disable2(int)} flags + */ + @VisibleForTesting + Pair<Integer, Integer> getStatusBarDisableFlags(int lockTaskFlags) { + // Everything is disabled by default + int flags1 = StatusBarManager.DISABLE_MASK; + int flags2 = StatusBarManager.DISABLE2_MASK; + for (int i = STATUS_BAR_FLAG_MAP_LOCKED.size() - 1; i >= 0; i--) { + Pair<Integer, Integer> statusBarFlags = STATUS_BAR_FLAG_MAP_LOCKED.valueAt(i); + if ((STATUS_BAR_FLAG_MAP_LOCKED.keyAt(i) & lockTaskFlags) != 0) { + flags1 &= ~statusBarFlags.first; + flags2 &= ~statusBarFlags.second; + } + } + // Some flags are not used for LockTask purposes, so we mask them + flags1 &= STATUS_BAR_MASK_LOCKED; + return new Pair<>(flags1, flags2); + } + + /** + * Gets the cached value of LockTask feature flags for a specific user. + */ + private int getLockTaskFeaturesForUser(int userId) { + return mLockTaskFeatures.get(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); + } + + /** * @return the topmost locked task */ private TaskRecord getLockedTask() { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 80f6a4b9f258..2d8a0ee02225 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -18,6 +18,7 @@ package com.android.server.devicepolicy; import static android.Manifest.permission.BIND_DEVICE_ADMIN; import static android.Manifest.permission.MANAGE_CA_CERTIFICATES; +import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.admin.DevicePolicyManager.CODE_ACCOUNTS_NOT_EMPTY; import static android.app.admin.DevicePolicyManager.CODE_ADD_MANAGED_PROFILE_DISALLOWED; import static android.app.admin.DevicePolicyManager.CODE_CANNOT_ADD_MANAGED_PROFILE; @@ -225,6 +226,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component"; + private static final String TAG_LOCK_TASK_FEATURES = "lock-task-features"; + private static final String TAG_STATUS_BAR = "statusbar"; private static final String ATTR_DISABLED = "disabled"; @@ -507,6 +510,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // This is the list of component allowed to start lock task mode. List<String> mLockTaskPackages = new ArrayList<>(); + // Bitfield of feature flags to be enabled during LockTask mode. + int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE; + boolean mStatusBarDisabled = false; ComponentName mRestrictionsProvider; @@ -2628,6 +2634,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endTag(null, TAG_LOCK_TASK_COMPONENTS); } + if (policy.mLockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE) { + out.startTag(null, TAG_LOCK_TASK_FEATURES); + out.attribute(null, ATTR_VALUE, Integer.toString(policy.mLockTaskFeatures)); + out.endTag(null, TAG_LOCK_TASK_FEATURES); + } + if (policy.mStatusBarDisabled) { out.startTag(null, TAG_STATUS_BAR); out.attribute(null, ATTR_DISABLED, Boolean.toString(policy.mStatusBarDisabled)); @@ -2868,6 +2880,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mAcceptedCaCertificates.add(parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) { policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name")); + } else if (TAG_LOCK_TASK_FEATURES.equals(tag)) { + policy.mLockTaskFeatures = Integer.parseInt( + parser.getAttributeValue(null, ATTR_VALUE)); } else if (TAG_STATUS_BAR.equals(tag)) { policy.mStatusBarDisabled = Boolean.parseBoolean( parser.getAttributeValue(null, ATTR_DISABLED)); @@ -2936,6 +2951,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { validatePasswordOwnerLocked(policy); updateMaximumTimeToLockLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); + updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle); if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } @@ -2953,6 +2969,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + private void updateLockTaskFeaturesLocked(int flags, int userId) { + long ident = mInjector.binderClearCallingIdentity(); + try { + mInjector.getIActivityManager() + .updateLockTaskFeatures(userId, flags); + } catch (RemoteException e) { + // Not gonna happen. + } finally { + mInjector.binderRestoreCallingIdentity(ident); + } + } + private void updateDeviceOwnerLocked() { long ident = mInjector.binderClearCallingIdentity(); try { @@ -6939,6 +6967,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED; policy.mAffiliationIds.clear(); policy.mLockTaskPackages.clear(); + policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE; saveSettingsLocked(userId); try { @@ -8921,25 +8950,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { updateLockTaskPackagesLocked(packages, userHandle); } - private void maybeClearLockTaskPackagesLocked() { - final long ident = mInjector.binderClearCallingIdentity(); - try { - final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); - for (int i = 0; i < userInfos.size(); i++) { - int userId = userInfos.get(i).id; - final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages; - if (!lockTaskPackages.isEmpty() && - !isUserAffiliatedWithDeviceLocked(userId)) { - Slog.d(LOG_TAG, - "User id " + userId + " not affiliated. Clearing lock task packages"); - setLockTaskPackagesLocked(userId, Collections.<String>emptyList()); - } - } - } finally { - mInjector.binderRestoreCallingIdentity(ident); - } - } - @Override public String[] getLockTaskPackages(ComponentName who) { Preconditions.checkNotNull(who, "ComponentName is null"); @@ -8966,12 +8976,82 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } @Override + public void setLockTaskFeatures(ComponentName who, int flags) { + Preconditions.checkNotNull(who, "ComponentName is null"); + final int userHandle = mInjector.userHandleGetCallingUserId(); + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (!isUserAffiliatedWithDeviceLocked(userHandle)) { + throw new SecurityException("Admin " + who + + " is neither the device owner or affiliated user's profile owner."); + } + setLockTaskFeaturesLocked(userHandle, flags); + } + } + + private void setLockTaskFeaturesLocked(int userHandle, int flags) { + DevicePolicyData policy = getUserData(userHandle); + policy.mLockTaskFeatures = flags; + saveSettingsLocked(userHandle); + updateLockTaskFeaturesLocked(flags, userHandle); + } + + @Override + public int getLockTaskFeatures(ComponentName who) { + Preconditions.checkNotNull(who, "ComponentName is null"); + final int userHandle = mInjector.userHandleGetCallingUserId(); + synchronized (this) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + if (!isUserAffiliatedWithDeviceLocked(userHandle)) { + throw new SecurityException("Admin " + who + + " is neither the device owner or affiliated user's profile owner."); + } + return getUserData(userHandle).mLockTaskFeatures; + } + } + + private void maybeClearLockTaskPolicyLocked() { + final long ident = mInjector.binderClearCallingIdentity(); + try { + final List<UserInfo> userInfos = mUserManager.getUsers(/*excludeDying=*/ true); + for (int i = userInfos.size() - 1; i >= 0; i--) { + int userId = userInfos.get(i).id; + if (isUserAffiliatedWithDeviceLocked(userId)) { + continue; + } + + final List<String> lockTaskPackages = getUserData(userId).mLockTaskPackages; + if (!lockTaskPackages.isEmpty()) { + Slog.d(LOG_TAG, + "User id " + userId + " not affiliated. Clearing lock task packages"); + setLockTaskPackagesLocked(userId, Collections.<String>emptyList()); + } + final int lockTaskFeatures = getUserData(userId).mLockTaskFeatures; + if (lockTaskFeatures != DevicePolicyManager.LOCK_TASK_FEATURE_NONE){ + Slog.d(LOG_TAG, + "User id " + userId + " not affiliated. Clearing lock task features"); + setLockTaskFeaturesLocked(userId, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); + } + } + } finally { + mInjector.binderRestoreCallingIdentity(ident); + } + } + + @Override public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { if (!isCallerWithSystemUid()) { throw new SecurityException("notifyLockTaskModeChanged can only be called by system"); } synchronized (this) { final DevicePolicyData policy = getUserData(userHandle); + + if (policy.mStatusBarDisabled) { + // Status bar is managed by LockTaskController during LockTask, so we cancel this + // policy when LockTask starts, and reapply it when LockTask ends + setStatusBarDisabledInternal(!isEnabled, userHandle); + } + Bundle adminExtras = new Bundle(); adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg); for (ActiveAdmin admin : policy.mAdminList) { @@ -9182,8 +9262,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); DevicePolicyData policy = getUserData(userId); if (policy.mStatusBarDisabled != disabled) { - if (!setStatusBarDisabledInternal(disabled, userId)) { - return false; + boolean isLockTaskMode = false; + try { + isLockTaskMode = mInjector.getIActivityManager().getLockTaskModeState() + != LOCK_TASK_MODE_NONE; + } catch (RemoteException e) { + Slog.e(LOG_TAG, "Failed to get LockTask mode"); + } + if (!isLockTaskMode) { + if (!setStatusBarDisabledInternal(disabled, userId)) { + return false; + } } policy.mStatusBarDisabled = disabled; saveSettingsLocked(userId); @@ -10283,7 +10372,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // but as a result of that other users might become affiliated or un-affiliated. maybePauseDeviceWideLoggingLocked(); maybeResumeDeviceWideLoggingLocked(); - maybeClearLockTaskPackagesLocked(); + maybeClearLockTaskPolicyLocked(); } } diff --git a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java index 4c1d3e9f0003..44a79ab3a6f3 100644 --- a/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/LockTaskControllerTest.java @@ -19,6 +19,16 @@ package com.android.server.am; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; +import static android.app.StatusBarManager.DISABLE2_MASK; +import static android.app.StatusBarManager.DISABLE2_NONE; +import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE; +import static android.app.StatusBarManager.DISABLE_HOME; +import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS; +import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; import static android.os.Process.SYSTEM_UID; import static com.android.server.am.LockTaskController.STATUS_BAR_MASK_LOCKED; @@ -29,6 +39,7 @@ import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import android.app.StatusBarManager; +import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; import android.content.ComponentName; import android.content.Context; @@ -42,6 +53,7 @@ import android.platform.test.annotations.Presubmit; import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; +import android.util.Pair; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; @@ -139,7 +151,7 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.checkLockedTask(tr)); // THEN lock task mode should be started - verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); } @Test @@ -159,7 +171,7 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.checkLockedTask(tr2)); // THEN lock task mode should be started - verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); } @Test @@ -188,7 +200,7 @@ public class LockTaskControllerTest { assertTrue(mLockTaskController.checkLockedTask(tr)); // THEN lock task mode should be started - verifyLockTaskStarted(STATUS_BAR_MASK_PINNED); + verifyLockTaskStarted(STATUS_BAR_MASK_PINNED, DISABLE2_NONE); // THEN screen pinning toast should be shown verify(mLockTaskNotify).showPinningStartToast(); } @@ -291,6 +303,9 @@ public class LockTaskControllerTest { Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCK_TO_APP_EXIT_LOCKED, 1); + // reset invocation counter + reset(mStatusBarService); + // WHEN calling stopLockTask mLockTaskController.stopLockTaskMode(true, SYSTEM_UID); @@ -354,7 +369,7 @@ public class LockTaskControllerTest { assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); assertTrue(mLockTaskController.checkLockedTask(tr1)); assertTrue(mLockTaskController.checkLockedTask(tr2)); - verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); // WHEN removing one package from whitelist whitelist = new String[] {TEST_PACKAGE_NAME}; @@ -366,7 +381,7 @@ public class LockTaskControllerTest { // THEN the other task should remain locked assertEquals(LOCK_TASK_MODE_LOCKED, mLockTaskController.getLockTaskModeState()); assertTrue(mLockTaskController.checkLockedTask(tr1)); - verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED); + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); // WHEN removing the last package from whitelist whitelist = new String[] {}; @@ -379,6 +394,131 @@ public class LockTaskControllerTest { verifyLockTaskStopped(times(1)); } + @Test + public void testUpdateLockTaskFeatures() throws Exception { + // GIVEN a locked task + TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr, false, TEST_UID); + + // THEN lock task mode should be started with default status bar masks + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); + + // reset invocation counter + reset(mStatusBarService); + + // WHEN home button is enabled for lock task mode + mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_HOME); + + // THEN status bar should be updated to reflect this change + int expectedFlags = STATUS_BAR_MASK_LOCKED + & ~DISABLE_HOME; + int expectedFlags2 = DISABLE2_MASK; + verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), + eq(mContext.getPackageName())); + verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), + eq(mContext.getPackageName())); + + // reset invocation counter + reset(mStatusBarService); + + // WHEN notifications are enabled for lock task mode + mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NOTIFICATIONS); + + // THEN status bar should be updated to reflect this change + expectedFlags = STATUS_BAR_MASK_LOCKED + & ~DISABLE_NOTIFICATION_ICONS + & ~DISABLE_NOTIFICATION_ALERTS; + expectedFlags2 = DISABLE2_MASK + & ~DISABLE2_NOTIFICATION_SHADE; + verify(mStatusBarService).disable(eq(expectedFlags), any(IBinder.class), + eq(mContext.getPackageName())); + verify(mStatusBarService).disable2(eq(expectedFlags2), any(IBinder.class), + eq(mContext.getPackageName())); + } + + @Test + public void testUpdateLockTaskFeatures_differentUser() throws Exception { + // GIVEN a locked task + TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr, false, TEST_UID); + + // THEN lock task mode should be started with default status bar masks + verifyLockTaskStarted(STATUS_BAR_MASK_LOCKED, DISABLE2_MASK); + + // reset invocation counter + reset(mStatusBarService); + + // WHEN home button is enabled for lock task mode for another user + mLockTaskController.updateLockTaskFeatures(TEST_USER_ID + 1, LOCK_TASK_FEATURE_HOME); + + // THEN status bar shouldn't change + verify(mStatusBarService, never()).disable(anyInt(), any(IBinder.class), + eq(mContext.getPackageName())); + verify(mStatusBarService, never()).disable2(anyInt(), any(IBinder.class), + eq(mContext.getPackageName())); + } + + @Test + public void testUpdateLockTaskFeatures_keyguard() throws Exception { + // GIVEN a locked task + TaskRecord tr = getTaskRecord(TaskRecord.LOCK_TASK_AUTH_WHITELISTED); + mLockTaskController.startLockTaskMode(tr, false, TEST_UID); + + // THEN keyguard should be disabled + verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString()); + + // WHEN keyguard is enabled for lock task mode + mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_KEYGUARD); + + // THEN keyguard should be enabled + verify(mWindowManager).reenableKeyguard(any(IBinder.class)); + + // WHEN keyguard is disabled again for lock task mode + mLockTaskController.updateLockTaskFeatures(TEST_USER_ID, LOCK_TASK_FEATURE_NONE); + + // THEN keyguard should be disabled + verify(mWindowManager, times(2)).disableKeyguard(any(IBinder.class), anyString()); + } + + @Test + public void testGetStatusBarDisableFlags() { + // Note that we don't enumerate all StatusBarManager flags, but only choose a subset to test + + // WHEN nothing is enabled + Pair<Integer, Integer> flags = mLockTaskController.getStatusBarDisableFlags( + LOCK_TASK_FEATURE_NONE); + // THEN unsupported feature flags should still be untouched + assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0); + // THEN everything else should be disabled + assertTrue((StatusBarManager.DISABLE_CLOCK & flags.first) != 0); + assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0); + + // WHEN only home button is enabled + flags = mLockTaskController.getStatusBarDisableFlags( + LOCK_TASK_FEATURE_HOME); + // THEN unsupported feature flags should still be untouched + assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0); + // THEN home button should indeed be enabled + assertTrue((StatusBarManager.DISABLE_HOME & flags.first) == 0); + // THEN other feature flags should remain disabled + assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) != 0); + + // WHEN only global actions menu and notifications are enabled + flags = mLockTaskController.getStatusBarDisableFlags( + DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS + | DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS); + // THEN unsupported feature flags should still be untouched + assertTrue((~STATUS_BAR_MASK_LOCKED & flags.first) == 0); + // THEN notifications should be enabled + assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ICONS & flags.first) == 0); + assertTrue((StatusBarManager.DISABLE_NOTIFICATION_ALERTS & flags.first) == 0); + assertTrue((StatusBarManager.DISABLE2_NOTIFICATION_SHADE & flags.second) == 0); + // THEN global actions should be enabled + assertTrue((StatusBarManager.DISABLE2_GLOBAL_ACTIONS & flags.second) == 0); + // THEN quick settings should still be disabled + assertTrue((StatusBarManager.DISABLE2_QUICK_SETTINGS & flags.second) != 0); + } + private TaskRecord getTaskRecord(int lockTaskAuth) { return getTaskRecord(TEST_PACKAGE_NAME, lockTaskAuth); } @@ -411,12 +551,14 @@ public class LockTaskControllerTest { return tr; } - private void verifyLockTaskStarted(int statusBarMask) throws Exception { + private void verifyLockTaskStarted(int statusBarMask, int statusBarMask2) throws Exception { // THEN the keyguard should have been disabled verify(mWindowManager).disableKeyguard(any(IBinder.class), anyString()); // THEN the status bar should have been disabled verify(mStatusBarService).disable(eq(statusBarMask), any(IBinder.class), eq(mContext.getPackageName())); + verify(mStatusBarService).disable2(eq(statusBarMask2), any(IBinder.class), + eq(mContext.getPackageName())); // THEN the DO/PO should be informed about the operation verify(mDevicePolicyManager).notifyLockTaskModeChanged(true, TEST_PACKAGE_NAME, TEST_USER_ID); @@ -428,6 +570,8 @@ public class LockTaskControllerTest { // THEN the status bar should have been disabled verify(mStatusBarService, mode).disable(eq(StatusBarManager.DISABLE_NONE), any(IBinder.class), eq(mContext.getPackageName())); + verify(mStatusBarService, mode).disable2(eq(StatusBarManager.DISABLE2_NONE), + any(IBinder.class), eq(mContext.getPackageName())); // THEN the DO/PO should be informed about the operation verify(mDevicePolicyManager, mode).notifyLockTaskModeChanged(false, null, TEST_USER_ID); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index e1e9cf5bc013..9d23fe9691b1 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3340,13 +3340,15 @@ public class DevicePolicyManagerTest extends DpmTestBase { MoreAsserts.assertEmpty(targetUsers); } - public void testLockTaskPackagesAllowedForAffiliatedUsers() throws Exception { + public void testLockTaskPolicyAllowedForAffiliatedUsers() throws Exception { // Setup a device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; setupDeviceOwner(); - // Lock task packages are updated when loading user data. - verify(getServices().iactivityManager) - .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(new String[0])); + // Lock task policy is updated when loading user data. + verify(getServices().iactivityManager).updateLockTaskPackages( + UserHandle.USER_SYSTEM, new String[0]); + verify(getServices().iactivityManager).updateLockTaskFeatures( + UserHandle.USER_SYSTEM, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); // Set up a managed profile managed by different package (package name shouldn't matter) final int MANAGED_PROFILE_USER_ID = 15; @@ -3354,8 +3356,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { final ComponentName adminDifferentPackage = new ComponentName("another.package", "whatever.class"); addManagedProfile(adminDifferentPackage, MANAGED_PROFILE_ADMIN_UID, admin2); - verify(getServices().iactivityManager) - .updateLockTaskPackages(eq(MANAGED_PROFILE_USER_ID), eq(new String[0])); + verify(getServices().iactivityManager).updateLockTaskPackages( + MANAGED_PROFILE_USER_ID, new String[0]); + verify(getServices().iactivityManager).updateLockTaskFeatures( + MANAGED_PROFILE_USER_ID, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); // The DO can still set lock task packages mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -3364,8 +3368,14 @@ public class DevicePolicyManagerTest extends DpmTestBase { MoreAsserts.assertEquals(doPackages, dpm.getLockTaskPackages(admin1)); assertTrue(dpm.isLockTaskPermitted("doPackage1")); assertFalse(dpm.isLockTaskPermitted("anotherPackage")); - verify(getServices().iactivityManager) - .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(doPackages)); + verify(getServices().iactivityManager).updateLockTaskPackages( + UserHandle.USER_SYSTEM, doPackages); + // And the DO can still set lock task features + final int doFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS + | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS; + dpm.setLockTaskFeatures(admin1, doFlags); + verify(getServices().iactivityManager).updateLockTaskFeatures( + UserHandle.USER_SYSTEM, doFlags); // Managed profile is unaffiliated - shouldn't be able to setLockTaskPackages. mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID; @@ -3375,6 +3385,11 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertExpectException(SecurityException.class, /* messageRegex =*/ null, () -> dpm.getLockTaskPackages(adminDifferentPackage)); assertFalse(dpm.isLockTaskPermitted("doPackage1")); + // And it shouldn't be able to setLockTaskFeatures. + final int poFlags = DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS + | DevicePolicyManager.LOCK_TASK_FEATURE_RECENTS; + assertExpectException(SecurityException.class, /* messageRegex =*/ null, + () -> dpm.setLockTaskFeatures(adminDifferentPackage, poFlags)); // Setting same affiliation ids final Set<String> userAffiliationIds = Collections.singleton("some-affiliation-id"); @@ -3389,15 +3404,21 @@ public class DevicePolicyManagerTest extends DpmTestBase { MoreAsserts.assertEquals(poPackages, dpm.getLockTaskPackages(adminDifferentPackage)); assertTrue(dpm.isLockTaskPermitted("poPackage1")); assertFalse(dpm.isLockTaskPermitted("doPackage2")); - verify(getServices().iactivityManager) - .updateLockTaskPackages(eq(MANAGED_PROFILE_USER_ID), eq(poPackages)); + verify(getServices().iactivityManager).updateLockTaskPackages( + MANAGED_PROFILE_USER_ID, poPackages); + // And it can set lock task features. + dpm.setLockTaskFeatures(adminDifferentPackage, poFlags); + verify(getServices().iactivityManager).updateLockTaskFeatures( + MANAGED_PROFILE_USER_ID, poFlags); // Unaffiliate the profile, lock task mode no longer available on the profile. dpm.setAffiliationIds(adminDifferentPackage, Collections.emptySet()); assertFalse(dpm.isLockTaskPermitted("poPackage1")); // Lock task packages cleared when loading user data and when the user becomes unaffiliated. - verify(getServices().iactivityManager, times(2)) - .updateLockTaskPackages(eq(MANAGED_PROFILE_USER_ID), eq(new String[0])); + verify(getServices().iactivityManager, times(2)).updateLockTaskPackages( + MANAGED_PROFILE_USER_ID, new String[0]); + verify(getServices().iactivityManager, times(2)).updateLockTaskFeatures( + MANAGED_PROFILE_USER_ID, DevicePolicyManager.LOCK_TASK_FEATURE_NONE); mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; assertTrue(dpm.isLockTaskPermitted("doPackage1")); |