[ADI][45/N] bypass developer verification in emergency cases

An emergency situation is when the developer verification policy
is BLOCK_FAIL_CLOSED, and the verifier fails to be launched or
connected or it times out, while one of the following apps are being
installed or updated:

* The verifier itself
* The original update-owner of the verifier as specified in sysconfig
* The emergency installer of the verifier's original update-owner

Because these are critical packages, we don't want to block them in the
emergency situations. This CL allows the developer verification bypass
under such situations.

BUG: 360129657
FLAG: android.content.pm.verification_service
Test: atest CtsRootDeveloperVerificationPrivInstallerTarget36TestCases CtsRootDeveloperVerificationInstallerTarget36TestCases CtsRootDeveloperVerificationPrivInstallerTarget35TestCases CtsRootDeveloperVerificationInstallerTarget35TestCases
Test: atest com.android.server.pm.PackageInstallerSessionTest

Change-Id: I925b2b4cfb7c92277adef9f09bf322fb5af9d3eb
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 5f9d1a8..8a2ba85 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -175,7 +175,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.server.EventLogTags;
-import com.android.server.SystemConfig;
 import com.android.server.criticalevents.CriticalEventLog;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.parsing.PackageCacher;
@@ -336,8 +335,7 @@
         final String oldUpdateOwner =
                 pkgAlreadyExists ? oldPkgSetting.getInstallSource().mUpdateOwnerPackageName : null;
         final String updateOwnerFromSysconfig = isApex || !pkgSetting.isSystem() ? null
-                : mPm.mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(
-                        parsedPackage.getPackageName());
+                : mPm.getSystemAppUpdateOwnerPackageName(parsedPackage.getPackageName());
         final boolean isUpdateOwnershipDenylisted =
                 mUpdateOwnershipHelper.isUpdateOwnershipDenylisted(parsedPackage.getPackageName());
         final boolean isUpdateOwnershipEnabled = oldUpdateOwner != null;
@@ -483,12 +481,11 @@
         if (listItems != null && !listItems.isEmpty()) {
             mUpdateOwnershipHelper.addToUpdateOwnerDenyList(pkgSetting.getPackageName(),
                     listItems);
-            SystemConfig config = SystemConfig.getInstance();
             synchronized (mPm.mLock) {
                 for (String unownedPackage : listItems) {
                     PackageSetting unownedSetting = mPm.mSettings.getPackageLPr(unownedPackage);
                     if (unownedSetting != null
-                            && config.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
+                            && mPm.getSystemAppUpdateOwnerPackageName(unownedPackage) == null) {
                         unownedSetting.setUpdateOwnerPackage(null);
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 386b269..67ad2ba 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -55,6 +55,7 @@
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
 import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_BYPASSED_REASON_ADB;
+import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY;
 import static android.content.pm.verify.developer.DeveloperVerificationSession.DEVELOPER_VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE;
 import static android.os.Process.INVALID_UID;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
@@ -3090,17 +3091,62 @@
                 return false;
             }
         }
+        return true;
+    }
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    boolean shouldAllowDeveloperVerificationEmergencyBypass(String packageName, Computer snapshot) {
         final String verifierPackageName = mDeveloperVerifierController.getVerifierPackageName();
-        synchronized (mLock) {
-            if (TextUtils.equals(verifierPackageName, mPackageName)) {
-                // The verifier itself is being updated. Skip.
-                // TODO(b/360129657): log bypass reason and this bypass should only happen if the
-                // current verifier cannot be connected or isn't responding.
-                Slog.w(TAG, "Skipping verification service because the verifier is being updated");
+        if (verifierPackageName == null) {
+            // Impossible condition. The verifier must exist because otherwise we wouldn't get
+            // here. Added to prevent lint warnings.
+            return false;
+        }
+        if (packageName == null) {
+            return false;
+        }
+        PackageStateInternal ps = snapshot.getPackageStateInternal(packageName, Process.SYSTEM_UID);
+        if (ps == null || !ps.isSystem()) {
+            // The app being installed must be a system app to be considered a critical app for
+            // the emergency bypass.
+            return false;
+        }
+        // Check if app being installed is the verifier itself.
+        if (TextUtils.equals(verifierPackageName, packageName)) {
+            Slog.d(TAG, "Bypassing developer verification because the verifier is being updated");
+            return true;
+        }
+        // Check if app being installed is the sysconfig-specified update-owner of the verifier.
+        final String updateOwnerPackageName = mPm.getSystemAppUpdateOwnerPackageName(
+                verifierPackageName);
+        if (updateOwnerPackageName == null) {
+            // No sysconfig-specified update-owner for the verifier. No need to check further.
+            return false;
+        }
+        if (TextUtils.equals(updateOwnerPackageName, packageName)) {
+            Slog.d(TAG, "Bypassing verification service because the sysconfig-specified "
+                    + "update owner of the verifier is being updated");
+            return true;
+        }
+        // Check if app being installed is the emergency installer of the sysconfig-specified
+        // update-owner of the verifier.
+        if (isEmergencyInstallerEnabled(updateOwnerPackageName, snapshot, userId, ps.getAppId())) {
+            final PackageStateInternal psUpdateOwner = snapshot.getPackageStateInternal(
+                    updateOwnerPackageName, Process.SYSTEM_UID);
+            if (psUpdateOwner == null || psUpdateOwner.getPkg() == null) {
+                // Impossible condition, because the if clause above already checked this.
+                // Added to prevent lint warnings.
                 return false;
             }
+            String emergencyInstallerPackageName = psUpdateOwner.getPkg().getEmergencyInstaller();
+            if (emergencyInstallerPackageName != null
+                    && TextUtils.equals(emergencyInstallerPackageName, packageName)) {
+                Slog.d(TAG, "Bypassing verification service because the "
+                        + "emergency installer of the verifier's update owner is being updated");
+                return true;
+            }
         }
-        return true;
+        return false;
     }
 
     private void retryDeveloperVerificationSession(Supplier<Computer> snapshotSupplier) {
@@ -3241,6 +3287,17 @@
                     resumeVerify();
                     return;
                 }
+                if (shouldAllowDeveloperVerificationEmergencyBypass(
+                        getPackageName(), mPm.snapshotComputer())) {
+                    // Bypass verification when critical package is being updated and the verifier
+                    // cannot be connected.
+                    synchronized (mMetrics) {
+                        mMetrics.onDeveloperVerificationBypassed(
+                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
+                    }
+                    resumeVerify();
+                    return;
+                }
 
                 mVerificationUserActionNeededReason =
                         DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
@@ -3267,6 +3324,17 @@
                     resumeVerify();
                     return;
                 }
+                if (shouldAllowDeveloperVerificationEmergencyBypass(
+                        getPackageName(), mPm.snapshotComputer())) {
+                    // Bypass verification when critical package is being updated and the verifier
+                    // cannot be connected.
+                    synchronized (mMetrics) {
+                        mMetrics.onDeveloperVerificationBypassed(
+                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
+                    }
+                    resumeVerify();
+                    return;
+                }
 
                 mVerificationUserActionNeededReason =
                         DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
@@ -3304,6 +3372,17 @@
                     resumeVerify();
                     return;
                 }
+                if (shouldAllowDeveloperVerificationEmergencyBypass(
+                        getPackageName(), mPm.snapshotComputer())) {
+                    // Bypass verification when critical package is being updated and the verifier
+                    // has timed out.
+                    synchronized (mMetrics) {
+                        mMetrics.onDeveloperVerificationBypassed(
+                                DEVELOPER_VERIFICATION_BYPASSED_REASON_EMERGENCY);
+                    }
+                    resumeVerify();
+                    return;
+                }
 
                 mVerificationUserActionNeededReason =
                         DEVELOPER_VERIFICATION_USER_ACTION_NEEDED_REASON_UNKNOWN;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 081a5c3..1f7a050 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8450,4 +8450,12 @@
     String getDeveloperVerificationPolicyDelegatePackageName() {
         return mDeveloperVerificationPolicyDelegatePackage;
     }
+
+    /**
+     * @return The update-owner of the given package name as specified in the system config file.
+     */
+    @Nullable
+    public String getSystemAppUpdateOwnerPackageName(String packageName) {
+        return mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(packageName);
+    }
 }
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
index 0bd9377..73572f7 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageInstallerSessionTest.kt
@@ -28,11 +28,14 @@
 import android.os.Parcel
 import android.os.PersistableBundle
 import android.os.Process
+import android.os.UserHandle
 import android.platform.test.annotations.Presubmit
 import android.util.AtomicFile
 import android.util.Slog
 import android.util.Xml
 import com.android.internal.os.BackgroundThread
+import com.android.internal.pm.parsing.pkg.AndroidPackageInternal
+import com.android.server.pm.pkg.PackageStateInternal
 import com.android.server.pm.verify.developer.DeveloperVerifierController
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
@@ -49,6 +52,7 @@
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
@@ -62,6 +66,7 @@
         private const val TAG_SESSIONS = "sessions"
         private const val TEST_KEY_FOR_EXTENSION_PARAMS = "testKey"
         private const val TEST_VALUE_FOR_EXTENSION_PARAMS = "testValue"
+        private const val USER_ID = 456
     }
 
     @JvmField
@@ -73,6 +78,8 @@
 
     @Mock
     lateinit var mMockPackageManagerInternal: PackageManagerService
+    @Mock
+    lateinit var mMockDeveloperVerifierController: DeveloperVerifierController
 
     @Mock
     lateinit var mSnapshot: Computer
@@ -151,6 +158,138 @@
         writeRestoreAssert(listOf(session, childSession1, childSession2))
     }
 
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNullPackageName() {
+        // Test no package name
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            null, mSnapshot)).isFalse()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonVerifierPackageName() {
+        // Test no verifier package name
+        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(null)
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            "testPackageName", mSnapshot)).isFalse()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonPreinstalledApp() {
+        val testPackageName = "testPackageName"
+        val session = createSession()
+        whenever(mSnapshot.getPackageStateInternal(eq(testPackageName), eq(Process.SYSTEM_UID)))
+            .thenReturn(null)
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            testPackageName, mSnapshot)).isFalse()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonSystemApp() {
+        val testPackageName = "testPackageName"
+        val session = createSession()
+        val mockPs = mock(PackageStateInternal::class.java)
+        whenever(mockPs.isSystem).thenReturn(false)
+        whenever(mSnapshot.getPackageStateInternal(eq(testPackageName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockPs)
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            testPackageName, mSnapshot)).isFalse()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassForVerifier() {
+        val verifierPackageName = "verifierPackageName"
+        val mockPs = mock(PackageStateInternal::class.java)
+        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
+            verifierPackageName)
+        whenever(mockPs.isSystem).thenReturn(true)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(verifierPackageName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockPs)
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            verifierPackageName, mSnapshot)).isTrue()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassReturnsFalseForNonUpdateOwner() {
+        val verifierPackageName = "verifierPackageName"
+        val updateOwnerName = "updateOwnerPackageName"
+        val mockPs = mock(PackageStateInternal::class.java)
+        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
+            verifierPackageName)
+        whenever(mockPs.isSystem).thenReturn(true)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockPs)
+        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
+            anyString())).thenReturn(null)
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            updateOwnerName, mSnapshot)).isFalse()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassForUpdateOwner() {
+        val verifierPackageName = "verifierPackageName"
+        val updateOwnerName = "updateOwnerPackageName"
+        val mockPs = mock(PackageStateInternal::class.java)
+        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
+            verifierPackageName)
+        whenever(mockPs.isSystem).thenReturn(true)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockPs)
+        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
+            eq(verifierPackageName))).thenReturn(updateOwnerName)
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            updateOwnerName, mSnapshot)).isTrue()
+    }
+
+    @Test
+    fun testShouldAllowDeveloperVerificationEmergencyBypassForEmergencyInstaller() {
+        val verifierPackageName = "verifierPackageName"
+        val updateOwnerName = "updateOwnerPackageName"
+        val emergencyInstallerPackageName = "emergencyInstallerPackageName"
+        val mockPs = mock(PackageStateInternal::class.java)
+        val mockUpdateOwnerPs = mock(PackageStateInternal::class.java)
+        val mockUpdateOwnerPkg = mock(AndroidPackageInternal::class.java)
+        val mockUid = 10001
+        val mockUpdateOwnerUid = 10200
+        whenever(mockPs.appId).thenReturn(mockUid)
+        whenever(mockUpdateOwnerPs.appId).thenReturn(mockUpdateOwnerUid)
+        whenever(mockUpdateOwnerPkg.emergencyInstaller).thenReturn(
+            emergencyInstallerPackageName)
+        whenever(mMockDeveloperVerifierController.verifierPackageName).thenReturn(
+            verifierPackageName)
+        whenever(mockPs.isSystem).thenReturn(true)
+        whenever(mockUpdateOwnerPs.isSystem).thenReturn(true)
+        whenever(mockUpdateOwnerPs.pkg).thenReturn(mockUpdateOwnerPkg)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(updateOwnerName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockUpdateOwnerPs)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(updateOwnerName)))
+            .thenReturn(mockUpdateOwnerPs)
+        whenever(mSnapshot.getPackageStateInternal(
+            eq(emergencyInstallerPackageName), eq(Process.SYSTEM_UID)))
+            .thenReturn(mockPs)
+        whenever(mSnapshot.getPackagesForUid(eq(mockUid))).thenReturn(
+            listOf(emergencyInstallerPackageName).toTypedArray())
+        whenever(mSnapshot.checkUidPermission(anyString(),
+            eq(UserHandle.getUid(USER_ID, mockUpdateOwnerUid))))
+            .thenReturn(PackageManager.PERMISSION_GRANTED)
+        whenever(mSnapshot.checkUidPermission(anyString(), eq(mockUid)))
+            .thenReturn(PackageManager.PERMISSION_GRANTED)
+        whenever(mMockPackageManagerInternal.getSystemAppUpdateOwnerPackageName(
+            eq(verifierPackageName))).thenReturn(updateOwnerName)
+        val session = createSession()
+        assertThat(session.shouldAllowDeveloperVerificationEmergencyBypass(
+            emergencyInstallerPackageName, mSnapshot)).isTrue()
+    }
+
     private fun createSession(
         staged: Boolean = false,
         sessionId: Int = 123,
@@ -183,7 +322,7 @@
             /* looper */ BackgroundThread.getHandler().looper,
             /* stagingManager */ null,
             /* sessionId */ sessionId,
-            /* userId */ 456,
+            /* userId */ USER_ID,
             /* installerUid */ Process.myUid(),
             /* installSource */ installSource,
             /* sessionParams */ params,
@@ -205,7 +344,7 @@
             /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
             /* stagedSessionErrorMessage */ "some error",
             /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
-            /* VerifierController */ mock(DeveloperVerifierController::class.java),
+            /* VerifierController */ mMockDeveloperVerifierController,
             /* initialVerificationPolicy */ DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
             /* currentVerificationPolicy */ DEVELOPER_VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
             /* installDependencyHelper */ null