Allow changing signing cert for system apps that use shared users

This allows changing the signing certificate of system apps that use
shared users across an OTA, but still verifies that all signatures
within a given boot are consistent for a given shared user.

Bug: 72837107
Bug: 77973716
Bug: 74501739
Test: Flashed dev-keys build into slot b and (local) release-keys build
into slot a, booted into b, then successfully booted into a without
wiping userdata.
Test: Same as above, but treated com.android.mtp (uses android.media UID)
as PRESIGNED during release signing so it kept dev-keys. Verified it
wasn't scanned first and that only that package was rejected.
Test: Same as above, but treated com.android.providers.downloads (uses
android.media UID) as PRESIGNED during release signing so it kept
dev-keys. Verified it was scanned first (out of APKs using this UID) and
that only this package was accepted. Verified other APKs using
android.media UID that were scanned after were rejected.

Change-Id: I2076e8358cae22e45a24aa4c32a1b8dd446679ca
Merged-In: I2076e8358cae22e45a24aa4c32a1b8dd446679ca
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 86fc0cf..62b9972 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11000,6 +11000,12 @@
                     // We just determined the app is signed correctly, so bring
                     // over the latest parsed certs.
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
+
+                    if (signatureCheckPs.sharedUser != null) {
+                        if (signatureCheckPs.sharedUser.signaturesChanged == null) {
+                            signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+                        }
+                    }
                 } catch (PackageManagerException e) {
                     if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                         throw e;
@@ -11007,19 +11013,25 @@
                     // The signature has changed, but this package is in the system
                     // image...  let's recover!
                     pkgSetting.signatures.mSignatures = pkg.mSignatures;
-                    // However...  if this package is part of a shared user, but it
-                    // doesn't match the signature of the shared user, let's fail.
-                    // What this means is that you can't change the signatures
-                    // associated with an overall shared user, which doesn't seem all
-                    // that unreasonable.
+
+                    // If the system app is part of a shared user we allow that shared user to
+                    // change signatures as well as part of an OTA. We still need to verify that the
+                    // signatures are consistent within the shared user for a given boot, so only
+                    // allow updating the signatures on the first package scanned for the shared
+                    // user (i.e. if the signaturesChanged state hasn't been initialized yet in
+                    // SharedUserSetting).
                     if (signatureCheckPs.sharedUser != null) {
-                        if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+                        if (signatureCheckPs.sharedUser.signaturesChanged != null &&
+                            compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
                                 pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
                             throw new PackageManagerException(
                                     INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
                                     "Signature mismatch for shared user: "
                                             + pkgSetting.sharedUser);
                         }
+
+                        signatureCheckPs.sharedUser.signatures.mSignatures = pkg.mSignatures;
+                        signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
                     }
                     // File a report about this.
                     String msg = "System package " + pkg.packageName
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 06e020a..74294ea 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -33,6 +33,7 @@
     final ArraySet<PackageSetting> packages = new ArraySet<PackageSetting>();
 
     final PackageSignatures signatures = new PackageSignatures();
+    Boolean signaturesChanged;
 
     SharedUserSetting(String _name, int _pkgFlags, int _pkgPrivateFlags) {
         super(_pkgFlags, _pkgPrivateFlags);