Disallow disable / hide device provision app

+ Rename functions in ProtectedPackages.
+ Add a Set in ProtectedPackages to store protected package.

Bug: 29116229
Change-Id: Ib7dd93a158c09ebbf70f4d57c1afbd2c5102edbd
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 14f7727..d208fe7 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -157,7 +157,7 @@
             int deviceOwnerUserId, String deviceOwner, SparseArray<String> profileOwners);
 
     /**
-     * Whether a package's data be cleared.
+     * Returns {@code true} if a given package can't be wiped. Otherwise, returns {@code false}.
      */
-    public abstract boolean canPackageBeWiped(int userId, String packageName);
+    public abstract boolean isPackageDataProtected(int userId, String packageName);
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a8bb0e0..e463f44 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2499,4 +2499,6 @@
     <!-- True if the device supports system navigation keys. -->
     <bool name="config_supportSystemNavigationKeys">false</bool>
 
+    <!-- Package name for the device provisioning package. -->
+    <string name="config_deviceProvisioningPackage"></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6b7d32d..759ed32 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2632,4 +2632,7 @@
 
   <java-symbol type="layout" name="unsupported_display_size_dialog_content" />
   <java-symbol type="string" name="unsupported_display_size_message" />
+
+  <!-- Package name for the device provisioning package -->
+  <java-symbol type="string" name="config_deviceProvisioningPackage" />
 </resources>
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b5b115c..02d6a42d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5423,10 +5423,10 @@
             IPackageManager pm = AppGlobals.getPackageManager();
             int pkgUid = -1;
             synchronized(this) {
-                if (getPackageManagerInternalLocked().canPackageBeWiped(
+                if (getPackageManagerInternalLocked().isPackageDataProtected(
                         userId, packageName)) {
                     throw new SecurityException(
-                            "Cannot clear data for a device owner or a profile owner");
+                            "Cannot clear data for a protected package: " + packageName);
                 }
 
                 try {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 62cdefa..a30422d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -627,7 +627,7 @@
     @GuardedBy("mPackages")
     final ArraySet<String> mFrozenPackages = new ArraySet<>();
 
-    final ProtectedPackages mProtectedPackages = new ProtectedPackages();
+    final ProtectedPackages mProtectedPackages;
 
     boolean mRestoredSettings;
 
@@ -2281,6 +2281,8 @@
         mSystemPermissions = systemConfig.getSystemPermissions();
         mAvailableFeatures = systemConfig.getAvailableFeatures();
 
+        mProtectedPackages = new ProtectedPackages(mContext);
+
         synchronized (mInstallLock) {
         // writer
         synchronized (mPackages) {
@@ -11661,6 +11663,12 @@
                 if (pkgSetting == null) {
                     return false;
                 }
+                // Only allow protected packages to hide themselves.
+                if (hidden && !UserHandle.isSameApp(uid, pkgSetting.appId)
+                        && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                    Slog.w(TAG, "Not hiding protected package: " + packageName);
+                    return false;
+                }
                 if (pkgSetting.getHidden(userId) != hidden) {
                     pkgSetting.setHidden(hidden, userId);
                     mSettings.writePackageRestrictionsLPr(userId);
@@ -16431,8 +16439,9 @@
         enforceCrossUserPermission(Binder.getCallingUid(), userId,
                 true /* requireFullPermission */, false /* checkShell */, "clear application data");
 
-        if (mProtectedPackages.canPackageBeWiped(userId, packageName)) {
-            throw new SecurityException("Cannot clear data for a device owner or a profile owner");
+        if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
+            throw new SecurityException("Cannot clear data for a protected package: "
+                    + packageName);
         }
         // Queue up an async operation since the package deletion may take a little while.
         mHandler.post(new Runnable() {
@@ -17758,9 +17767,9 @@
                         + Binder.getCallingPid()
                         + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
             }
-            // Don't allow changing profile and device owners.
-            if (mProtectedPackages.canPackageStateBeChanged(userId, packageName)) {
-                throw new SecurityException("Cannot disable a device owner or a profile owner");
+            // Don't allow changing protected packages.
+            if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
+                throw new SecurityException("Cannot disable a protected package: " + packageName);
             }
         }
 
@@ -20892,9 +20901,8 @@
         }
 
         @Override
-        public boolean canPackageBeWiped(int userId, String packageName) {
-            return mProtectedPackages.canPackageBeWiped(userId,
-                    packageName);
+        public boolean isPackageDataProtected(int userId, String packageName) {
+            return mProtectedPackages.isPackageDataProtected(userId, packageName);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/ProtectedPackages.java b/services/core/java/com/android/server/pm/ProtectedPackages.java
index 7bdea18..e67364a 100644
--- a/services/core/java/com/android/server/pm/ProtectedPackages.java
+++ b/services/core/java/com/android/server/pm/ProtectedPackages.java
@@ -16,10 +16,15 @@
 
 package com.android.server.pm;
 
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.Context;
 import android.os.UserHandle;
 import android.util.SparseArray;
 
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+
 /**
  * Manages package names that need special protection.
  *
@@ -29,61 +34,87 @@
  */
 public class ProtectedPackages {
     @UserIdInt
+    @GuardedBy("this")
     private int mDeviceOwnerUserId;
 
+    @Nullable
+    @GuardedBy("this")
     private String mDeviceOwnerPackage;
 
+    @Nullable
+    @GuardedBy("this")
     private SparseArray<String> mProfileOwnerPackages;
 
-    private final Object mLock = new Object();
+    @Nullable
+    @GuardedBy("this")
+    private final String mDeviceProvisioningPackage;
+
+    private final Context mContext;
+
+    public ProtectedPackages(Context context) {
+        mContext = context;
+        mDeviceProvisioningPackage = mContext.getResources().getString(
+                R.string.config_deviceProvisioningPackage);
+    }
 
     /**
      * Sets the device/profile owner information.
      */
-    public void setDeviceAndProfileOwnerPackages(
+    public synchronized void setDeviceAndProfileOwnerPackages(
             int deviceOwnerUserId, String deviceOwnerPackage,
             SparseArray<String> profileOwnerPackages) {
-        synchronized (mLock) {
-            mDeviceOwnerUserId = deviceOwnerUserId;
-            mDeviceOwnerPackage =
-                    (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
-            mProfileOwnerPackages = (profileOwnerPackages == null) ? null
-                    : profileOwnerPackages.clone();
-        }
+        mDeviceOwnerUserId = deviceOwnerUserId;
+        mDeviceOwnerPackage =
+                (deviceOwnerUserId == UserHandle.USER_NULL) ? null : deviceOwnerPackage;
+        mProfileOwnerPackages = (profileOwnerPackages == null) ? null
+                : profileOwnerPackages.clone();
     }
 
-    private boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
+    private synchronized boolean hasDeviceOwnerOrProfileOwner(int userId, String packageName) {
         if (packageName == null) {
             return false;
         }
-        synchronized (mLock) {
-            if (mDeviceOwnerPackage != null) {
-                if ((mDeviceOwnerUserId == userId)
-                        && (packageName.equals(mDeviceOwnerPackage))) {
-                    return true;
-                }
+        if (mDeviceOwnerPackage != null) {
+            if ((mDeviceOwnerUserId == userId)
+                    && (packageName.equals(mDeviceOwnerPackage))) {
+                return true;
             }
-            if (mProfileOwnerPackages != null) {
-                if (packageName.equals(mProfileOwnerPackages.get(userId))) {
-                    return true;
-                }
+        }
+        if (mProfileOwnerPackages != null) {
+            if (packageName.equals(mProfileOwnerPackages.get(userId))) {
+                return true;
             }
         }
         return false;
     }
 
     /**
-     * Whether a package or the components in a package's enabled state can be changed
-     * by other callers than itself.
+     * Returns {@code true} if a given package is protected. Otherwise, returns {@code false}.
+     *
+     * <p>A protected package means that, apart from the package owner, no system or privileged apps
+     * can modify its data or package state.
      */
-    public boolean canPackageStateBeChanged(@UserIdInt int userId, String packageName) {
-        return hasDeviceOwnerOrProfileOwner(userId, packageName);
+    private synchronized boolean isProtectedPackage(String packageName) {
+        return packageName != null && packageName.equals(mDeviceProvisioningPackage);
     }
 
     /**
-     * Whether a package's data be cleared.
+     * Returns {@code true} if a given package's state is protected. Otherwise, returns
+     * {@code false}.
+     *
+     * <p>This is not applicable if the caller is the package owner.
      */
-    public boolean canPackageBeWiped(@UserIdInt int userId, String packageName) {
-        return hasDeviceOwnerOrProfileOwner(userId, packageName);
+    public boolean isPackageStateProtected(@UserIdInt int userId, String packageName) {
+        return hasDeviceOwnerOrProfileOwner(userId, packageName)
+                || isProtectedPackage(packageName);
+    }
+
+    /**
+     * Returns {@code true} if a given package's data is protected. Otherwise, returns
+     * {@code false}.
+     */
+    public boolean isPackageDataProtected(@UserIdInt int userId, String packageName) {
+        return hasDeviceOwnerOrProfileOwner(userId, packageName)
+                || isProtectedPackage(packageName);
     }
 }