Introduce device owner API to disable the status bar

Let the device owner disable the status bar to achieve multi-app single purpose
mode. When the status bar is disabled, quick settings, notifications and the
assist gesture are blocked.

Bug: 19533026
Change-Id: I72830798135136e5edc53e5e2221aebb9a7c7d57
diff --git a/api/current.txt b/api/current.txt
index ea4aaf9..d2d7d95 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5737,6 +5737,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setStatusBarEnabledState(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 1a28546..fcb2ce6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5841,6 +5841,7 @@
     method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
     method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
     method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
+    method public void setStatusBarEnabledState(android.content.ComponentName, boolean);
     method public int setStorageEncryption(android.content.ComponentName, boolean);
     method public void setTrustAgentConfiguration(android.content.ComponentName, android.content.ComponentName, android.os.PersistableBundle);
     method public void setUninstallBlocked(android.content.ComponentName, java.lang.String, boolean);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a0a6c4c..44760ce 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4210,4 +4210,20 @@
             return false;
         }
     }
+
+    /**
+     * Called by device owner to set the enabled state of the status bar. Disabling the status
+     * bar blocks notifications, quick settings and other screen overlays that allow escaping from
+     * a single use device.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param enabled New state of the status bar.
+     */
+    public void setStatusBarEnabledState(ComponentName admin, boolean enabled) {
+        try {
+            mService.setStatusBarEnabledState(admin, enabled);
+        } catch (RemoteException re) {
+            Log.w(TAG, "Failed talking with device policy service", re);
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 131b99c..7502e1d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -223,4 +223,5 @@
     PersistableBundle getOtaPolicy();
 
     boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
+    void setStatusBarEnabledState(in ComponentName who, boolean enabled);
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 6cb839e..87e0603 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -27,6 +27,7 @@
     void expandNotificationsPanel();
     void collapsePanels();
     void disable(int what, IBinder token, String pkg);
+    void disableForUser(int what, IBinder token, String pkg, int userId);
     void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription);
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index f6df757..184224b 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -79,7 +79,7 @@
 
         public void binderDied() {
             Slog.i(TAG, "binder died for pkg=" + pkg);
-            disableInternal(userId, 0, token, pkg);
+            disableForUser(0, token, pkg, userId);
             token.unlinkToDeath(this, 0);
         }
     }
@@ -194,10 +194,11 @@
 
     @Override
     public void disable(int what, IBinder token, String pkg) {
-        disableInternal(mCurrentUserId, what, token, pkg);
+        disableForUser(what, token, pkg, mCurrentUserId);
     }
 
-    private void disableInternal(int userId, int what, IBinder token, String pkg) {
+    @Override
+    public void disableForUser(int what, IBinder token, String pkg, int userId) {
         enforceStatusBar();
 
         synchronized (mLock) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c7d2b86..fcf6189 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -32,6 +32,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.StatusBarManager;
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DeviceAdminReceiver;
 import android.app.admin.DevicePolicyManager;
@@ -102,6 +103,7 @@
 
 import com.android.internal.R;
 import com.android.internal.os.storage.ExternalStorageFormatter;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.Preconditions;
@@ -152,7 +154,11 @@
 
     private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
-    private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
+    private static final String TAG_LOCK_TASK_COMPONENTS = "lock-task-component";
+
+    private static final String TAG_STATUS_BAR = "statusbar";
+
+    private static final String ATTR_ENABLED = "enabled";
 
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
@@ -172,6 +178,12 @@
 
     private static final String ATTR_DELEGATED_CERT_INSTALLER = "delegated-cert-installer";
 
+    private static final int STATUS_BAR_DISABLE_MASK =
+            StatusBarManager.DISABLE_EXPAND |
+            StatusBarManager.DISABLE_NOTIFICATION_ICONS |
+            StatusBarManager.DISABLE_NOTIFICATION_ALERTS |
+            StatusBarManager.DISABLE_SEARCH;
+
     private static final Set<String> DEVICE_OWNER_USER_RESTRICTIONS;
     static {
         DEVICE_OWNER_USER_RESTRICTIONS = new HashSet();
@@ -237,6 +249,8 @@
     // Stores and loads state on device and profile owners.
     private DeviceOwner mDeviceOwner;
 
+    private final Binder mToken = new Binder();
+
     /**
      * Whether or not device admin feature is supported. If it isn't return defaults for all
      * public methods.
@@ -287,6 +301,8 @@
         // This is the list of component allowed to start lock task mode.
         final List<String> mLockTaskPackages = new ArrayList<>();
 
+        boolean mStatusBarEnabledState = true;
+
         ComponentName mRestrictionsProvider;
 
         String mDelegatedCertInstallerPackage;
@@ -1429,9 +1445,15 @@
 
             for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
                 String component = policy.mLockTaskPackages.get(i);
-                out.startTag(null, LOCK_TASK_COMPONENTS_XML);
+                out.startTag(null, TAG_LOCK_TASK_COMPONENTS);
                 out.attribute(null, "name", component);
-                out.endTag(null, LOCK_TASK_COMPONENTS_XML);
+                out.endTag(null, TAG_LOCK_TASK_COMPONENTS);
+            }
+
+            if (!policy.mStatusBarEnabledState) {
+                out.startTag(null, TAG_STATUS_BAR);
+                out.attribute(null, ATTR_ENABLED, Boolean.toString(policy.mStatusBarEnabledState));
+                out.endTag(null, TAG_STATUS_BAR);
             }
 
             out.endTag(null, "policies");
@@ -1552,9 +1574,13 @@
                     policy.mActivePasswordNonLetter = Integer.parseInt(
                             parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
-                } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
+                } else if (TAG_LOCK_TASK_COMPONENTS.equals(tag)) {
                     policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
                     XmlUtils.skipCurrentTag(parser);
+                } else if (TAG_STATUS_BAR.equals(tag)) {
+                    policy.mStatusBarEnabledState = Boolean.parseBoolean(
+                            parser.getAttributeValue(null, ATTR_ENABLED));
+                    XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
                     XmlUtils.skipCurrentTag(parser);
@@ -1679,7 +1705,7 @@
         if (!mHasFeature) {
             return;
         }
-        getUserData(UserHandle.USER_OWNER);
+        DevicePolicyData policy = getUserData(UserHandle.USER_OWNER);
         loadDeviceOwner();
         cleanUpOldUsers();
         // Register an observer for watching for user setup complete.
@@ -1696,6 +1722,10 @@
                     getScreenCaptureDisabled(null, userHandle));
         }
 
+        if (mDeviceOwner != null && mDeviceOwner.hasDeviceOwner()
+                && !policy.mStatusBarEnabledState) {
+            setStatusBarEnabledStateInternal(STATUS_BAR_DISABLE_MASK, UserHandle.USER_OWNER);
+        }
     }
 
     private void cleanUpOldUsers() {
@@ -5830,6 +5860,38 @@
         return true;
     }
 
+    @Override
+    public void setStatusBarEnabledState(ComponentName who, boolean enabled) {
+        int userId = UserHandle.getCallingUserId();
+        synchronized (this) {
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            DevicePolicyData policy = getUserData(userId);
+            if (policy.mStatusBarEnabledState != enabled) {
+                policy.mStatusBarEnabledState = enabled;
+                setStatusBarEnabledStateInternal(
+                        enabled ? StatusBarManager.DISABLE_NONE : STATUS_BAR_DISABLE_MASK,
+                        userId);
+                saveSettingsLocked(userId);
+            }
+        }
+    }
+
+    private void setStatusBarEnabledStateInternal(int flags, int userId) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            IStatusBarService statusBarService = IStatusBarService.Stub.asInterface(
+                    ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+            if (statusBarService != null) {
+                statusBarService.disableForUser(flags, mToken,
+                        mDeviceOwner.getDeviceOwnerPackageName(), userId);
+            }
+        } catch (RemoteException e) {
+            Slog.e(LOG_TAG, "Failed to disable the status bar", e);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     /**
      * We need to update the internal state of whether a user has completed setup once. After
      * that, we ignore any changes that reset the Settings.Secure.USER_SETUP_COMPLETE changes