Do not allow uninstalling while an app is pinned

If an app is pinned we want to avoid ways to unpin without entering a
set passcode. If the package of the base activity in the pinned activity
stack is uninstalled then the device exits pinning mode so we want to
restrict uninstalling this package.

Bug: 135604684
Test: Pin test app, test app tries to uninstall itself
      Pin test app, `adb uninstall`
      Pin test app, test app launches second test app, assert that
          second test app can be uninstalled but base test app can't

Change-Id: I32ee438e9dd9e245bed6e6a9f4efd0abbb70de1f
Merged-In: I32ee438e9dd9e245bed6e6a9f4efd0abbb70de1f
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8a7214d..1270362 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1689,6 +1689,15 @@
     public static final int DELETE_FAILED_USED_SHARED_LIBRARY = -6;
 
     /**
+     * Deletion failed return code: this is passed to the
+     * {@link IPackageDeleteObserver} if the system failed to delete the package
+     * because there is an app pinned.
+     *
+     * @hide
+     */
+    public static final int DELETE_FAILED_APP_PINNED = -7;
+
+    /**
      * Return code that is passed to the {@link IPackageMoveObserver} when the
      * package has been successfully moved by the system.
      *
@@ -7545,6 +7554,7 @@
             case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED";
             case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED";
             case DELETE_FAILED_USED_SHARED_LIBRARY: return "DELETE_FAILED_USED_SHARED_LIBRARY";
+            case DELETE_FAILED_APP_PINNED: return "DELETE_FAILED_APP_PINNED";
             default: return Integer.toString(status);
         }
     }
@@ -7559,6 +7569,7 @@
             case DELETE_FAILED_OWNER_BLOCKED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
             case DELETE_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED;
             case DELETE_FAILED_USED_SHARED_LIBRARY: return PackageInstaller.STATUS_FAILURE_CONFLICT;
+            case DELETE_FAILED_APP_PINNED: return PackageInstaller.STATUS_FAILURE_BLOCKED;
             default: return PackageInstaller.STATUS_FAILURE;
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c20a912..1d9967e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -18162,6 +18162,19 @@
         final String packageName = versionedPackage.getPackageName();
         final long versionCode = versionedPackage.getLongVersionCode();
         final String internalPackageName;
+
+        try {
+            if (LocalServices.getService(ActivityTaskManagerInternal.class)
+                    .isBaseOfLockedTask(packageName)) {
+                observer.onPackageDeleted(
+                        packageName, PackageManager.DELETE_FAILED_APP_PINNED, null);
+                EventLog.writeEvent(0x534e4554, "127605586", -1, "");
+                return;
+            }
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+
         synchronized (mLock) {
             // Normalize package name to handle renamed packages and static libs
             internalPackageName = resolveInternalPackageNameLPr(packageName, versionCode);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index d4dd35f..eb749f6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -568,4 +568,10 @@
 
     /** Set all associated companion app that belongs to an userId. */
     public abstract void setCompanionAppPackages(int userId, Set<String> companionAppPackages);
+
+    /**
+     * @param packageName The package to check
+     * @return Whether the package is the base of any locked task
+     */
+    public abstract boolean isBaseOfLockedTask(String packageName);
 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0542ef9..8dbd661 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -7473,5 +7473,13 @@
                 mCompanionAppUidsMap.put(userId, result);
             }
         }
+
+
+        @Override
+        public boolean isBaseOfLockedTask(String packageName) {
+            synchronized (mGlobalLock) {
+                return getLockTaskController().isBaseOfLockedTask(packageName);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index c36dede..c4a42ab 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -871,6 +871,21 @@
     }
 
     /**
+     * @param packageName The package to check
+     * @return Whether the package is the base of any locked task
+     */
+    boolean isBaseOfLockedTask(String packageName) {
+        for (int i = 0; i < mLockTaskModeTasks.size(); i++) {
+            final Intent bi = mLockTaskModeTasks.get(i).getBaseIntent();
+            if (bi != null && packageName.equals(bi.getComponent()
+                    .getPackageName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Gets the cached value of LockTask feature flags for a specific user.
      */
     private int getLockTaskFeaturesForUser(int userId) {