DPM: introduce setLockTaskFeatures().
Add new DPM APIs to control which SystemUI features are enabled during
LockTask mode:
* setLockTaskFeatures()
* getLockTaskFeatures()
* int flags representing various configurable SystemUI features
Bug: 65813398
Test: bit FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest
Test: bit FrameworksServicesTests:com.android.server.am.LockTaskControllerTest
Test: cts-tradefed run cts-dev --module DevicePolicyManager -t com.android.cts.devicepolicy.DeviceOwnerTest#testLockTask_deviceOwnerUser
Change-Id: I0ee3cf6dbe2234ec29d1384195dadc0f290aa73b
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e1e53b3..bff7c55 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10743,6 +10743,20 @@
}
}
+ @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 1c094c1..c519ddc75 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 @@
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.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.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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 c59f44e..21e2360 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 @@
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;
@@ -224,6 +225,8 @@
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";
@@ -506,6 +509,9 @@
// 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;
@@ -2623,6 +2629,12 @@
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));
@@ -2863,6 +2875,9 @@
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));
@@ -2931,6 +2946,7 @@
validatePasswordOwnerLocked(policy);
updateMaximumTimeToLockLocked(userHandle);
updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle);
+ updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle);
if (policy.mStatusBarDisabled) {
setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle);
}
@@ -2948,6 +2964,18 @@
}
}
+ 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 {
@@ -6934,6 +6962,7 @@
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
policy.mAffiliationIds.clear();
policy.mLockTaskPackages.clear();
+ policy.mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
saveSettingsLocked(userId);
try {
@@ -8916,25 +8945,6 @@
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");
@@ -8961,12 +8971,82 @@
}
@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) {
@@ -9152,8 +9232,17 @@
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);
@@ -10253,7 +10342,7 @@
// 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 4c1d3e9..44a79ab 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 @@
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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 a8bf8f1..b8b0caf 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -3299,13 +3299,15 @@
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;
@@ -3313,8 +3315,10 @@
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;
@@ -3323,8 +3327,14 @@
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;
@@ -3334,6 +3344,11 @@
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");
@@ -3348,15 +3363,21 @@
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"));