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/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"));