[4/N] APIs for verification policy and failure reasons

+ Define failure reason codes
+ Define policy constants
+ Removed VERIFICATION_FAILED_REASON_NETWORK_LIMITED
+ Add getter/setter for session and global policy overrides
+ Allow Shell to have VERIFICATION_AGENT permission

FLAG: android.content.pm.verification_service

BUG: 360129103
BUG: 360129657

Test: atest CtsPackageManagerTestCases:VerifierServiceTest

Change-Id: Ibfeb88ad71677c4c61476c8af6b8607c131072f6
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fa4fc43..349d06c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4170,9 +4170,11 @@
   }
 
   public class PackageInstaller {
+    method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final int getVerificationPolicy();
     method @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull java.io.File, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @FlaggedApi("android.content.pm.read_install_info") @NonNull public android.content.pm.PackageInstaller.InstallInfo readInstallInfo(@NonNull android.os.ParcelFileDescriptor, @Nullable String, int) throws android.content.pm.PackageInstaller.PackageParsingException;
     method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setPermissionsResult(int, boolean);
+    method @FlaggedApi("android.content.pm.verification_service") @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public final boolean setVerificationPolicy(int);
     field public static final String ACTION_CONFIRM_INSTALL = "android.content.pm.action.CONFIRM_INSTALL";
     field public static final String ACTION_CONFIRM_PRE_APPROVAL = "android.content.pm.action.CONFIRM_PRE_APPROVAL";
     field public static final int DATA_LOADER_TYPE_INCREMENTAL = 2; // 0x2
@@ -4183,12 +4185,20 @@
     field @FlaggedApi("android.content.pm.archiving") public static final String EXTRA_DELETE_FLAGS = "android.content.pm.extra.DELETE_FLAGS";
     field public static final String EXTRA_LEGACY_STATUS = "android.content.pm.extra.LEGACY_STATUS";
     field @Deprecated public static final String EXTRA_RESOLVED_BASE_PATH = "android.content.pm.extra.RESOLVED_BASE_PATH";
+    field @FlaggedApi("android.content.pm.verification_service") public static final String EXTRA_VERIFICATION_FAILURE_REASON = "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
     field public static final int LOCATION_DATA_APP = 0; // 0x0
     field public static final int LOCATION_MEDIA_DATA = 2; // 0x2
     field public static final int LOCATION_MEDIA_OBB = 1; // 0x1
     field public static final int REASON_CONFIRM_PACKAGE_CHANGE = 0; // 0x0
     field public static final int REASON_OWNERSHIP_CHANGED = 1; // 0x1
     field public static final int REASON_REMIND_OWNERSHIP = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1; // 0x1
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0; // 0x0
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3; // 0x3
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1; // 0x1
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2; // 0x2
+    field @FlaggedApi("android.content.pm.verification_service") public static final int VERIFICATION_POLICY_NONE = 0; // 0x0
   }
 
   public static class PackageInstaller.InstallInfo {
@@ -4635,12 +4645,13 @@
     method @NonNull public android.content.pm.SigningInfo getSigningInfo();
     method @NonNull public android.net.Uri getStagedPackageUri();
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public long getTimeoutTime();
+    method public int getVerificationPolicy();
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus);
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationComplete(@NonNull android.content.pm.verify.pkg.VerificationStatus, @NonNull android.os.PersistableBundle);
     method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public void reportVerificationIncomplete(int);
+    method @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT) public boolean setVerificationPolicy(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.verify.pkg.VerificationSession> CREATOR;
-    field public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2; // 0x2
     field public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1; // 0x1
     field public static final int VERIFICATION_INCOMPLETE_UNKNOWN = 0; // 0x0
   }
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index 451c0e5..c911326 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -93,4 +93,10 @@
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES,android.Manifest.permission.REQUEST_INSTALL_PACKAGES})")
     void reportUnarchivalStatus(int unarchiveId, int status, long requiredStorageBytes, in PendingIntent userActionIntent, in UserHandle userHandle);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    int getVerificationPolicy();
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    boolean setVerificationPolicy(int policy);
 }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c673d58..cba7bc9 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -62,6 +62,8 @@
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.content.pm.verify.domain.DomainSet;
+import android.content.pm.verify.pkg.VerificationSession;
+import android.content.pm.verify.pkg.VerificationStatus;
 import android.graphics.Bitmap;
 import android.icu.util.ULocale;
 import android.net.Uri;
@@ -418,6 +420,21 @@
     public static final String EXTRA_WARNINGS = "android.content.pm.extra.WARNINGS";
 
     /**
+     * When verification is blocked as part of the installation, additional reason for the block
+     * will be provided to the installer with a {@link VerificationFailedReason} as part of the
+     * installation result returned via the {@link IntentSender} in
+     * {@link Session#commit(IntentSender)}. This extra is provided only when the installation has
+     * failed. Installers can use this extra to check if the installation failure was caused by a
+     * verification failure.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final String EXTRA_VERIFICATION_FAILURE_REASON =
+            "android.content.pm.extra.VERIFICATION_FAILURE_REASON";
+
+    /**
      * Streaming installation pending.
      * Caller should make sure DataLoader is able to prepare image and reinitiate the operation.
      *
@@ -760,6 +777,90 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UnarchivalStatus {}
 
+    /**
+     * Verification failed because of unknown reasons, such as when the verifier times out or cannot
+     * be connected. It can also corresponds to the status of
+     * {@link VerificationSession#VERIFICATION_INCOMPLETE_UNKNOWN} reported by the verifier via
+     * {@link VerificationSession#reportVerificationIncomplete(int)}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_UNKNOWN = 0;
+
+    /**
+     * Verification failed because the network is unavailable. This corresponds to the status of
+     * {@link VerificationSession#VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE} reported by the
+     * verifier via {@link VerificationSession#reportVerificationIncomplete(int)}.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE = 1;
+
+    /**
+     * Verification failed because the package is blocked, as reported by the verifier via
+     * {@link VerificationSession#reportVerificationComplete(VerificationStatus)} or
+     * {@link VerificationSession#reportVerificationComplete(VerificationStatus, PersistableBundle)}
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED = 2;
+
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            VERIFICATION_FAILED_REASON_UNKNOWN,
+            VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE,
+            VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED,
+    })
+    public @interface VerificationFailedReason {
+    }
+
+    /**
+     * Do not block installs, regardless of verification status.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_NONE = 0; // platform default
+    /**
+     * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED}.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_OPEN = 1;
+    /**
+     * Only block installations on {@link #VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED} and ask the
+     * user if they'd like to install anyway when the verification is blocked for other reason.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_WARN = 2;
+    /**
+     * Block installations whose verification status is blocked for any reason.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    public static final int VERIFICATION_POLICY_BLOCK_FAIL_CLOSED = 3;
+    /**
+     * @hide
+     */
+    @IntDef(value = {
+            VERIFICATION_POLICY_NONE,
+            VERIFICATION_POLICY_BLOCK_FAIL_OPEN,
+            VERIFICATION_POLICY_BLOCK_FAIL_WARN,
+            VERIFICATION_POLICY_BLOCK_FAIL_CLOSED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VerificationPolicy {
+    }
 
     /** Default set of checksums - includes all available checksums.
      * @see Session#requestChecksums  */
@@ -1503,6 +1604,40 @@
     }
 
     /**
+     * Return the current verification enforcement policy. This may only be called by the
+     * package currently set by the system as the verifier agent.
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public final @VerificationPolicy int getVerificationPolicy() {
+        try {
+            return mInstaller.getVerificationPolicy();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Set the current verification enforcement policy which will be applied to all the future
+     * installation sessions. This may only be called by the package currently set by the system as
+     * the verifier agent.
+     * @hide
+     * @return whether the new policy was successfully set.
+     */
+    @FlaggedApi(Flags.FLAG_VERIFICATION_SERVICE)
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public final boolean setVerificationPolicy(@VerificationPolicy int policy) {
+        try {
+            return mInstaller.setVerificationPolicy(policy);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * An installation that is being actively staged. For an install to succeed,
      * all existing and new packages must have identical package names, version
      * codes, and signing certificates.
diff --git a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
index 7a9484a..036c1e6 100644
--- a/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
+++ b/core/java/android/content/pm/verify/pkg/IVerificationSessionInterface.aidl
@@ -25,4 +25,6 @@
     long getTimeoutTime(int verificationId);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
     long extendTimeRemaining(int verificationId, long additionalMs);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)")
+    boolean setVerificationPolicy(int verificationId, int policy);
 }
\ No newline at end of file
diff --git a/core/java/android/content/pm/verify/pkg/VerificationSession.java b/core/java/android/content/pm/verify/pkg/VerificationSession.java
index 70b4a02..f393be8 100644
--- a/core/java/android/content/pm/verify/pkg/VerificationSession.java
+++ b/core/java/android/content/pm/verify/pkg/VerificationSession.java
@@ -22,6 +22,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.content.pm.Flags;
+import android.content.pm.PackageInstaller;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.SigningInfo;
 import android.net.Uri;
@@ -52,17 +53,12 @@
      * The verification cannot be completed because the network is unavailable.
      */
     public static final int VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE = 1;
-    /**
-     * The verification cannot be completed because the network is limited.
-     */
-    public static final int VERIFICATION_INCOMPLETE_NETWORK_LIMITED = 2;
 
     /**
      * @hide
      */
     @IntDef(prefix = {"VERIFICATION_INCOMPLETE_"}, value = {
             VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE,
-            VERIFICATION_INCOMPLETE_NETWORK_LIMITED,
             VERIFICATION_INCOMPLETE_UNKNOWN,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -85,6 +81,15 @@
     private final IVerificationSessionInterface mSession;
     @NonNull
     private final IVerificationSessionCallback mCallback;
+    /**
+     * The current policy that is active for the session. It might not be
+     * the same as the original policy that was initially assigned for this verification session,
+     * because the active policy can be overridden by {@link #setVerificationPolicy(int)}.
+     * <p>To improve the latency, store the original policy value and any changes made to it,
+     * so that {@link #getVerificationPolicy()} does not need to make a binder call to retrieve the
+     * currently active policy.</p>
+     */
+    private volatile @PackageInstaller.VerificationPolicy int mVerificationPolicy;
 
     /**
      * Constructor used by the system to describe the details of a verification session.
@@ -94,6 +99,7 @@
             @NonNull Uri stagedPackageUri, @NonNull SigningInfo signingInfo,
             @NonNull List<SharedLibraryInfo> declaredLibraries,
             @NonNull PersistableBundle extensionParams,
+            @PackageInstaller.VerificationPolicy int defaultPolicy,
             @NonNull IVerificationSessionInterface session,
             @NonNull IVerificationSessionCallback callback) {
         mId = id;
@@ -103,6 +109,7 @@
         mSigningInfo = signingInfo;
         mDeclaredLibraries = declaredLibraries;
         mExtensionParams = extensionParams;
+        mVerificationPolicy = defaultPolicy;
         mSession = session;
         mCallback = callback;
     }
@@ -144,7 +151,7 @@
 
     /**
      * Returns a mapping of any shared libraries declared in the manifest
-     * to the {@link SharedLibraryInfo#Type} that is declared. This will be an empty
+     * to the {@link SharedLibraryInfo.Type} that is declared. This will be an empty
      * map if no shared libraries are declared by the package.
      */
     @NonNull
@@ -174,6 +181,39 @@
     }
 
     /**
+     * Return the current policy that is active for this session.
+     * <p>If the policy for this session has been changed by {@link #setVerificationPolicy},
+     * the return value of this method is the current policy that is active for this session.
+     * Otherwise, the return value is the same as the initial policy that was assigned to the
+     * session when it was first created.</p>
+     */
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        return mVerificationPolicy;
+    }
+
+    /**
+     * Override the verification policy for this session.
+     * @return True if the override was successful, False otherwise.
+     */
+    @RequiresPermission(android.Manifest.permission.VERIFICATION_AGENT)
+    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+        if (mVerificationPolicy == policy) {
+            // No effective policy change
+            return true;
+        }
+        try {
+            if (mSession.setVerificationPolicy(mId, policy)) {
+                mVerificationPolicy = policy;
+                return true;
+            } else {
+                return false;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Extend the timeout for this session by the provided additionalMs to
      * fetch relevant information over the network or wait for the network.
      * This may be called multiple times. If the request would bypass any max
@@ -239,6 +279,7 @@
         mSigningInfo = SigningInfo.CREATOR.createFromParcel(in);
         mDeclaredLibraries = in.createTypedArrayList(SharedLibraryInfo.CREATOR);
         mExtensionParams = in.readPersistableBundle(getClass().getClassLoader());
+        mVerificationPolicy = in.readInt();
         mSession = IVerificationSessionInterface.Stub.asInterface(in.readStrongBinder());
         mCallback = IVerificationSessionCallback.Stub.asInterface(in.readStrongBinder());
     }
@@ -257,6 +298,7 @@
         mSigningInfo.writeToParcel(dest, flags);
         dest.writeTypedList(mDeclaredLibraries);
         dest.writePersistableBundle(mExtensionParams);
+        dest.writeInt(mVerificationPolicy);
         dest.writeStrongBinder(mSession.asBinder());
         dest.writeStrongBinder(mCallback.asBinder());
     }
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
index 987f68d..80255c5 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerificationSessionTest.java
@@ -16,6 +16,9 @@
 
 package android.content.pm.verify;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -23,6 +26,8 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.pm.SharedLibraryInfo;
@@ -73,6 +78,7 @@
     private static final long TEST_EXTEND_TIME = 2000L;
     private static final String TEST_KEY = "test key";
     private static final String TEST_VALUE = "test value";
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
 
     private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
     private final PersistableBundle mTestExtensionParams = new PersistableBundle();
@@ -90,7 +96,7 @@
         mTestExtensionParams.putString(TEST_KEY, TEST_VALUE);
         mTestSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
                 TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO, mTestDeclaredLibraries,
-                mTestExtensionParams, mTestSessionInterface, mTestCallback);
+                mTestExtensionParams, TEST_POLICY, mTestSessionInterface, mTestCallback);
     }
 
     @Test
@@ -118,6 +124,7 @@
         // structure is different, but all the key/value pairs should be preserved as before.
         assertThat(sessionFromParcel.getExtensionParams().getString(TEST_KEY))
                 .isEqualTo(mTestExtensionParams.getString(TEST_KEY));
+        assertThat(sessionFromParcel.getVerificationPolicy()).isEqualTo(TEST_POLICY);
     }
 
     @Test
@@ -152,4 +159,42 @@
         verify(mTestCallback, times(1)).reportVerificationIncomplete(
                 eq(TEST_ID), eq(reason));
     }
+
+    @Test
+    public void testPolicyNoOverride() {
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        // This "set" is a no-op
+        assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        verifyZeroInteractions(mTestSessionInterface);
+    }
+
+    @Test
+    public void testPolicyOverrideFail() throws Exception {
+        final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+        when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(false);
+        assertThat(mTestSession.setVerificationPolicy(newPolicy)).isFalse();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+        // Next "get" should not trigger binder call because the previous "set" has failed
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+        verifyNoMoreInteractions(mTestSessionInterface);
+    }
+
+    @Test
+    public void testPolicyOverrideSuccess() throws Exception {
+        final int newPolicy = VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+        when(mTestSessionInterface.setVerificationPolicy(anyInt(), anyInt())).thenReturn(true);
+        assertThat(mTestSession.setVerificationPolicy(newPolicy)).isTrue();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(newPolicy));
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(newPolicy);
+
+        // Setting back to the original policy should still trigger binder calls
+        assertThat(mTestSession.setVerificationPolicy(TEST_POLICY)).isTrue();
+        verify(mTestSessionInterface, times(1))
+                .setVerificationPolicy(eq(TEST_ID), eq(TEST_POLICY));
+        assertThat(mTestSession.getVerificationPolicy()).isEqualTo(TEST_POLICY);
+    }
 }
diff --git a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
index 7f73a1e..7807c8a 100644
--- a/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
+++ b/core/tests/coretests/src/android/content/pm/verify/VerifierServiceTest.java
@@ -16,6 +16,8 @@
 
 package android.content.pm.verify;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.eq;
@@ -52,6 +54,7 @@
     private static final String TEST_PACKAGE_NAME = "com.foo";
     private static final Uri TEST_PACKAGE_URI = Uri.parse("test://test");
     private static final SigningInfo TEST_SIGNING_INFO = new SigningInfo();
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
     private VerifierService mService;
     private VerificationSession mSession;
 
@@ -60,8 +63,7 @@
         mService = Mockito.mock(VerifierService.class, Answers.CALLS_REAL_METHODS);
         mSession = new VerificationSession(TEST_ID, TEST_INSTALL_SESSION_ID,
                 TEST_PACKAGE_NAME, TEST_PACKAGE_URI, TEST_SIGNING_INFO,
-                new ArrayList<>(),
-                new PersistableBundle(), null, null);
+                new ArrayList<>(), new PersistableBundle(), TEST_POLICY, null, null);
     }
 
     @Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e72f0e..4aa7e96 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -572,6 +572,7 @@
         <permission name="android.permission.READ_BLOCKED_NUMBERS" />
         <!-- Permission required for CTS test - PackageManagerTest -->
         <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
+        <permission name="android.permission.VERIFICATION_AGENT"/>
         <!-- Permission required for CTS test CtsInputTestCases -->
         <permission name="android.permission.OVERRIDE_SYSTEM_KEY_BEHAVIOR_IN_FOCUSED_WINDOW" />
         <!-- Permission required for CTS test - PackageManagerShellCommandInstallTest -->
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 408ed1e..b385aaa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -924,6 +924,7 @@
 
     <!-- Permission required for CTS test - CtsPackageManagerTestCases-->
     <uses-permission android:name="android.permission.DOMAIN_VERIFICATION_AGENT" />
+    <uses-permission android:name="android.permission.VERIFICATION_AGENT" />
 
     <!-- Permission required for Cts test - CtsInputTestCases -->
     <uses-permission
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 34d939b..f6a808b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -25,12 +25,14 @@
 import static android.content.pm.PackageInstaller.UNARCHIVAL_ERROR_USER_ACTION_NEEDED;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_GENERIC_ERROR;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
 import static android.content.pm.PackageManager.DELETE_ARCHIVE;
 import static android.content.pm.PackageManager.INSTALL_UNARCHIVE_DRAFT;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.pm.PackageArchiver.isArchivingEnabled;
+import static com.android.server.pm.PackageInstallerSession.isValidVerificationPolicy;
 import static com.android.server.pm.PackageManagerService.SHELL_PACKAGE_NAME;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -150,6 +152,7 @@
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.IntPredicate;
 import java.util.function.Supplier;
 
@@ -275,6 +278,13 @@
         }
     };
 
+    /**
+     * Default verification policy for incoming installation sessions.
+     * TODO(b/360129657): update the default policy.
+     */
+    private final AtomicInteger mVerificationPolicy = new AtomicInteger(
+            VERIFICATION_POLICY_BLOCK_FAIL_WARN);
+
     private static final class Lifecycle extends SystemService {
         private final PackageInstallerService mPackageInstallerService;
 
@@ -1042,7 +1052,7 @@
                 userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
                 null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
                 false, false, false, PackageManager.INSTALL_UNKNOWN, "", null,
-                mVerifierController);
+                mVerifierController, mVerificationPolicy.get());
 
         synchronized (mSessions) {
             mSessions.put(sessionId, session);
@@ -1866,6 +1876,34 @@
         }
     }
 
+    @Override
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.VERIFICATION_AGENT permission "
+                    + "to get the verification policy");
+        }
+        return mVerificationPolicy.get();
+    }
+
+    @Override
+    public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.VERIFICATION_AGENT)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need the "
+                    + "com.android.permission.VERIFICATION_AGENT permission "
+                    + "to set the verification policy");
+        }
+        if (!isValidVerificationPolicy(policy)) {
+            return false;
+        }
+        if (policy != mVerificationPolicy.get()) {
+            mVerificationPolicy.set(policy);
+        }
+        return true;
+    }
+
     private static int getSessionCount(SparseArray<PackageInstallerSession> sessions,
             int installerUid) {
         int count = 0;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 9e0ba84..04d0d18 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -21,9 +21,17 @@
 import static android.app.admin.DevicePolicyResources.Strings.Core.PACKAGE_UPDATED_BY_DO;
 import static android.content.pm.DataLoaderType.INCREMENTAL;
 import static android.content.pm.DataLoaderType.STREAMING;
+import static android.content.pm.PackageInstaller.EXTRA_VERIFICATION_FAILURE_REASON;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_OK;
 import static android.content.pm.PackageInstaller.UNARCHIVAL_STATUS_UNSET;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED;
+import static android.content.pm.PackageInstaller.VERIFICATION_FAILED_REASON_UNKNOWN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_WARN;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_NONE;
 import static android.content.pm.PackageItemInfo.MAX_SAFE_LABEL_LENGTH;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ABORTED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_SIGNATURE;
@@ -38,7 +46,7 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_STAGED;
 import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
-import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN;
+import static android.content.pm.verify.pkg.VerificationSession.VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE;
 import static android.os.Process.INVALID_UID;
 import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
 import static android.system.OsConstants.O_CREAT;
@@ -313,6 +321,7 @@
     private static final String ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT =
             "applicationEnabledSettingPersistent";
     private static final String ATTR_DOMAIN = "domain";
+    private static final String ATTR_VERIFICATION_POLICY = "verificationPolicy";
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
@@ -410,6 +419,11 @@
     private final PackageSessionProvider mSessionProvider;
     private final SilentUpdatePolicy mSilentUpdatePolicy;
     /**
+     * The verification policy applied to this session, which might be different from the default
+     * verification policy used by the system.
+     */
+    private final AtomicInteger mVerificationPolicy;
+    /**
      * Note all calls must be done outside {@link #mLock} to prevent lock inversion.
      */
     private final StagingManager mStagingManager;
@@ -791,7 +805,8 @@
             if (errorMsg != null) {
                 Slog.e(TAG, "verifySession error: " + errorMsg);
                 setSessionFailed(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
-                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg);
+                onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR, errorMsg,
+                        /* extras= */ null);
                 return false;
             }
             return true;
@@ -1167,7 +1182,8 @@
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int sessionErrorCode,
             String sessionErrorMessage, DomainSet preVerifiedDomains,
-            @NonNull VerifierController verifierController) {
+            @NonNull VerifierController verifierController,
+            @PackageInstaller.VerificationPolicy int verificationPolicy) {
         mCallback = callback;
         mContext = context;
         mPm = pm;
@@ -1177,6 +1193,7 @@
         mHandler = new Handler(looper, mHandlerCallback);
         mStagingManager = stagingManager;
         mVerifierController = verifierController;
+        mVerificationPolicy = new AtomicInteger(verificationPolicy);
 
         this.sessionId = sessionId;
         this.userId = userId;
@@ -2580,10 +2597,10 @@
         dispatchSessionFinished(error, detailMessage, null);
     }
 
-    private void onSessionVerificationFailure(int error, String msg) {
+    private void onSessionVerificationFailure(int error, String msg, Bundle extras) {
         Slog.e(TAG, "Failed to verify session " + sessionId);
         // Dispatch message to remove session from PackageInstallerService.
-        dispatchSessionFinished(error, msg, null);
+        dispatchSessionFinished(error, msg, extras);
         maybeFinishChildSessions(error, msg);
     }
 
@@ -2856,7 +2873,7 @@
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
             setSessionFailed(e.error, errorMsg);
-            onSessionVerificationFailure(e.error, errorMsg);
+            onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
         }
         if (Flags.verificationService()) {
             final Supplier<Computer> snapshotSupplier = mPm::snapshotComputer;
@@ -2872,11 +2889,12 @@
                 // the installation can proceed.
                 if (!mVerifierController.startVerificationSession(snapshotSupplier, userId,
                         sessionId, getPackageName(), Uri.fromFile(stageDir), signingInfo,
-                        declaredLibraries, /* extensionParams= */ null,
+                        declaredLibraries, mVerificationPolicy.get(), /* extensionParams= */ null,
                         new VerifierCallback(), /* retry= */ false)) {
                     // A verifier is installed but cannot be connected. Installation disallowed.
                     onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "A verifier agent is available on device but cannot be connected.");
+                            "A verifier agent is available on device but cannot be connected.",
+                            /* extras= */ null);
                 }
             } else {
                 // Verifier is not installed. Let the installation pass for now.
@@ -2917,7 +2935,7 @@
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
             final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
             setSessionFailed(e.error, errorMsg);
-            onSessionVerificationFailure(e.error, errorMsg);
+            onSessionVerificationFailure(e.error, errorMsg, /* extras= */ null);
         }
     }
 
@@ -2926,24 +2944,57 @@
      */
     public class VerifierCallback {
         /**
+         * Called by the VerifierController when the verifier requests to get the current
+         * verification policy for this session.
+         */
+        public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+            return mVerificationPolicy.get();
+        }
+        /**
+         * Called by the VerifierController when the verifier requests to change the verification
+         * policy for this session.
+         */
+        public boolean setVerificationPolicy(@PackageInstaller.VerificationPolicy int policy) {
+            if (!isValidVerificationPolicy(policy)) {
+                return false;
+            }
+            mVerificationPolicy.set(policy);
+            return true;
+        }
+        /**
          * Called by the VerifierController when the connection has failed.
          */
         public void onConnectionFailed() {
-            mHandler.post(() -> {
-                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                            "A verifier agent is available on device but cannot be connected.");
-            });
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                                VERIFICATION_FAILED_REASON_UNKNOWN);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "A verifier agent is available on device but cannot be connected.",
+                                bundle);
+
+                    });
         }
         /**
          * Called by the VerifierController when the verification request has timed out.
          */
         public void onTimeout() {
-            mHandler.post(() -> {
-                mVerifierController.notifyVerificationTimeout(sessionId);
-                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                        "Verification timed out; missing a response from the verifier within the"
-                                + " time limit");
-            });
+            // Always notify the verifier, regardless of the policy.
+            mVerifierController.notifyVerificationTimeout(sessionId);
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                                VERIFICATION_FAILED_REASON_UNKNOWN);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                "Verification timed out; missing a response from the verifier"
+                                        + " within the time limit", bundle);
+                    });
         }
         /**
          * Called by the VerifierController when the verification request has received a complete
@@ -2953,17 +3004,22 @@
                 @Nullable PersistableBundle extensionResponse) {
             // TODO: handle extension response
             mHandler.post(() -> {
-                if (statusReceived.isVerified()) {
+                if (statusReceived.isVerified()
+                        || mVerificationPolicy.get() == VERIFICATION_POLICY_NONE) {
                     // Continue with the rest of the verification and installation.
                     resumeVerify();
-                } else {
-                    StringBuilder sb = new StringBuilder("Verifier rejected the installation");
-                    if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
-                        sb.append(" with message: ").append(statusReceived.getFailureMessage());
-                    }
-                    onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
-                            sb.toString());
+                    return;
                 }
+                // Package is blocked.
+                StringBuilder sb = new StringBuilder("Verifier rejected the installation");
+                if (!TextUtils.isEmpty(statusReceived.getFailureMessage())) {
+                    sb.append(" with message: ").append(statusReceived.getFailureMessage());
+                }
+                Bundle bundle = new Bundle();
+                bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON,
+                        VERIFICATION_FAILED_REASON_PACKAGE_BLOCKED);
+                onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                        sb.toString(), bundle);
             });
         }
         /**
@@ -2971,14 +3027,49 @@
          * response.
          */
         public void onVerificationIncompleteReceived(int incompleteReason) {
-            mHandler.post(() -> {
-                if (incompleteReason == VERIFICATION_INCOMPLETE_UNKNOWN) {
-                    // TODO: change this to a user confirmation and handle other incomplete reasons
-                    onSessionVerificationFailure(INSTALL_FAILED_INTERNAL_ERROR,
-                            "Verification cannot be completed for unknown reasons.");
-                }
-            });
+            // TODO(b/360129657): prompt user on fail warning
+            handleNonPackageBlockedFailure(
+                    /* onFailWarning= */ PackageInstallerSession.this::resumeVerify,
+                    /* onFailClosed= */ () -> {
+                        final int failureReason;
+                        StringBuilder sb = new StringBuilder(
+                                "Verification cannot be completed because of ");
+                        if (incompleteReason == VERIFICATION_INCOMPLETE_NETWORK_UNAVAILABLE) {
+                            failureReason = VERIFICATION_FAILED_REASON_NETWORK_UNAVAILABLE;
+                            sb.append("unavailable network.");
+                        } else {
+                            failureReason = VERIFICATION_FAILED_REASON_UNKNOWN;
+                            sb.append("unknown reasons.");
+                        }
+                        Bundle bundle = new Bundle();
+                        bundle.putInt(EXTRA_VERIFICATION_FAILURE_REASON, failureReason);
+                        onSessionVerificationFailure(INSTALL_FAILED_VERIFICATION_FAILURE,
+                                sb.toString(), bundle);
+                    });
         }
+
+        private void handleNonPackageBlockedFailure(Runnable onFailWarning, Runnable onFailClosed) {
+            final Runnable r = switch (mVerificationPolicy.get()) {
+                case VERIFICATION_POLICY_NONE, VERIFICATION_POLICY_BLOCK_FAIL_OPEN ->
+                        PackageInstallerSession.this::resumeVerify;
+                case VERIFICATION_POLICY_BLOCK_FAIL_WARN -> onFailWarning;
+                case VERIFICATION_POLICY_BLOCK_FAIL_CLOSED -> onFailClosed;
+                default -> {
+                    Log.wtf(TAG, "Unknown verification policy: " + mVerificationPolicy.get());
+                    yield onFailClosed;
+                }
+            };
+            mHandler.post(r);
+        }
+    }
+
+    /**
+     * Returns whether a policy is a valid verification policy.
+     */
+    public static boolean isValidVerificationPolicy(
+            @PackageInstaller.VerificationPolicy int policy) {
+        return policy >= VERIFICATION_POLICY_NONE
+                && policy <= VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
     }
 
     private IntentSender getRemoteStatusReceiver() {
@@ -3156,7 +3247,7 @@
                 if (error == INSTALL_SUCCEEDED) {
                     onVerificationComplete();
                 } else {
-                    onSessionVerificationFailure(error, msg);
+                    onSessionVerificationFailure(error, msg, /* extras= */ null);
                 }
             });
         });
@@ -5328,6 +5419,14 @@
         }
     }
 
+    /**
+     * @return the current policy for the verification request associated with this session.
+     */
+    @VisibleForTesting
+    public @PackageInstaller.VerificationPolicy int getVerificationPolicy() {
+        assertCallerIsOwnerOrRoot();
+        return mVerificationPolicy.get();
+    }
 
     void setSessionReady() {
         synchronized (mLock) {
@@ -5631,6 +5730,10 @@
             if (!ArrayUtils.isEmpty(warnings)) {
                 fillIn.putStringArrayListExtra(PackageInstaller.EXTRA_WARNINGS, warnings);
             }
+            if (extras.containsKey(EXTRA_VERIFICATION_FAILURE_REASON)) {
+                fillIn.putExtra(EXTRA_VERIFICATION_FAILURE_REASON,
+                        extras.getInt(EXTRA_VERIFICATION_FAILURE_REASON));
+            }
         }
         try {
             final BroadcastOptions options = BroadcastOptions.makeBasic();
@@ -5786,6 +5889,7 @@
             out.attributeInt(null, ATTR_INSTALL_REASON, params.installReason);
             writeBooleanAttribute(out, ATTR_APPLICATION_ENABLED_SETTING_PERSISTENT,
                     params.applicationEnabledSettingPersistent);
+            out.attributeInt(null, ATTR_VERIFICATION_POLICY, mVerificationPolicy.get());
 
             final boolean isDataLoader = params.dataLoaderParams != null;
             writeBooleanAttribute(out, ATTR_IS_DATALOADER, isDataLoader);
@@ -5936,6 +6040,8 @@
         final boolean sealed = in.getAttributeBoolean(null, ATTR_SEALED, false);
         final int parentSessionId = in.getAttributeInt(null, ATTR_PARENT_SESSION_ID,
                 SessionInfo.INVALID_ID);
+        final int verificationPolicy = in.getAttributeInt(null, ATTR_VERIFICATION_POLICY,
+                VERIFICATION_POLICY_NONE);
 
         final SessionParams params = new SessionParams(
                 SessionParams.MODE_INVALID);
@@ -6110,6 +6216,7 @@
                 installerUid, installSource, params, createdMillis, committedMillis, stageDir,
                 stageCid, fileArray, checksumsMap, prepared, committed, destroyed, sealed,
                 childSessionIdsArray, parentSessionId, isReady, isFailed, isApplied,
-                sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController);
+                sessionErrorCode, sessionErrorMessage, preVerifiedDomains, verifierController,
+                verificationPolicy);
     }
 }
diff --git a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
index 7eac940..b7cc7cce 100644
--- a/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
+++ b/services/core/java/com/android/server/pm/verify/pkg/VerifierController.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.SharedLibraryInfo;
@@ -94,9 +95,22 @@
     // Max duration allowed to wait for a verifier to respond to a verification request.
     private static final long DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS =
             TimeUnit.MINUTES.toMillis(10);
+    /**
+     * Configurable maximum amount of time in milliseconds for the system to wait from the moment
+     * when the installation session requires a verification, till when the request is delivered to
+     * the verifier, pending the connection to be established. If the request has not been delivered
+     * to the verifier within this amount of time, e.g., because the verifier has crashed or ANR'd,
+     * the controller then sends a failure status back to the installation session.
+     * Flag type: {@code long}
+     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
+     */
+    private static final String PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+            "verifier_connection_timeout_millis";
     // The maximum amount of time to wait from the moment when the session requires a verification,
     // till when the request is delivered to the verifier, pending the connection to be established.
-    private static final long CONNECTION_TIMEOUT_SECONDS = 10;
+    private static final long DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS =
+            TimeUnit.SECONDS.toMillis(10);
+
     // The maximum amount of time to wait before the system unbinds from the verifier.
     private static final long UNBIND_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(6);
 
@@ -271,6 +285,7 @@
             int installationSessionId, String packageName,
             Uri stagedPackageUri, SigningInfo signingInfo,
             List<SharedLibraryInfo> declaredLibraries,
+            @PackageInstaller.VerificationPolicy int verificationPolicy,
             PersistableBundle extensionParams, PackageInstallerSession.VerifierCallback callback,
             boolean retry) {
         // Try connecting to the verifier if not already connected
@@ -292,7 +307,7 @@
                 /* id= */ verificationId,
                 /* installSessionId= */ installationSessionId,
                 packageName, stagedPackageUri, signingInfo, declaredLibraries, extensionParams,
-                new VerificationSessionInterface(),
+                verificationPolicy, new VerificationSessionInterface(callback),
                 new VerificationSessionCallback(callback));
         AndroidFuture<Void> unusedFuture = mRemoteService.post(service -> {
             if (!retry) {
@@ -306,7 +321,8 @@
                 }
                 service.onVerificationRetry(session);
             }
-        }).orTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS).whenComplete((res, err) -> {
+        }).orTimeout(mInjector.getVerifierConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
+                .whenComplete((res, err) -> {
             if (err != null) {
                 Slog.e(TAG, "Error notifying verification request for session " + verificationId,
                         err);
@@ -407,6 +423,12 @@
 
     // This class handles requests from the remote verifier
     private class VerificationSessionInterface extends IVerificationSessionInterface.Stub {
+        private final PackageInstallerSession.VerifierCallback mCallback;
+
+        VerificationSessionInterface(PackageInstallerSession.VerifierCallback callback) {
+            mCallback = callback;
+        }
+
         @Override
         public long getTimeoutTime(int verificationId) {
             checkCallerPermission();
@@ -432,6 +454,20 @@
                 return tracker.extendTimeRemaining(additionalMs);
             }
         }
+
+        @Override
+        public boolean setVerificationPolicy(int verificationId,
+                @PackageInstaller.VerificationPolicy int policy) {
+            checkCallerPermission();
+            synchronized (mVerificationStatus) {
+                final VerificationStatusTracker tracker = mVerificationStatus.get(verificationId);
+                if (tracker == null) {
+                    throw new IllegalStateException("Verification session " + verificationId
+                            + " doesn't exist or has finished");
+                }
+            }
+            return mCallback.setVerificationPolicy(policy);
+        }
     }
 
     private class VerificationSessionCallback extends IVerificationSessionCallback.Stub {
@@ -451,8 +487,8 @@
                     throw new IllegalStateException("Verification session " + id
                             + " doesn't exist or has finished");
                 }
-                mCallback.onVerificationIncompleteReceived(reason);
             }
+            mCallback.onVerificationIncompleteReceived(reason);
             // Remove status tracking and stop the timeout countdown
             removeStatusTracker(id);
         }
@@ -630,6 +666,14 @@
             return getMaxVerificationExtendedTimeoutMillisFromDeviceConfig();
         }
 
+        /**
+         * This is added so that we can mock the maximum connection timeout duration without
+         * calling into DeviceConfig.
+         */
+        public long getVerifierConnectionTimeoutMillis() {
+            return getVerifierConnectionTimeoutMillisFromDeviceConfig();
+        }
+
         private static long getVerificationRequestTimeoutMillisFromDeviceConfig() {
             return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                     PROPERTY_VERIFICATION_REQUEST_TIMEOUT_MILLIS,
@@ -641,5 +685,11 @@
                     PROPERTY_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS,
                     DEFAULT_MAX_VERIFICATION_REQUEST_EXTENDED_TIMEOUT_MILLIS);
         }
+
+        private static long getVerifierConnectionTimeoutMillisFromDeviceConfig() {
+            return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
+                    PROPERTY_VERIFIER_CONNECTION_TIMEOUT_MILLIS,
+                    DEFAULT_VERIFIER_CONNECTION_TIMEOUT_MILLIS);
+        }
     }
 }
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 cbca434..8af5b20 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
@@ -21,6 +21,7 @@
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DEFAULT
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_DENIED
 import android.content.pm.PackageInstaller.SessionParams.PERMISSION_STATE_GRANTED
+import android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
 import android.content.pm.PackageManager
 import android.content.pm.verify.domain.DomainSet
 import android.os.Parcel
@@ -33,6 +34,11 @@
 import com.android.server.pm.verify.pkg.VerifierController
 import com.android.server.testutils.whenever
 import com.google.common.truth.Truth.assertThat
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
 import libcore.io.IoUtils
 import org.junit.Before
 import org.junit.Rule
@@ -46,11 +52,6 @@
 import org.mockito.MockitoAnnotations
 import org.xmlpull.v1.XmlPullParser
 import org.xmlpull.v1.XmlPullParserException
-import java.io.File
-import java.io.FileInputStream
-import java.io.FileNotFoundException
-import java.io.FileOutputStream
-import java.io.IOException
 
 @Presubmit
 class PackageInstallerSessionTest {
@@ -197,7 +198,8 @@
             /* stagedSessionErrorCode */ PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
             /* stagedSessionErrorMessage */ "some error",
             /* preVerifiedDomains */ DomainSet(setOf("com.foo", "com.bar")),
-            /* VerifierController */ mock(VerifierController::class.java)
+            /* VerifierController */ mock(VerifierController::class.java),
+            VERIFICATION_POLICY_BLOCK_FAIL_CLOSED
         )
     }
 
@@ -339,6 +341,7 @@
         assertThat(expected.childSessionIds).asList()
             .containsExactlyElementsIn(actual.childSessionIds.toList())
         assertThat(expected.preVerifiedDomains).isEqualTo(actual.preVerifiedDomains)
+        assertThat(expected.verificationPolicy).isEqualTo(actual.verificationPolicy)
     }
 
     private fun assertInstallSourcesEquivalent(expected: InstallSource, actual: InstallSource) {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
index be094b0..37b23b1 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/verify/pkg/VerifierControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.server.pm.verify.pkg;
 
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
+import static android.content.pm.PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -92,6 +95,9 @@
     private static final long TEST_TIMEOUT_DURATION_MILLIS = TimeUnit.MINUTES.toMillis(1);
     private static final long TEST_MAX_TIMEOUT_DURATION_MILLIS =
             TimeUnit.MINUTES.toMillis(10);
+    private static final long TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS =
+            TimeUnit.SECONDS.toMillis(10);
+    private static final int TEST_POLICY = VERIFICATION_POLICY_BLOCK_FAIL_CLOSED;
 
     private final ArrayList<SharedLibraryInfo> mTestDeclaredLibraries = new ArrayList<>();
     private final PersistableBundle mTestExtensionParams = new PersistableBundle();
@@ -124,6 +130,9 @@
                 TEST_TIMEOUT_DURATION_MILLIS);
         when(mInjector.getMaxVerificationExtendedTimeoutMillis()).thenReturn(
                 TEST_MAX_TIMEOUT_DURATION_MILLIS);
+        when(mInjector.getVerifierConnectionTimeoutMillis()).thenReturn(
+                TEST_VERIFIER_CONNECTION_TIMEOUT_DURATION_MILLIS
+        );
         // Mock time forward as the code continues to check for the current time
         when(mInjector.getCurrentTimeMillis())
                 .thenReturn(TEST_REQUEST_START_TIME)
@@ -159,12 +168,12 @@
                 .isFalse();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isFalse();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isFalse();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isFalse();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isFalse();
         verifyZeroInteractions(mSessionCallback);
     }
 
@@ -200,8 +209,8 @@
         ServiceConnector.ServiceLifecycleCallbacks<IVerifierService> callbacks = captor.getValue();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService, times(1)).onVerificationRequired(any(VerificationSession.class));
         callbacks.onBinderDied();
         // Test that nothing crashes if the service connection is lost
@@ -212,12 +221,12 @@
         verifyNoMoreInteractions(mMockService);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         mVerifierController.notifyVerificationTimeout(TEST_ID);
         verify(mMockService, times(1)).onVerificationTimeout(eq(TEST_ID));
     }
@@ -226,8 +235,8 @@
     public void testNotifyPackageNameAvailable() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         mVerifierController.notifyPackageNameAvailable(TEST_PACKAGE_NAME);
         verify(mMockService).onPackageNameAvailable(eq(TEST_PACKAGE_NAME));
     }
@@ -236,8 +245,8 @@
     public void testNotifyVerificationCancelled() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         mVerifierController.notifyVerificationCancelled(TEST_PACKAGE_NAME);
         verify(mMockService).onVerificationCancelled(eq(TEST_PACKAGE_NAME));
     }
@@ -248,8 +257,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -276,8 +285,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         verify(mMockService).onVerificationRetry(captor.capture());
         VerificationSession session = captor.getValue();
         assertThat(session.getId()).isEqualTo(TEST_ID);
@@ -302,8 +311,8 @@
     public void testNotifyVerificationTimeout() throws Exception {
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         mVerifierController.notifyVerificationTimeout(TEST_ID);
         verify(mMockService).onVerificationTimeout(eq(TEST_ID));
     }
@@ -320,8 +329,8 @@
         ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mSessionCallback, times(1)).onTimeout();
         verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -339,8 +348,8 @@
                 .thenAnswer(i -> true);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mSessionCallback, times(1)).onTimeout();
         verify(mInjector, times(2)).getCurrentTimeMillis();
@@ -350,8 +359,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ true)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ true)).isTrue();
         verify(mMockService).onVerificationRetry(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -367,8 +376,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         session.reportVerificationIncomplete(VerificationSession.VERIFICATION_INCOMPLETE_UNKNOWN);
@@ -383,8 +392,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -401,8 +410,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder()
@@ -421,8 +430,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         assertThat(mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false)).isTrue();
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false)).isTrue();
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         VerificationStatus status = new VerificationStatus.Builder().setVerified(true).build();
@@ -439,8 +448,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -456,8 +465,8 @@
                 ArgumentCaptor.forClass(VerificationSession.class);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mMockService).onVerificationRequired(captor.capture());
         VerificationSession session = captor.getValue();
         final long initialTimeoutTime = TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS;
@@ -493,10 +502,27 @@
                 .thenReturn(TEST_REQUEST_START_TIME + TEST_TIMEOUT_DURATION_MILLIS + 1);
         mVerifierController.startVerificationSession(
                 mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
-                TEST_SIGNING_INFO, mTestDeclaredLibraries, mTestExtensionParams, mSessionCallback,
-                /* retry= */ false);
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
         verify(mHandler, times(3)).sendMessageAtTime(any(Message.class), anyLong());
         verify(mInjector, times(6)).getCurrentTimeMillis();
         verify(mSessionCallback, times(1)).onTimeout();
     }
+
+    @Test
+    public void testPolicyOverride() throws Exception {
+        ArgumentCaptor<VerificationSession> captor =
+                ArgumentCaptor.forClass(VerificationSession.class);
+        mVerifierController.startVerificationSession(
+                mSnapshotSupplier, 0, TEST_ID, TEST_PACKAGE_NAME, TEST_PACKAGE_URI,
+                TEST_SIGNING_INFO, mTestDeclaredLibraries, TEST_POLICY, mTestExtensionParams,
+                mSessionCallback, /* retry= */ false);
+        verify(mMockService).onVerificationRequired(captor.capture());
+        VerificationSession session = captor.getValue();
+        final int policy = VERIFICATION_POLICY_BLOCK_FAIL_OPEN;
+        when(mSessionCallback.setVerificationPolicy(eq(policy))).thenReturn(true);
+        assertThat(session.setVerificationPolicy(policy)).isTrue();
+        assertThat(session.getVerificationPolicy()).isEqualTo(policy);
+        verify(mSessionCallback, times(1)).setVerificationPolicy(eq(policy));
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
index 43a8aa9..124c41e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/StagingManagerTest.java
@@ -741,7 +741,8 @@
                 /* stagedSessionErrorCode */ PackageManager.INSTALL_UNKNOWN,
                 /* stagedSessionErrorMessage */ "no error",
                 /* preVerifiedDomains */ null,
-                /* verifierController */ null);
+                /* verifierController */ null,
+                /* verificationPolicy */ PackageInstaller.VERIFICATION_POLICY_BLOCK_FAIL_CLOSED);
 
         StagingManager.StagedSession stagedSession = spy(session.mStagedSession);
         doReturn(packageName).when(stagedSession).getPackageName();