Merge "Fix TaskView cropping for transient taskbar" into udc-dev
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cd96a1e..765a17f 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -925,7 +925,7 @@
 
   public class KeyguardManager {
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.StartLockscreenValidationRequest, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.RemoteLockscreenValidationSession, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence);
     method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed();
@@ -937,7 +937,7 @@
     method @RequiresPermission(android.Manifest.permission.SHOW_KEYGUARD_MESSAGE) public void requestDismissKeyguard(@NonNull android.app.Activity, @Nullable CharSequence, @Nullable android.app.KeyguardManager.KeyguardDismissCallback);
     method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public boolean setLock(int, @NonNull byte[], int);
     method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public void setPrivateNotificationsAllowed(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.StartLockscreenValidationRequest startRemoteLockscreenValidation();
+    method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationSession startRemoteLockscreenValidation();
     method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public boolean unregisterWeakEscrowTokenRemovedListener(@NonNull android.app.KeyguardManager.WeakEscrowTokenRemovedListener);
     method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.app.RemoteLockscreenValidationResult validateRemoteLockscreen(@NonNull byte[]);
     field public static final int PASSWORD = 0; // 0x0
@@ -1026,6 +1026,7 @@
     field public static final int RESULT_GUESS_VALID = 1; // 0x1
     field public static final int RESULT_LOCKOUT = 3; // 0x3
     field public static final int RESULT_NO_REMAINING_ATTEMPTS = 4; // 0x4
+    field public static final int RESULT_SESSION_EXPIRED = 5; // 0x5
   }
 
   public static final class RemoteLockscreenValidationResult.Builder {
@@ -1035,6 +1036,23 @@
     method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setTimeoutMillis(long);
   }
 
+  public final class RemoteLockscreenValidationSession implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLockType();
+    method public int getRemainingAttempts();
+    method @NonNull public byte[] getSourcePublicKey();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.RemoteLockscreenValidationSession> CREATOR;
+  }
+
+  public static final class RemoteLockscreenValidationSession.Builder {
+    ctor public RemoteLockscreenValidationSession.Builder();
+    method @NonNull public android.app.RemoteLockscreenValidationSession build();
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setLockType(int);
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setRemainingAttempts(int);
+    method @NonNull public android.app.RemoteLockscreenValidationSession.Builder setSourcePublicKey(@NonNull byte[]);
+  }
+
   public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
     ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
     method public int describeContents();
@@ -1052,23 +1070,6 @@
     method public void launchAssist(@Nullable android.os.Bundle);
   }
 
-  public final class StartLockscreenValidationRequest implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getLockscreenUiType();
-    method public int getRemainingAttempts();
-    method @NonNull public byte[] getSourcePublicKey();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.StartLockscreenValidationRequest> CREATOR;
-  }
-
-  public static final class StartLockscreenValidationRequest.Builder {
-    ctor public StartLockscreenValidationRequest.Builder();
-    method @NonNull public android.app.StartLockscreenValidationRequest build();
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setLockscreenUiType(int);
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setRemainingAttempts(int);
-    method @NonNull public android.app.StartLockscreenValidationRequest.Builder setSourcePublicKey(@NonNull byte[]);
-  }
-
   public class StatusBarManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarMode();
@@ -14615,7 +14616,6 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getCompleteActiveSubscriptionIdList();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
     method @NonNull public static android.content.res.Resources getResourcesForSubId(@NonNull android.content.Context, int);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public android.os.UserHandle getSubscriptionUserHandle(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
     method public void requestEmbeddedSubscriptionInfoListRefresh();
     method public void requestEmbeddedSubscriptionInfoListRefresh(int);
@@ -14625,7 +14625,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPreferredDataSubscriptionId(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION) public void setSubscriptionUserHandle(int, @Nullable android.os.UserHandle);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUiccApplicationsEnabled(int, boolean);
     field @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED";
     field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index be0d1c9..0ef8e92 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -146,13 +146,13 @@
     public static final String EXTRA_CHECKBOX_LABEL = "android.app.extra.CHECKBOX_LABEL";
 
     /**
-     * A {@link StartLockscreenValidationRequest} extra to be sent along with
+     * A {@link RemoteLockscreenValidationSession} extra to be sent along with
      * {@link #ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL} containing the data needed to prompt for
      * a remote device's lock screen.
      * @hide
      */
-    public static final String EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST =
-            "android.app.extra.START_LOCKSCREEN_VALIDATION_REQUEST";
+    public static final String EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION =
+            "android.app.extra.REMOTE_LOCKSCREEN_VALIDATION_SESSION";
 
     /**
      * Result code returned by the activity started by
@@ -359,8 +359,7 @@
     /**
      * Get an Intent to launch an activity to prompt the user to confirm the
      * credentials (pin, pattern or password) of a remote device.
-     * @param startLockscreenValidationRequest contains information necessary to start remote device
-     *                                         credential validation.
+     * @param session contains information necessary to start remote device credential validation.
      * @param remoteLockscreenValidationServiceComponent
      *          the {@link ComponentName} of the implementation of
      *          {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService}
@@ -376,15 +375,14 @@
     @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
     @NonNull
     public Intent createConfirmDeviceCredentialForRemoteValidationIntent(
-            @NonNull StartLockscreenValidationRequest startLockscreenValidationRequest,
+            @NonNull RemoteLockscreenValidationSession session,
             @NonNull ComponentName remoteLockscreenValidationServiceComponent,
             @Nullable CharSequence title,
             @Nullable CharSequence description,
             @Nullable CharSequence checkboxLabel,
             @Nullable CharSequence alternateButtonLabel) {
         Intent intent = new Intent(ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL)
-                .putExtra(
-                        EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST, startLockscreenValidationRequest)
+                .putExtra(EXTRA_REMOTE_LOCKSCREEN_VALIDATION_SESSION, session)
                 .putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent)
                 .putExtra(EXTRA_TITLE, title)
                 .putExtra(EXTRA_DESCRIPTION, description)
@@ -1157,7 +1155,7 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         return mLockPatternUtils.startRemoteLockscreenValidation();
     }
 
@@ -1165,11 +1163,10 @@
      * Verifies credentials guess from a remote device.
      *
      * <p>Secret must be encrypted using {@code SecureBox} library
-     * with public key from {@code StartLockscreenValidationRequest}
+     * with public key from {@code RemoteLockscreenValidationSession}
      * and header set to {@code "encrypted_remote_credentials"} in UTF-8 encoding.
      *
-     * @throws IllegalStateException if there is no active lock screen validation session or
-     * there was a decryption error.
+     * @throws IllegalStateException if there was a decryption error.
      *
      * @hide
      */
diff --git a/core/java/android/app/RemoteLockscreenValidationResult.java b/core/java/android/app/RemoteLockscreenValidationResult.java
index 0245f8c..bbb3567 100644
--- a/core/java/android/app/RemoteLockscreenValidationResult.java
+++ b/core/java/android/app/RemoteLockscreenValidationResult.java
@@ -55,10 +55,16 @@
      */
     public static final int RESULT_NO_REMAINING_ATTEMPTS = 4;
 
+    /**
+     * New lockscreen validation session is required to verify guess.
+     */
+    public static final int RESULT_SESSION_EXPIRED = 5;
+
     @IntDef({RESULT_GUESS_VALID,
             RESULT_GUESS_INVALID,
             RESULT_LOCKOUT,
-            RESULT_NO_REMAINING_ATTEMPTS})
+            RESULT_NO_REMAINING_ATTEMPTS,
+            RESULT_SESSION_EXPIRED})
     @Retention(RetentionPolicy.SOURCE)
     @interface ResultCode {}
 
diff --git a/core/java/android/app/StartLockscreenValidationRequest.aidl b/core/java/android/app/RemoteLockscreenValidationSession.aidl
similarity index 93%
rename from core/java/android/app/StartLockscreenValidationRequest.aidl
rename to core/java/android/app/RemoteLockscreenValidationSession.aidl
index 367dfee..edc8d56 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.aidl
+++ b/core/java/android/app/RemoteLockscreenValidationSession.aidl
@@ -17,4 +17,4 @@
 package android.app;
 
 /** {@hide} */
-parcelable StartLockscreenValidationRequest;
+parcelable RemoteLockscreenValidationSession;
diff --git a/core/java/android/app/StartLockscreenValidationRequest.java b/core/java/android/app/RemoteLockscreenValidationSession.java
similarity index 69%
rename from core/java/android/app/StartLockscreenValidationRequest.java
rename to core/java/android/app/RemoteLockscreenValidationSession.java
index e818195..c6592e3 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.java
+++ b/core/java/android/app/RemoteLockscreenValidationSession.java
@@ -30,44 +30,45 @@
  * @hide
  */
 @SystemApi
-public final class StartLockscreenValidationRequest implements Parcelable {
+public final class RemoteLockscreenValidationSession implements Parcelable {
 
     @LockTypes
-    private int mLockscreenUiType;
+    private int mLockType;
 
     private byte[] mSourcePublicKey;
 
     private int mRemainingAttempts;
 
-    public static final @NonNull Parcelable.Creator<StartLockscreenValidationRequest> CREATOR = new
-            Parcelable.Creator<StartLockscreenValidationRequest>() {
+    public static final @NonNull Parcelable.Creator<RemoteLockscreenValidationSession> CREATOR = new
+            Parcelable.Creator<RemoteLockscreenValidationSession>() {
         @Override
-        public StartLockscreenValidationRequest createFromParcel(Parcel source) {
-            return new StartLockscreenValidationRequest(source);
+        public RemoteLockscreenValidationSession createFromParcel(Parcel source) {
+            return new RemoteLockscreenValidationSession(source);
         }
 
         @Override
-        public StartLockscreenValidationRequest[] newArray(int size) {
-            return new StartLockscreenValidationRequest[size];
+        public RemoteLockscreenValidationSession[] newArray(int size) {
+            return new RemoteLockscreenValidationSession[size];
         }
     };
 
 
     /**
-     * Builder for {@code StartLockscreenValidationRequest}
+     * Builder for {@code RemoteLockscreenValidationSession}
      */
     public static final class Builder {
-        private StartLockscreenValidationRequest mInstance = new StartLockscreenValidationRequest();
+        private RemoteLockscreenValidationSession mInstance =
+                new RemoteLockscreenValidationSession();
 
         /**
          * Sets UI type.
          * Default value is {@code LockTypes.PASSWORD}
          *
-         * @param lockscreenUiType The UI format
+         * @param lockType The UI format
          * @return This builder.
          */
-        public @NonNull Builder setLockscreenUiType(@LockTypes int lockscreenUiType) {
-            mInstance.mLockscreenUiType = lockscreenUiType;
+        public @NonNull Builder setLockType(@LockTypes int lockType) {
+            mInstance.mLockType = lockType;
             return this;
         }
 
@@ -92,11 +93,11 @@
         }
 
         /**
-         * Creates {@code StartLockscreenValidationRequest}
+         * Creates {@code RemoteLockscreenValidationSession}
          *
          * @throws NullPointerException if required fields are not set.
          */
-        public @NonNull StartLockscreenValidationRequest build() {
+        public @NonNull RemoteLockscreenValidationSession build() {
             Objects.requireNonNull(mInstance.mSourcePublicKey);
             return mInstance;
         }
@@ -105,8 +106,8 @@
     /**
      * Specifies lock screen credential type.
      */
-    public @LockTypes int getLockscreenUiType() {
-        return mLockscreenUiType;
+    public @LockTypes int getLockType() {
+        return mLockType;
     }
 
     /**
@@ -127,16 +128,16 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeInt(mLockscreenUiType);
+        out.writeInt(mLockType);
         out.writeByteArray(mSourcePublicKey);
         out.writeInt(mRemainingAttempts);
     }
 
-    private StartLockscreenValidationRequest() {
+    private RemoteLockscreenValidationSession() {
     }
 
-    private StartLockscreenValidationRequest(Parcel in) {
-        mLockscreenUiType = in.readInt();
+    private RemoteLockscreenValidationSession(Parcel in) {
+        mLockType = in.readInt();
         mSourcePublicKey = in.createByteArray();
         mRemainingAttempts = in.readInt();
     }
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 4532661..4b3eb3a 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -1392,10 +1392,6 @@
         return sql.replaceAll("[\\s]*\\n+[\\s]*", " ");
     }
 
-    void clearPreparedStatementCache() {
-        mPreparedStatementCache.evictAll();
-    }
-
     /**
      * Holder type for a prepared statement.
      *
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 6023d66..069c264 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1126,16 +1126,6 @@
         mConnectionWaiterPool = waiter;
     }
 
-    void clearAcquiredConnectionsPreparedStatementCache() {
-        synchronized (mLock) {
-            if (!mAcquiredConnections.isEmpty()) {
-                for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
-                    connection.clearPreparedStatementCache();
-                }
-            }
-        }
-    }
-
     /**
      * Dumps debugging information about this connection pool.
      *
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index db898c3..c08294f 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -2088,12 +2088,10 @@
             try (SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs)) {
                 return statement.executeUpdateDelete();
             } finally {
-                // If schema was updated, close non-primary connections and clear prepared
-                // statement caches of active connections, otherwise they might have outdated
-                // schema information.
+                // If schema was updated, close non-primary connections, otherwise they might
+                // have outdated schema information
                 if (statementType == DatabaseUtils.STATEMENT_DDL) {
                     mConnectionPoolLocked.closeAvailableNonPrimaryConnectionsAndLogExceptions();
-                    mConnectionPoolLocked.clearAcquiredConnectionsPreparedStatementCache();
                 }
             }
         } finally {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index fbc0184..6cd4a2e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1189,8 +1189,8 @@
             PackageManager packageManager = context.getPackageManager();
 
             try {
-                return packageManager.getProperty(context.getOpPackageName(),
-                            PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT).getBoolean();
+                return packageManager.getProperty(PROPERTY_COMPAT_OVERRIDE_LANDSCAPE_TO_PORTRAIT,
+                        context.getOpPackageName()).getBoolean();
             } catch (PackageManager.NameNotFoundException e) {
                 // No such property
             }
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index 9cd5aa0..3190c69 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -328,7 +328,7 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         final List<CredentialProviderInfo> providerInfos = new ArrayList<>();
@@ -342,7 +342,7 @@
                                 si,
                                 /* isSystemProvider= */ true,
                                 disableSystemAppVerificationForTests,
-                                enabledServices.contains(si));
+                                enabledServices.contains(si.getComponentName()));
                 if (cpi.isSystemProvider()) {
                     providerInfos.add(cpi);
                 } else {
@@ -371,31 +371,6 @@
     }
 
     /**
-     * Returns a valid credential provider that has the given package name. Returns null if no
-     * match is found.
-     */
-    @Nullable
-    public static CredentialProviderInfo getCredentialProviderFromPackageName(
-            @NonNull Context context,
-            int userId,
-            @NonNull String packageName,
-            int providerFilter,
-            @NonNull Set<ServiceInfo> enabledServices) {
-        requireNonNull(context, "context must not be null");
-        requireNonNull(packageName, "package name must not be null");
-        requireNonNull(enabledServices, "enabledServices must not be null");
-
-        for (CredentialProviderInfo credentialProviderInfo : getCredentialProviderServices(context,
-                userId, providerFilter, enabledServices)) {
-            if (credentialProviderInfo.getServiceInfo()
-                    .packageName.equals(packageName)) {
-                return credentialProviderInfo;
-            }
-        }
-        return null;
-    }
-
-    /**
      * Returns the valid credential provider services available for the user with the given {@code
      * userId}.
      */
@@ -404,7 +379,7 @@
             @NonNull Context context,
             int userId,
             int providerFilter,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         // Get the device policy.
@@ -433,7 +408,7 @@
             @NonNull Context context,
             int userId,
             int providerFilter,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         requireNonNull(context, "context must not be null");
 
         // Get the device policy.
@@ -539,7 +514,7 @@
             @NonNull Context context,
             @UserIdInt int userId,
             boolean disableSystemAppVerificationForTests,
-            Set<ServiceInfo> enabledServices) {
+            Set<ComponentName> enabledServices) {
         final List<CredentialProviderInfo> services = new ArrayList<>();
         final List<ResolveInfo> resolveInfos =
                 context.getPackageManager()
@@ -549,6 +524,11 @@
                                 userId);
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+            if (serviceInfo == null) {
+                Log.i(TAG, "No serviceInfo found for resolveInfo so skipping this provider");
+                continue;
+            }
+
             try {
                 CredentialProviderInfo cpi =
                         CredentialProviderInfoFactory.create(
@@ -556,7 +536,7 @@
                                 serviceInfo,
                                 /* isSystemProvider= */ false,
                                 disableSystemAppVerificationForTests,
-                                enabledServices.contains(serviceInfo));
+                                enabledServices.contains(serviceInfo.getComponentName()));
                 if (!cpi.isSystemProvider()) {
                     services.add(cpi);
                 }
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index a2fa139..a389223 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -58,11 +58,13 @@
             setTitle(title);
         }
 
-        final Bundle extras = getIntent().getExtras();
-        mCallback = (DreamService.DreamActivityCallbacks) extras.getBinder(EXTRA_CALLBACK);
-
-        if (mCallback != null) {
+        final Object callback = getIntent().getExtras().getBinder(EXTRA_CALLBACK);
+        if (callback instanceof DreamService.DreamActivityCallbacks) {
+            mCallback = (DreamService.DreamActivityCallbacks) callback;
             mCallback.onActivityCreated(this);
+        } else {
+            mCallback = null;
+            finishAndRemoveTask();
         }
     }
 
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index a646df3..a781454 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -18,7 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.trust.IStrongAuthTracker;
 import android.os.Bundle;
 import android.security.keystore.recovery.WrappedApplicationKey;
@@ -95,7 +95,7 @@
             in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
-    StartLockscreenValidationRequest startRemoteLockscreenValidation();
+    RemoteLockscreenValidationSession startRemoteLockscreenValidation();
     RemoteLockscreenValidationResult validateRemoteLockscreen(in byte[] encryptedCredential);
     boolean hasSecureLockScreen();
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b86020e..4d91410 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -29,7 +29,7 @@
 import android.annotation.UserIdInt;
 import android.app.PropertyInvalidatedCache;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
@@ -1875,7 +1875,7 @@
      * Starts a session to verify lockscreen credentials provided by a remote device.
      */
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         try {
             return getLockSettings().startRemoteLockscreenValidation();
         } catch (RemoteException e) {
diff --git a/data/keyboards/Vendor_004c_Product_0265.idc b/data/keyboards/Vendor_004c_Product_0265.idc
deleted file mode 120000
index 707dfcf..0000000
--- a/data/keyboards/Vendor_004c_Product_0265.idc
+++ /dev/null
@@ -1 +0,0 @@
-Vendor_05ac_Product_0265.idc
\ No newline at end of file
diff --git a/data/keyboards/Vendor_03f6_Product_a001.idc b/data/keyboards/Vendor_03f6_Product_a001.idc
deleted file mode 100644
index bcb4ee3..0000000
--- a/data/keyboards/Vendor_03f6_Product_a001.idc
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Brydge Touchpad
-#
-
-# Reports from this touchpad sometimes get bunched together due to Bluetooth
-# batching, leading to bad timestamps that mess up finger velocity calculations.
-# To fix this, set a fake delta using the touchpad's known report rate.
-gestureProp.Fake_Timestamp_Delta = 0.010
diff --git a/data/keyboards/Vendor_046d_Product_4011.idc b/data/keyboards/Vendor_046d_Product_4011.idc
deleted file mode 100644
index 3a23830..0000000
--- a/data/keyboards/Vendor_046d_Product_4011.idc
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Logitech Wireless Touchpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -313.240741792594
-gestureProp.Pressure_Calibration_Slope = 4.39678062436752
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Palm_Pressure = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4101.idc b/data/keyboards/Vendor_046d_Product_4101.idc
deleted file mode 100644
index 47e2530..0000000
--- a/data/keyboards/Vendor_046d_Product_4101.idc
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Logitech T650
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -0.439288351750068
-gestureProp.Pressure_Calibration_Slope = 3.05998553523335
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_046d_Product_4102.idc b/data/keyboards/Vendor_046d_Product_4102.idc
deleted file mode 100644
index e33a28a..0000000
--- a/data/keyboards/Vendor_046d_Product_4102.idc
+++ /dev/null
@@ -1,24 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Logitech TK820
-#
-
-gestureProp.Touchpad_Stack_Version = 2
-# Pressure jumps around a lot on this touchpad, so allow that:
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Pressure_Calibration_Offset = -18.8078435
-gestureProp.Pressure_Calibration_Slope = 2.466208137
diff --git a/data/keyboards/Vendor_046d_Product_b00c.idc b/data/keyboards/Vendor_046d_Product_b00c.idc
deleted file mode 100644
index a49970c..0000000
--- a/data/keyboards/Vendor_046d_Product_b00c.idc
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Logitech T651
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-gestureProp.Pressure_Calibration_Offset = -4.46520447177073
-gestureProp.Pressure_Calibration_Slope = 3.21071719332644
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/data/keyboards/Vendor_05ac_Product_0265.idc b/data/keyboards/Vendor_05ac_Product_0265.idc
deleted file mode 100644
index d72de64..0000000
--- a/data/keyboards/Vendor_05ac_Product_0265.idc
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Apple Magic Trackpad 2 configuration file
-#   Bluetooth vendor ID 004c
-#   USB vendor ID 05ac
-#
-
-gestureProp.Pressure_Calibration_Offset = 30
-gestureProp.Palm_Pressure = 250.0
-gestureProp.Palm_Width = 20.0
-gestureProp.Multiple_Palm_Width = 20.0
-
-# Enable Stationary Wiggle Filter
-gestureProp.Stationary_Wiggle_Filter_Enabled = 1
-gestureProp.Finger_Moving_Energy = 0.0008
-gestureProp.Finger_Moving_Hysteresis = 0.0004
-
-# Avoid accidental scroll/move on finger lift
-gestureProp.Max_Stationary_Move_Speed = 47
-gestureProp.Max_Stationary_Move_Speed_Hysteresis = 1
-gestureProp.Max_Stationary_Move_Suppress_Distance = 0.2
diff --git a/data/keyboards/Vendor_05ac_Product_030e.idc b/data/keyboards/Vendor_05ac_Product_030e.idc
deleted file mode 100644
index 23a2e18..0000000
--- a/data/keyboards/Vendor_05ac_Product_030e.idc
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-#
-# Apple Magic Trackpad
-#
-
-gestureProp.Touchpad_Stack_Version = 1
-# We are using raw touch major value as pressure value, so set the Palm
-# pressure threshold high.
-gestureProp.Palm_Pressure = 1000
-gestureProp.Compute_Surface_Area_from_Pressure = 0
-gestureProp.IIR_b0 = 1
-gestureProp.IIR_b1 = 0
-gestureProp.IIR_b2 = 0
-gestureProp.IIR_b3 = 0
-gestureProp.IIR_a1 = 0
-gestureProp.IIR_a2 = 0
-# NOTE: bias on X-axis is uncalibrated
-gestureProp.Touchpad_Device_Output_Bias_on_X-Axis = -283.3226025266607
-gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis = -283.3226025266607
-gestureProp.Max_Allowed_Pressure_Change_Per_Sec = 100000.0
-gestureProp.Max_Hysteresis_Pressure_Per_Sec = 100000.0
-# Drumroll suppression causes janky movement on this touchpad.
-gestureProp.Drumroll_Suppression_Enable = 0
-gestureProp.Two_Finger_Vertical_Close_Distance_Thresh = 35.0
-gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls = 0
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
index 68ff806..65955b1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.graphics.Rect;
+import android.hardware.devicestate.DeviceStateManager;
 import android.util.Log;
 
 import androidx.annotation.NonNull;
@@ -33,7 +34,8 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-/** A representation of a folding feature for both Extension and Sidecar.
+/**
+ * A representation of a folding feature for both Extension and Sidecar.
  * For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
  * {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
  * {@link androidx.window.extensions.layout.FoldingFeature}.
@@ -67,10 +69,11 @@
     public static final int COMMON_STATE_UNKNOWN = -1;
 
     /**
-     * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
-     * and Extensions do not match exactly.
+     * A common state that contains no folding features. For example, an in-folding device in the
+     * "closed" device state.
      */
-    public static final int COMMON_STATE_FLAT = 3;
+    public static final int COMMON_STATE_NO_FOLDING_FEATURES = 1;
+
     /**
      * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
      * Sidecar and Extensions do not match exactly.
@@ -78,9 +81,27 @@
     public static final int COMMON_STATE_HALF_OPENED = 2;
 
     /**
-     * The possible states for a folding hinge.
+     * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
+     * and Extensions do not match exactly.
      */
-    @IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
+    public static final int COMMON_STATE_FLAT = 3;
+
+    /**
+     * A common state where the hinge state should be derived using the base state from
+     * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)} instead of the
+     * emulated state. This is an internal state and must not be passed to clients.
+     */
+    public static final int COMMON_STATE_USE_BASE_STATE = 1000;
+
+    /**
+     * The possible states for a folding hinge. Common in this context means normalized between
+     * extensions and sidecar.
+     */
+    @IntDef({COMMON_STATE_UNKNOWN,
+            COMMON_STATE_NO_FOLDING_FEATURES,
+            COMMON_STATE_HALF_OPENED,
+            COMMON_STATE_FLAT,
+            COMMON_STATE_USE_BASE_STATE})
     @Retention(RetentionPolicy.SOURCE)
     public @interface State {
     }
@@ -167,7 +188,7 @@
             }
             String stateString = featureMatcher.group(6);
             stateString = stateString == null ? "" : stateString;
-            final int state;
+            @State final int state;
             switch (stateString) {
                 case PATTERN_STATE_FLAT:
                     state = COMMON_STATE_FLAT;
@@ -191,8 +212,8 @@
     @NonNull
     private final Rect mRect;
 
-    CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
-        assertValidState(state);
+    CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) {
+        assertReportableState(state);
         this.mType = type;
         this.mState = state;
         if (rect.width() == 0 && rect.height() == 0) {
@@ -231,13 +252,22 @@
     }
 
     @Override
+    public String toString() {
+        return "CommonFoldingFeature=[Type: " + mType + ", state: " + mState + "]";
+    }
+
+    @Override
     public int hashCode() {
         return Objects.hash(mType, mState, mRect);
     }
 
-    private static void assertValidState(@Nullable Integer state) {
-        if (state != null && state != COMMON_STATE_FLAT
-                && state != COMMON_STATE_HALF_OPENED && state != COMMON_STATE_UNKNOWN) {
+    /**
+     * Checks if the provided folding feature state should be reported to clients. See
+     * {@link androidx.window.extensions.layout.FoldingFeature}
+     */
+    private static void assertReportableState(@State int state) {
+        if (state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED
+                && state != COMMON_STATE_UNKNOWN) {
             throw new IllegalArgumentException("Invalid state: " + state
                     + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
         }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 0bdf98c..66f27f5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -19,6 +19,7 @@
 import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
 
 import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
+import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
 import static androidx.window.common.CommonFoldingFeature.parseListFromString;
 
 import android.annotation.NonNull;
@@ -52,13 +53,54 @@
             DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
     private static final boolean DEBUG = false;
 
+    /**
+     * Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
+     * {@link CommonFoldingFeature.State} map.
+     */
     private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
 
+    /**
+     * Emulated device state received via
+     * {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
+     * "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
+     * physical device states. They represent the state of the device when various software
+     * features and APIs are applied. The emulated states generally consist of all "base" states,
+     * but may have additional states such as "concurrent" or "rear display". Concurrent mode for
+     * example is activated via public API and can be active in both the "open" and "half folded"
+     * device states.
+     */
     private int mCurrentDeviceState = INVALID_DEVICE_STATE;
 
+    /**
+     * Base device state received via
+     * {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
+     * "Base" in this context means the "physical" state of the device.
+     */
+    private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE;
+
     @NonNull
     private final BaseDataProducer<String> mRawFoldSupplier;
 
+    private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
+        @Override
+        public void onStateChanged(int state) {
+            mCurrentDeviceState = state;
+            mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+                    .this::notifyFoldingFeatureChange);
+        }
+
+        @Override
+        public void onBaseStateChanged(int state) {
+            mCurrentBaseDeviceState = state;
+
+            if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
+                    == COMMON_STATE_USE_BASE_STATE) {
+                mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
+                        .this::notifyFoldingFeatureChange);
+            }
+        }
+    };
+
     public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
             @NonNull BaseDataProducer<String> rawFoldSupplier) {
         mRawFoldSupplier = rawFoldSupplier;
@@ -92,12 +134,8 @@
         }
 
         if (mDeviceStateToPostureMap.size() > 0) {
-            DeviceStateCallback deviceStateCallback = (state) -> {
-                mCurrentDeviceState = state;
-                mRawFoldSupplier.getData(this::notifyFoldingFeatureChange);
-            };
             Objects.requireNonNull(context.getSystemService(DeviceStateManager.class))
-                    .registerCallback(context.getMainExecutor(), deviceStateCallback);
+                    .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
         }
     }
 
@@ -178,11 +216,18 @@
     }
 
     private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
-        final int globalHingeState = globalHingeState();
-        return parseListFromString(displayFeaturesString, globalHingeState);
+        return parseListFromString(displayFeaturesString, currentHingeState());
     }
 
-    private int globalHingeState() {
-        return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+    @CommonFoldingFeature.State
+    private int currentHingeState() {
+        @CommonFoldingFeature.State
+        int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
+
+        if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
+            posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
+        }
+
+        return posture;
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
new file mode 100644
index 0000000..5cd66ab
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.graphics.Rect
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            val stringExtras: Map<String, String> =
+                mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+
+            // cache the starting bounds here
+            val startBounds = Rect()
+
+            setup {
+                // Launch the PIP activity and wait for it to enter PiP mode
+                setRotation(Rotation.ROTATION_0)
+                RemoveAllTasksButHomeRule.removeAllTasksButHome()
+                pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
+
+                val initRegion = pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
+                startBounds
+                    .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+            }
+            transitions {
+                // continue the transition until the PIP snaps
+                pipApp.waitForPipToSnapTo(wmHelper, startBounds)
+            }
+        }
+
+    /** Checks that the visible region area of [pipApp] always moves right during the animation. */
+    @Postsubmit
+    @Test
+    fun pipLayerMovesRight() {
+        flicker.assertLayers {
+            val pipLayerList = layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+            pipLayerList.zipWithNext { previous, current ->
+                current.visibleRegion.isToTheRight(previous.visibleRegion.region)
+            }
+        }
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): List<FlickerTest> {
+            return FlickerTestFactory.nonRotationTests(
+                supportedRotations = listOf(Rotation.ROTATION_0)
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index 643af75..2071489 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -24,7 +24,7 @@
     }
 }
 plugins {
-    id 'com.android.application' version '8.0.0-beta01' apply false
-    id 'com.android.library' version '8.0.0-beta01' apply false
+    id 'com.android.application' version '8.0.0-beta03' apply false
+    id 'com.android.library' version '8.0.0-beta03' apply false
     id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
 }
diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
index 53b24b0..c3d5431 100644
--- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties
@@ -16,7 +16,7 @@
 
 #Thu Jul 14 10:36:06 CST 2022
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-2-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2f4ad9fd..832c1b9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3746,7 +3746,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 214;
+            private static final int SETTINGS_VERSION = 215;
 
             private final int mUserId;
 
@@ -5701,6 +5701,49 @@
                     currentVersion = 214;
                 }
 
+                if (currentVersion == 214) {
+                    // Version 214: Set a default value for Credential Manager service.
+
+                    final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    final Setting currentSetting = secureSettings
+                            .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE);
+                    if (currentSetting.isNull()) {
+                        final int resourceId =
+                            com.android.internal.R.string.config_defaultCredentialProviderService;
+                        final Resources resources = getContext().getResources();
+                        // If the config has not be defined we might get an exception. We also get
+                        // values from both the string array type and the single string in case the
+                        // OEM uses the wrong one.
+                        final List<String> providers = new ArrayList<>();
+                        try {
+                            providers.addAll(Arrays.asList(resources.getStringArray(resourceId)));
+                        } catch (Resources.NotFoundException e) {
+                            Slog.w(LOG_TAG,
+                                "Get default array Cred Provider not found: " + e.toString());
+                        }
+                        try {
+                            final String storedValue = resources.getString(resourceId);
+                            if (!TextUtils.isEmpty(storedValue)) {
+                                providers.add(storedValue);
+                            }
+                        } catch (Resources.NotFoundException e) {
+                            Slog.w(LOG_TAG,
+                                "Get default Cred Provider not found: " + e.toString());
+                        }
+
+                        if (!providers.isEmpty()) {
+                            final String defaultValue = String.join(":", providers);
+                            Slog.d(LOG_TAG, "Setting [" + defaultValue + "] as CredMan Service "
+                                    + "for user " + userId);
+                            secureSettings.insertSettingOverrideableByRestoreLocked(
+                                    Settings.Secure.CREDENTIAL_SERVICE, defaultValue, null, true,
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 215;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index dd60647..0d36d04 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -116,6 +116,27 @@
     public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26;
     // Device dreaming state
     public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27;
+    // Whether the screen is currently on. Note that the screen is considered on while turning on,
+    // but not while turning off.
+    public static final int SYSUI_STATE_SCREEN_ON = 1 << 28;
+    // Whether the screen is currently transitioning into the state indicated by
+    // SYSUI_STATE_SCREEN_ON.
+    public static final int SYSUI_STATE_SCREEN_TRANSITION = 1 << 29;
+
+    // Mask for SystemUiStateFlags to isolate SYSUI_STATE_SCREEN_ON and
+    // SYSUI_STATE_SCREEN_TRANSITION, to match SCREEN_STATE_*
+    public static final int SYSUI_STATE_SCREEN_STATE_MASK =
+            SYSUI_STATE_SCREEN_ON | SYSUI_STATE_SCREEN_TRANSITION;
+    // Screen is off.
+    public static final int SCREEN_STATE_OFF = 0;
+    // Screen is on.
+    public static final int SCREEN_STATE_ON = SYSUI_STATE_SCREEN_ON;
+    // Screen is still on, but transitioning to turn off.
+    public static final int SCREEN_STATE_TURNING_OFF = SYSUI_STATE_SCREEN_TRANSITION;
+    // Screen was off and is now turning on.
+    public static final int SCREEN_STATE_TURNING_ON =
+            SYSUI_STATE_SCREEN_TRANSITION | SYSUI_STATE_SCREEN_ON;
+
     // Whether the back gesture is allowed (or ignored) by the Shade
     public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean(
             "persist.wm.debug.shade_allow_back_gesture", false);
@@ -148,7 +169,9 @@
             SYSUI_STATE_IMMERSIVE_MODE,
             SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING,
             SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE,
-            SYSUI_STATE_DEVICE_DREAMING
+            SYSUI_STATE_DEVICE_DREAMING,
+            SYSUI_STATE_SCREEN_ON,
+            SYSUI_STATE_SCREEN_TRANSITION,
     })
     public @interface SystemUiStateFlags {}
 
@@ -187,6 +210,9 @@
         str.add((flags & SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0
                 ? "freeform_active_in_desktop_mode" : "");
         str.add((flags & SYSUI_STATE_DEVICE_DREAMING) != 0 ? "device_dreaming" : "");
+        str.add("screen_"
+                + ((flags & SYSUI_STATE_SCREEN_TRANSITION) != 0 ? "turning_" : "")
+                + ((flags & SYSUI_STATE_SCREEN_ON) != 0 ? "on" : "off"));
 
         return str.toString();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index f7d87fc..b62c729 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -64,6 +64,7 @@
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.recents.OverviewProxyService
 import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.traceSection
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -215,7 +216,9 @@
         requests.add(request)
         mainExecutor.execute {
             if (overlayView == null) {
-                createOverlayForDisplay(reason)
+                traceSection("SideFpsController#show(request=${request.name}, reason=$reason") {
+                    createOverlayForDisplay(reason)
+                }
             } else {
                 Log.v(TAG, "overlay already shown")
             }
@@ -227,7 +230,7 @@
         requests.remove(request)
         mainExecutor.execute {
             if (requests.isEmpty()) {
-                overlayView = null
+                traceSection("SideFpsController#hide(${request.name}") { overlayView = null }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index cbd61ffc..265a9903 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -628,14 +628,12 @@
     @JvmField val UDFPS_ELLIPSE_DETECTION = releasedFlag(2201, "udfps_ellipse_detection")
 
     // 2300 - stylus
+    @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used")
     @JvmField
-    val TRACK_STYLUS_EVER_USED = unreleasedFlag(2300, "track_stylus_ever_used", teamfood = true)
-    @JvmField
-    val ENABLE_STYLUS_CHARGING_UI =
-        unreleasedFlag(2301, "enable_stylus_charging_ui", teamfood = true)
+    val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui")
     @JvmField
     val ENABLE_USI_BATTERY_NOTIFICATIONS =
-        unreleasedFlag(2302, "enable_usi_battery_notifications", teamfood = true)
+        releasedFlag(2302, "enable_usi_battery_notifications")
     @JvmField val ENABLE_STYLUS_EDUCATION = unreleasedFlag(2303, "enable_stylus_education")
 
     // 2400 - performance tools and debugging info
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 019ca52..8a3ecc6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -31,6 +31,8 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_ON;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_TRANSITION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -121,7 +123,8 @@
 public class OverviewProxyService implements CallbackController<OverviewProxyListener>,
         NavigationModeController.ModeChangedListener, Dumpable {
 
-    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+    @VisibleForTesting
+    static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
 
     public static final String TAG_OPS = "OverviewProxyService";
     private static final long BACKOFF_MILLIS = 1000;
@@ -548,6 +551,7 @@
         mUiEventLogger = uiEventLogger;
         mDisplayTracker = displayTracker;
         mUnfoldTransitionProgressForwarder = unfoldTransitionProgressForwarder;
+        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
 
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
 
@@ -596,7 +600,6 @@
         // Connect to the service
         updateEnabledState();
         startConnectionToCurrentUser();
-        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
 
         // Listen for assistant changes
         assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
@@ -726,10 +729,8 @@
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
-        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
-                .setPackage(mRecentsComponentName.getPackageName());
         try {
-            mBound = mContext.bindServiceAsUser(launcherServiceIntent,
+            mBound = mContext.bindServiceAsUser(mQuickStepIntent,
                     mOverviewServiceConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     UserHandle.of(mUserTracker.getUserId()));
@@ -862,6 +863,11 @@
          */
         @Override
         public void onScreenTurnedOn() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, true)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurnedOn();
@@ -874,10 +880,26 @@
         }
 
         /**
+         * Notifies the Launcher that screen turned off.
+         */
+        @Override
+        public void onScreenTurnedOff() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, false)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, false)
+                .commitUpdate(mContext.getDisplayId());
+        }
+
+        /**
          * Notifies the Launcher that screen is starting to turn on.
          */
         @Override
         public void onScreenTurningOff() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, false)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurningOff();
@@ -894,6 +916,11 @@
          */
         @Override
         public void onScreenTurningOn() {
+            mSysUiState
+                .setFlag(SYSUI_STATE_SCREEN_ON, true)
+                .setFlag(SYSUI_STATE_SCREEN_TRANSITION, true)
+                .commitUpdate(mContext.getDisplayId());
+
             try {
                 if (mOverviewProxy != null) {
                     mOverviewProxy.onScreenTurningOn();
@@ -1005,4 +1032,21 @@
         default void onAssistantGestureCompletion(float velocity) {}
         default void startAssistant(Bundle bundle) {}
     }
+
+    /**
+     * Shuts down this service at the end of a testcase.
+     * <p>
+     * The in-production service is never shuts down, and it was not designed with testing in mind.
+     * This unregisters the mechanisms by which the service will be revived after a testcase.
+     * <p>
+     * NOTE: This is a stop-gap introduced when first added some tests to this class. It should
+     * probably be replaced by proper lifecycle management on this class.
+     */
+    @VisibleForTesting()
+    void shutdownForTest() {
+        mContext.unregisterReceiver(mLauncherStateChangedReceiver);
+        mIsEnabled = false;
+        mHandler.removeCallbacks(mConnectionRunnable);
+        disconnectFromLauncherService();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 664d61a..00519b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -3623,7 +3623,8 @@
         boolean goingToSleepWithoutAnimation = isGoingToSleep()
                 && !mDozeParameters.shouldControlScreenOff();
         boolean disabled = (!mDeviceInteractive && !mDozeServiceHost.isPulsing())
-                || goingToSleepWithoutAnimation;
+                || goingToSleepWithoutAnimation
+                || mDeviceProvisionedController.isFrpActive();
         mNotificationPanelViewController.setTouchAndAnimationDisabled(disabled);
         mNotificationIconAreaController.setAnimationsEnabled(!disabled);
     }
@@ -3797,10 +3798,10 @@
 
         boolean launchingAffordanceWithPreview = mLaunchingAffordance;
         mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview);
-
         if (mAlternateBouncerInteractor.isVisibleState()) {
-            if (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
-                    || mTransitionToFullShadeProgress > 0f) {
+            if ((!isOccluded() || isPanelExpanded())
+                    && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
+                    || mTransitionToFullShadeProgress > 0f)) {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
             } else {
                 mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
index 3944c8c..0d09fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedController.java
@@ -21,7 +21,8 @@
 /**
  * Controller to cache in process the state of the device provisioning.
  * <p>
- * This controller keeps track of the values of device provisioning and user setup complete
+ * This controller keeps track of the values of device provisioning, user setup complete, and
+ * whether Factory Reset Protection is active.
  */
 public interface DeviceProvisionedController extends CallbackController<DeviceProvisionedListener> {
 
@@ -49,6 +50,9 @@
      */
     boolean isCurrentUserSetup();
 
+    /** Returns true when Factory Reset Protection is locking the device. */
+    boolean isFrpActive();
+
     /**
      * Interface to provide calls when the values tracked change
      */
@@ -69,5 +73,10 @@
          * Call when some user changes from not provisioned to provisioned
          */
         default void onUserSetupChanged() { }
+
+        /**
+         * Called when the state of FRP changes.
+         */
+        default void onFrpActiveChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index a6b7d9c5..32c64f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -60,9 +60,11 @@
     }
 
     private val deviceProvisionedUri = globalSettings.getUriFor(Settings.Global.DEVICE_PROVISIONED)
+    private val frpActiveUri = secureSettings.getUriFor(Settings.Secure.SECURE_FRP_MODE)
     private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
 
     private val deviceProvisioned = AtomicBoolean(false)
+    private val frpActive = AtomicBoolean(false)
     @GuardedBy("lock")
     private val userSetupComplete = SparseBooleanArray()
     @GuardedBy("lock")
@@ -89,11 +91,15 @@
             userId: Int
         ) {
             val updateDeviceProvisioned = deviceProvisionedUri in uris
+            val updateFrp = frpActiveUri in uris
             val updateUser = if (userSetupUri in uris) userId else NO_USERS
-            updateValues(updateDeviceProvisioned, updateUser)
+            updateValues(updateDeviceProvisioned, updateFrp, updateUser)
             if (updateDeviceProvisioned) {
                 onDeviceProvisionedChanged()
             }
+            if (updateFrp) {
+                onFrpActiveChanged()
+            }
             if (updateUser != NO_USERS) {
                 onUserSetupChanged()
             }
@@ -103,7 +109,7 @@
     private val userChangedCallback = object : UserTracker.Callback {
         @WorkerThread
         override fun onUserChanged(newUser: Int, userContext: Context) {
-            updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+            updateValues(updateDeviceProvisioned = false, updateFrp = false, updateUser = newUser)
             onUserSwitched()
         }
 
@@ -125,19 +131,27 @@
         updateValues()
         userTracker.addCallback(userChangedCallback, backgroundExecutor)
         globalSettings.registerContentObserver(deviceProvisionedUri, observer)
+        globalSettings.registerContentObserver(frpActiveUri, observer)
         secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
     }
 
     @WorkerThread
-    private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
+    private fun updateValues(
+        updateDeviceProvisioned: Boolean = true,
+        updateFrp: Boolean = true,
+        updateUser: Int = ALL_USERS
+    ) {
         if (updateDeviceProvisioned) {
             deviceProvisioned
                     .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
         }
+        if (updateFrp) {
+            frpActive.set(globalSettings.getInt(Settings.Secure.SECURE_FRP_MODE, 0) != 0)
+        }
         synchronized(lock) {
             if (updateUser == ALL_USERS) {
-                val N = userSetupComplete.size()
-                for (i in 0 until N) {
+                val n = userSetupComplete.size()
+                for (i in 0 until n) {
                     val user = userSetupComplete.keyAt(i)
                     val value = secureSettings
                             .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
@@ -172,6 +186,10 @@
         return deviceProvisioned.get()
     }
 
+    override fun isFrpActive(): Boolean {
+        return frpActive.get()
+    }
+
     override fun isUserSetup(user: Int): Boolean {
         val index = synchronized(lock) {
             userSetupComplete.indexOfKey(user)
@@ -196,7 +214,13 @@
 
     override fun onDeviceProvisionedChanged() {
         dispatchChange(
-                DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+            DeviceProvisionedController.DeviceProvisionedListener::onDeviceProvisionedChanged
+        )
+    }
+
+    override fun onFrpActiveChanged() {
+        dispatchChange(
+            DeviceProvisionedController.DeviceProvisionedListener::onFrpActiveChanged
         )
     }
 
@@ -221,6 +245,7 @@
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println("Device provisioned: ${deviceProvisioned.get()}")
+        pw.println("Factory Reset Protection active: ${frpActive.get()}")
         synchronized(lock) {
             pw.println("User setup complete: $userSetupComplete")
             pw.println("Listeners: $listeners")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
new file mode 100644
index 0000000..eb7b481
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.app.AssistUtils
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.model.SysUiState
+import com.android.systemui.navigationbar.NavigationBarController
+import com.android.systemui.navigationbar.NavigationModeController
+import com.android.systemui.recents.OverviewProxyService.ACTION_QUICKSTEP
+import com.android.systemui.settings.FakeDisplayTracker
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.shared.recents.IOverviewProxy
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_ON
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_OFF
+import com.android.systemui.shared.system.QuickStepContract.SCREEN_STATE_TURNING_ON
+import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_STATE_MASK
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.unfold.progress.UnfoldTransitionProgressForwarder
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.sysui.ShellInterface
+import com.google.common.util.concurrent.MoreExecutors
+import dagger.Lazy
+import java.util.Optional
+import java.util.concurrent.Executor
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.intThat
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class OverviewProxyServiceTest : SysuiTestCase() {
+
+    @Main private val executor: Executor = MoreExecutors.directExecutor()
+
+    private lateinit var subject: OverviewProxyService
+    private val dumpManager = DumpManager()
+    private val displayTracker = FakeDisplayTracker(mContext)
+    private val sysUiState = SysUiState(displayTracker)
+    private val screenLifecycle = ScreenLifecycle(dumpManager)
+
+    @Mock private lateinit var overviewProxy: IOverviewProxy.Stub
+    @Mock private lateinit var packageManager: PackageManager
+
+    // The following mocks belong to not-yet-tested parts of OverviewProxyService.
+    @Mock private lateinit var commandQueue: CommandQueue
+    @Mock private lateinit var shellInterface: ShellInterface
+    @Mock private lateinit var navBarController: NavigationBarController
+    @Mock private lateinit var centralSurfaces: CentralSurfaces
+    @Mock private lateinit var navModeController: NavigationModeController
+    @Mock private lateinit var statusBarWinController: NotificationShadeWindowController
+    @Mock private lateinit var userTracker: UserTracker
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var sysuiUnlockAnimationController: KeyguardUnlockAnimationController
+    @Mock private lateinit var assistUtils: AssistUtils
+    @Mock
+    private lateinit var unfoldTransitionProgressForwarder:
+        Optional<UnfoldTransitionProgressForwarder>
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        val serviceComponent = ComponentName("test_package", "service_provider")
+        context.addMockService(serviceComponent, overviewProxy)
+        context.addMockServiceResolver(
+            TestableContext.MockServiceResolver {
+                if (it.action == ACTION_QUICKSTEP) serviceComponent else null
+            }
+        )
+        whenever(overviewProxy.queryLocalInterface(ArgumentMatchers.anyString()))
+            .thenReturn(overviewProxy)
+        whenever(overviewProxy.asBinder()).thenReturn(overviewProxy)
+
+        // packageManager.resolveServiceAsUser has to return non-null for
+        // OverviewProxyService#isEnabled to become true.
+        context.setMockPackageManager(packageManager)
+        whenever(packageManager.resolveServiceAsUser(any(), anyInt(), anyInt()))
+            .thenReturn(mock(ResolveInfo::class.java))
+
+        subject =
+            OverviewProxyService(
+                context,
+                executor,
+                commandQueue,
+                shellInterface,
+                Lazy { navBarController },
+                Lazy { Optional.of(centralSurfaces) },
+                navModeController,
+                statusBarWinController,
+                sysUiState,
+                userTracker,
+                screenLifecycle,
+                uiEventLogger,
+                displayTracker,
+                sysuiUnlockAnimationController,
+                assistUtils,
+                dumpManager,
+                unfoldTransitionProgressForwarder
+            )
+    }
+
+    @After
+    fun tearDown() {
+        subject.shutdownForTest()
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurnedOn triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurnedOn()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_ON }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurningOn triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurningOn()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_ON }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurnedOff triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurnedOff()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_OFF }
+            )
+    }
+
+    @Test
+    fun `ScreenLifecycle - screenTurningOff triggers SysUI state flag changes `() {
+        screenLifecycle.dispatchScreenTurningOff()
+
+        verify(overviewProxy)
+            .onSystemUiStateChanged(
+                intThat { it and SYSUI_STATE_SCREEN_STATE_MASK == SCREEN_STATE_TURNING_OFF }
+            )
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index c4ee326..031c17f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -1091,6 +1091,34 @@
     }
 
     @Test
+    public void testOccludingQSNotExpanded_transitionToAuthScrimmed() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and panel is NOT expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = false;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED));
+    }
+
+    @Test
+    public void testOccludingQSExpanded_transitionToAuthScrimmedShade() {
+        when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
+
+        // GIVEN device occluded and qs IS expanded
+        mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+        mCentralSurfaces.mPanelExpanded = true;
+
+        mCentralSurfaces.updateScrimController();
+
+        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+    }
+
+    @Test
     public void testShowKeyguardImplementation_setsState() {
         when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
 
@@ -1314,6 +1342,15 @@
         verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
     }
 
+    @Test
+    public void frpLockedDevice_shadeDisabled() {
+        when(mDeviceProvisionedController.isFrpActive()).thenReturn(true);
+        when(mDozeServiceHost.isPulsing()).thenReturn(true);
+        mCentralSurfaces.updateNotificationPanelTouchState();
+
+        verify(mNotificationPanelViewController).setTouchAndAnimationDisabled(true);
+    }
+
     /**
      * Configures the appropriate mocks and then calls {@link CentralSurfacesImpl#updateIsKeyguard}
      * to reconfigure the keyguard to reflect the requested showing/occluded states.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
index 5129f85..6980a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
@@ -90,6 +90,12 @@
     }
 
     @Test
+    fun testFrpNotActiveByDefault() {
+        init()
+        assertThat(controller.isFrpActive).isFalse()
+    }
+
+    @Test
     fun testNotUserSetupByDefault() {
         init()
         assertThat(controller.isUserSetup(START_USER)).isFalse()
@@ -104,6 +110,14 @@
     }
 
     @Test
+    fun testFrpActiveWhenCreated() {
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        init()
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupWhenCreated() {
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         init()
@@ -122,6 +136,16 @@
     }
 
     @Test
+    fun testFrpActiveChange() {
+        init()
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages() // background observer
+
+        assertThat(controller.isFrpActive).isTrue()
+    }
+
+    @Test
     fun testUserSetupChange() {
         init()
 
@@ -164,6 +188,7 @@
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
@@ -181,6 +206,7 @@
         verify(listener).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -195,6 +221,7 @@
         verify(listener, never()).onUserSwitched()
         verify(listener).onUserSetupChanged()
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
     }
 
     @Test
@@ -208,10 +235,26 @@
 
         verify(listener, never()).onUserSwitched()
         verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener).onDeviceProvisionedChanged()
     }
 
     @Test
+    fun testListenerCalledOnFrpActiveChanged() {
+        init()
+        controller.addCallback(listener)
+
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
+        testableLooper.processAllMessages()
+        mainExecutor.runAllReady()
+
+        verify(listener, never()).onUserSwitched()
+        verify(listener, never()).onUserSetupChanged()
+        verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener).onFrpActiveChanged()
+    }
+
+    @Test
     fun testRemoveListener() {
         init()
         controller.addCallback(listener)
@@ -220,11 +263,13 @@
         switchUser(10)
         settings.putIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 1, START_USER)
         settings.putInt(Settings.Global.DEVICE_PROVISIONED, 1)
+        settings.putInt(Settings.Secure.SECURE_FRP_MODE, 1)
 
         testableLooper.processAllMessages()
         mainExecutor.runAllReady()
 
         verify(listener, never()).onDeviceProvisionedChanged()
+        verify(listener, never()).onFrpActiveChanged()
         verify(listener, never()).onUserSetupChanged()
         verify(listener, never()).onUserSwitched()
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 67ba078..4b2467d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17279,6 +17279,9 @@
         public ComponentName startSdkSandboxService(Intent service, int clientAppUid,
                 String clientAppPackage, String processName) throws RemoteException {
             validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+                throw new IllegalArgumentException("uid does not belong to provided package");
+            }
             // TODO(b/269598719): Is passing the application thread of the system_server alright?
             // e.g. the sandbox getting privileged access due to this.
             ComponentName cn = ActivityManagerService.this.startService(
@@ -17345,6 +17348,9 @@
                 String processName, long flags)
                 throws RemoteException {
             validateSdkSandboxParams(service, clientAppUid, clientAppPackage, processName);
+            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
+                throw new IllegalArgumentException("uid does not belong to provided package");
+            }
             if (conn == null) {
                 throw new IllegalArgumentException("connection is null");
             }
@@ -17397,9 +17403,6 @@
             if (!UserHandle.isApp(clientAppUid)) {
                 throw new IllegalArgumentException("uid is not within application range");
             }
-            if (mAppOpsService.checkPackage(clientAppUid, clientAppPackage) != MODE_ALLOWED) {
-                throw new IllegalArgumentException("uid does not belong to provided package");
-            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 809f3f7..94d08bf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -46,6 +46,7 @@
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
@@ -2222,7 +2223,6 @@
         boolean wait = false;
         String opt;
         int displayId = Display.INVALID_DISPLAY;
-        boolean forceInvisible = false;
         while ((opt = getNextOption()) != null) {
             switch(opt) {
                 case "-w":
@@ -2231,27 +2231,31 @@
                 case "--display":
                     displayId = getDisplayIdFromNextArg();
                     break;
-                case "--force-invisible":
-                    forceInvisible = true;
-                    break;
                 default:
                     getErrPrintWriter().println("Error: unknown option: " + opt);
                     return -1;
             }
         }
         final int userId = Integer.parseInt(getNextArgRequired());
-        final boolean callStartProfile = !forceInvisible && isProfile(userId);
         final ProgressWaiter waiter = wait ? new ProgressWaiter(userId) : null;
-        Slogf.d(TAG, "runStartUser(): userId=%d, display=%d, waiter=%s, callStartProfile=%b, "
-                + "forceInvisible=%b", userId,  displayId, waiter, callStartProfile,
-                forceInvisible);
+
+        // For backwards compatibility, if the user is a profile, we need to define whether it
+        // should be started visible (when its parent is the current user) or not (when it isn't)
+        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+        final ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+        final int parentUserId = umi.getProfileParentId(userId);
+        final int currentUserId = ami.getCurrentUserId();
+        final boolean isProfile = parentUserId != userId;
+        final boolean isVisibleProfile = isProfile && parentUserId == currentUserId;
+        Slogf.d(TAG, "runStartUser(): userId=%d, parentUserId=%d, currentUserId=%d, isProfile=%b, "
+                + "isVisibleProfile=%b, display=%d, waiter=%s", userId, parentUserId, currentUserId,
+                isProfile, isVisibleProfile, displayId, waiter);
 
         boolean success;
         String displaySuffix = "";
-
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "shell_runStartUser" + userId);
         try {
-            if (callStartProfile) {
+            if (isVisibleProfile) {
                 Slogf.d(TAG, "calling startProfileWithListener(%d, %s)", userId, waiter);
                 // startProfileWithListener() will start the profile visible (as long its parent is
                 // the current user), while startUserInBackgroundWithListener() will always start
@@ -3906,11 +3910,6 @@
         return new Resources(AssetManager.getSystem(), metrics, config);
     }
 
-    private boolean isProfile(@UserIdInt int userId) {
-        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
-        return umi.getProfileParentId(userId) != userId;
-    }
-
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -4157,7 +4156,7 @@
             pw.println("      execution of that user if it is currently stopped.");
             pw.println("  get-current-user");
             pw.println("      Returns id of the current foreground user.");
-            pw.println("  start-user [-w] [--display DISPLAY_ID] [--force-invisible] <USER_ID>");
+            pw.println("  start-user [-w] [--display DISPLAY_ID] <USER_ID>");
             pw.println("      Start USER_ID in background if it is currently stopped;");
             pw.println("      use switch-user if you want to start the user in foreground.");
             pw.println("      -w: wait for start-user to complete and the user to be unlocked.");
@@ -4165,10 +4164,6 @@
                     + "which allows the user to launch activities on it.");
             pw.println("        (not supported on all devices; typically only on automotive builds "
                     + "where the vehicle has passenger displays)");
-            pw.println("      --force-invisible: always start the user invisible, even if it's a "
-                    + "profile.");
-            pw.println("        (by default, a profile is visible in the default display when its "
-                    + "parent is the current foreground user)");
             pw.println("  unlock-user <USER_ID>");
             pw.println("      Unlock the given user.  This will only work if the user doesn't");
             pw.println("      have an LSKF (PIN/pattern/password).");
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 81ca267..bef16b6 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -52,7 +52,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
@@ -224,22 +223,12 @@
     private static final int MSG_DELIVERY_TIMEOUT_HARD = 3;
     private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 4;
     private static final int MSG_CHECK_HEALTH = 5;
-    private static final int MSG_FINISH_RECEIVER = 6;
 
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
         mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
     }
 
-    private void enqueueFinishReceiver(@NonNull BroadcastProcessQueue queue,
-            @DeliveryState int deliveryState, @NonNull String reason) {
-        final SomeArgs args = SomeArgs.obtain();
-        args.arg1 = queue;
-        args.argi1 = deliveryState;
-        args.arg2 = reason;
-        mLocalHandler.sendMessage(Message.obtain(mLocalHandler, MSG_FINISH_RECEIVER, args));
-    }
-
     private final Handler mLocalHandler;
 
     private final Handler.Callback mLocalCallback = (msg) -> {
@@ -278,17 +267,6 @@
                 }
                 return true;
             }
-            case MSG_FINISH_RECEIVER: {
-                synchronized (mService) {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    final BroadcastProcessQueue queue = (BroadcastProcessQueue) args.arg1;
-                    final int deliveryState = args.argi1;
-                    final String reason = (String) args.arg2;
-                    args.recycle();
-                    finishReceiverActiveLocked(queue, deliveryState, reason);
-                }
-                return true;
-            }
         }
         return false;
     };
@@ -727,7 +705,7 @@
         // Ignore registered receivers from a previous PID
         if (receiver instanceof BroadcastFilter) {
             mRunningColdStart = null;
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED,
                     "BroadcastFilter for cold app");
             return;
         }
@@ -758,7 +736,7 @@
                 hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
         if (queue.app == null) {
             mRunningColdStart = null;
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
                     "startProcessLocked failed");
             return;
         }
@@ -800,7 +778,7 @@
             @NonNull BroadcastRecord r, int index) {
         final String reason = shouldSkipReceiver(queue, r, index);
         if (reason != null) {
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
             return true;
         }
         return false;
@@ -914,7 +892,7 @@
                     // TODO: consider making registered receivers of unordered
                     // broadcasts report results to detect ANRs
                     if (assumeDelivered) {
-                        enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_DELIVERED,
+                        finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED,
                                 "assuming delivered");
                     }
                 } else {
@@ -932,10 +910,10 @@
                 logw(msg);
                 app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
                         ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
-                enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
+                finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
             }
         } else {
-            enqueueFinishReceiver(queue, BroadcastRecord.DELIVERY_FAILURE,
+            finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
                     "missing IApplicationThread");
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c50e275..490a33e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3928,8 +3928,10 @@
     }
 
     @Override
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     /** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      * Part of service interface, check permissions and parameters here
      * Note calling package is for logging purposes only, not to be trusted
@@ -4945,8 +4947,10 @@
     }
 
     @Override
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     /**
      * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      */
@@ -7214,8 +7218,10 @@
      * @param device the audio device to be affected
      * @param deviceVolumeBehavior one of the device behaviors
      */
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
             @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
         // verify permissions
@@ -7295,8 +7301,11 @@
      * @param device the audio output device type
      * @return the volume behavior for the device
      */
-    @android.annotation.EnforcePermission(anyOf =
-            {"MODIFY_AUDIO_ROUTING", "QUERY_AUDIO_STATE", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
+    @android.annotation.EnforcePermission(anyOf = {
+            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.QUERY_AUDIO_STATE,
+            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+    })
     public @AudioManager.DeviceVolumeBehavior
     int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
         // verify permissions
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 12134f7..4e3de3c 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -499,7 +499,8 @@
 
             if ((recentTasks != null) && (!recentTasks.getList().isEmpty())) {
                 for (ActivityManager.RecentTaskInfo task : recentTasks.getList()) {
-                    if (packageName.equals(task.topActivityInfo.packageName)) {
+                    if (task.topActivityInfo != null && packageName.equals(
+                            task.topActivityInfo.packageName)) {
                         taskInfo = new TaskInfo();
                         taskInfo.frontTaskId = task.taskId;
                         taskInfo.isResizeable =
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index cc41207..afae08d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -56,7 +56,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.DeviceStateCache;
@@ -2514,7 +2514,7 @@
      * Starts a session to verify lock screen credentials provided by a remote device.
      */
     @NonNull
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation() {
         return mRecoverableKeyStoreManager.startRemoteLockscreenValidation(this);
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index c08958b..f073756 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,7 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.content.Context;
 import android.os.Binder;
 import android.os.RemoteException;
@@ -999,7 +999,7 @@
     /**
      * Starts a session to verify lock screen credentials provided by a remote device.
      */
-    public StartLockscreenValidationRequest startRemoteLockscreenValidation(
+    public RemoteLockscreenValidationSession startRemoteLockscreenValidation(
             LockSettingsService lockSettingsService) {
         if (mRemoteLockscreenValidationSessionStorage == null) {
             throw new UnsupportedOperationException("Under development");
@@ -1021,8 +1021,8 @@
         int badGuesses = mDatabase.getBadRemoteGuessCounter(userId);
         int remainingAttempts = Math.max(INVALID_REMOTE_GUESS_LIMIT - badGuesses, 0);
         // TODO(b/254335492): Schedule task to remove inactive session
-        return new StartLockscreenValidationRequest.Builder()
-                .setLockscreenUiType(keyguardCredentialsType)
+        return new RemoteLockscreenValidationSession.Builder()
+                .setLockType(keyguardCredentialsType)
                 .setRemainingAttempts(remainingAttempts)
                 .setSourcePublicKey(encodedPublicKey)
                 .build();
@@ -1046,7 +1046,9 @@
                 .build();
         }
         if (session == null) {
-            throw new IllegalStateException("There is no active lock screen check session");
+            return new RemoteLockscreenValidationResult.Builder()
+                .setResultCode(RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED)
+                .build();
         }
         byte[] decryptedCredentials;
         try {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2f98d34..3343172 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3504,15 +3504,15 @@
                     return;
                 }
                 final int oldMainUserId = getMainUserIdUnchecked();
-                final int oldFlags = systemUserData.info.flags;
-                final int newFlags;
+                final int oldSysFlags = systemUserData.info.flags;
+                final int newSysFlags;
                 final String newUserType;
                 if (newHeadlessSystemUserMode) {
                     newUserType = UserManager.USER_TYPE_SYSTEM_HEADLESS;
-                    newFlags = oldFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
+                    newSysFlags = oldSysFlags & ~UserInfo.FLAG_FULL & ~UserInfo.FLAG_MAIN;
                 } else {
                     newUserType = UserManager.USER_TYPE_FULL_SYSTEM;
-                    newFlags = oldFlags | UserInfo.FLAG_FULL;
+                    newSysFlags = oldSysFlags | UserInfo.FLAG_FULL | UserInfo.FLAG_MAIN;
                 }
 
                 if (systemUserData.info.userType.equals(newUserType)) {
@@ -3523,18 +3523,19 @@
                 Slogf.i(LOG_TAG, "Persisting emulated system user data: type changed from %s to "
                         + "%s, flags changed from %s to %s",
                         systemUserData.info.userType, newUserType,
-                        UserInfo.flagsToString(oldFlags), UserInfo.flagsToString(newFlags));
+                        UserInfo.flagsToString(oldSysFlags), UserInfo.flagsToString(newSysFlags));
 
                 systemUserData.info.userType = newUserType;
-                systemUserData.info.flags = newFlags;
+                systemUserData.info.flags = newSysFlags;
                 writeUserLP(systemUserData);
 
-                // Switch the MainUser to a reasonable choice if needed.
-                // (But if there was no MainUser, we deliberately continue to have no MainUser.)
+                // Designate the MainUser to a reasonable choice if needed.
                 final UserData oldMain = getUserDataNoChecks(oldMainUserId);
                 if (newHeadlessSystemUserMode) {
-                    if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) != 0) {
-                        // System was MainUser. So we need a new choice for Main. Pick the oldest.
+                    final boolean mainIsAlreadyNonSystem =
+                            oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0;
+                    if (!mainIsAlreadyNonSystem && isMainUserPermanentAdmin()) {
+                        // We need a new choice for Main. Pick the oldest.
                         // If no oldest, don't set any. Let the BootUserInitializer do that later.
                         final UserInfo newMainUser = getEarliestCreatedFullUser();
                         if (newMainUser != null) {
@@ -3544,16 +3545,16 @@
                         }
                     }
                 } else {
+                    // We already made user 0 Main above. Now strip it from the old Main user.
                     // TODO(b/256624031): For now, we demand the Main user (if there is one) is
                     //  always the system in non-HSUM. In the future, when we relax this, change how
                     //  we handle MAIN.
                     if (oldMain != null && (oldMain.info.flags & UserInfo.FLAG_SYSTEM) == 0) {
-                        // Someone else was the MainUser; transfer it to System.
                         Slogf.i(LOG_TAG, "Transferring Main to user 0 from " + oldMain.info.id);
                         oldMain.info.flags &= ~UserInfo.FLAG_MAIN;
-                        systemUserData.info.flags |= UserInfo.FLAG_MAIN;
                         writeUserLP(oldMain);
-                        writeUserLP(systemUserData);
+                    } else {
+                        Slogf.i(LOG_TAG, "Designated user 0 to be Main");
                     }
                 }
             }
@@ -3817,12 +3818,14 @@
         if (userVersion < 11) {
             // Add FLAG_MAIN
             if (isHeadlessSystemUserMode()) {
-                final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
-                if (earliestCreatedUser != null) {
-                    earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
-                    userIdsToWrite.add(earliestCreatedUser.id);
+                if (isMainUserPermanentAdmin()) {
+                    final UserInfo earliestCreatedUser = getEarliestCreatedFullUser();
+                    if (earliestCreatedUser != null) {
+                        earliestCreatedUser.flags |= UserInfo.FLAG_MAIN;
+                        userIdsToWrite.add(earliestCreatedUser.id);
+                    }
                 }
-            } else {
+            } else { // not isHeadlessSystemUserMode
                 synchronized (mUsersLock) {
                     final UserData userData = mUsers.get(UserHandle.USER_SYSTEM);
                     userData.info.flags |= UserInfo.FLAG_MAIN;
diff --git a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
index 98b24ea..333c98c 100644
--- a/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
+++ b/services/core/java/com/android/server/pm/UserManagerServiceShellCommand.java
@@ -380,6 +380,7 @@
             final int pid = Process.myPid();
             Slogf.i(LOG_TAG, "Restarting Android runtime(PID=%d) to finalize changes", pid);
             pw.println("Restarting Android runtime to finalize changes");
+            pw.println("The restart may trigger a 'Broken pipe' message; this is to be expected.");
             pw.flush();
 
             // Ideally there should be a cleaner / safer option to restart system_server, but
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c775419..faa0cc9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2098,7 +2098,7 @@
         mTaskSupervisor = supervisor;
 
         info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid,
-                launchMode);
+                info.launchMode, mActivityComponent);
         taskAffinity = info.taskAffinity;
         final String uid = Integer.toString(info.applicationInfo.uid);
         if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null
@@ -2199,12 +2199,18 @@
      * @param affinity The affinity of the activity.
      * @param uid The user-ID that has been assigned to this application.
      * @param launchMode The activity launch mode
+     * @param componentName The activity component name. This is only useful when the given
+     *                      launchMode is {@link ActivityInfo#LAUNCH_SINGLE_INSTANCE}
      * @return The task affinity
      */
-    static String computeTaskAffinity(String affinity, int uid, int launchMode) {
+    static String computeTaskAffinity(String affinity, int uid, int launchMode,
+            ComponentName componentName) {
         final String uidStr = Integer.toString(uid);
         if (affinity != null && !affinity.startsWith(uidStr)) {
-            affinity = uidStr + (launchMode == LAUNCH_SINGLE_INSTANCE ? "-si:" : ":") + affinity;
+            affinity = uidStr + ":" + affinity;
+            if (launchMode == LAUNCH_SINGLE_INSTANCE && componentName != null) {
+                affinity += ":" + componentName.hashCode();
+            }
         }
         return affinity;
     }
@@ -7855,7 +7861,6 @@
         if (task != null && requestedOrientation == SCREEN_ORIENTATION_BEHIND) {
             // We use Task here because we want to be consistent with what happens in
             // multi-window mode where other tasks orientations are ignored.
-            android.util.Log.d("orientation", "We are here");
             final ActivityRecord belowCandidate = task.getActivity(
                     a -> a.canDefineOrientationForActivitiesAbove() /* callback */,
                     this /* boundary */, false /* includeBoundary */,
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 14b845c..cb94146 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
+import static com.android.server.wm.ActivityRecord.State.RESUMED;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RECENTS;
@@ -1221,7 +1222,7 @@
     void onActivityIdle(ActivityRecord r) {
         // Clean up the hidden tasks when going to home because the user may not be unable to return
         // to the task from recents.
-        if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome()) {
+        if (!mHiddenTasks.isEmpty() && r.isActivityTypeHome() && r.isState(RESUMED)) {
             removeUnreachableHiddenTasks(r.getWindowingMode());
         }
         if (mCheckTrimmableTasksOnIdle) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 254c911..ebdc537 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5332,7 +5332,7 @@
         // the task if the affinity has changed.
 
         final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(),
-                srec.launchMode);
+                srec.launchMode, srec.mActivityComponent);
         if (srec == null || srec.getTask().affinity == null
                 || !srec.getTask().affinity.equals(affinity)) {
             return true;
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index e84f0cc..94260e20 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -46,9 +46,11 @@
 
     public ClearRequestSession(Context context, int userId, int callingUid,
             IClearCredentialStateCallback callback, ClearCredentialStateRequest request,
-            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_UNDEFINED,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        setupInitialPhaseMetric(ApiName.CLEAR_CREDENTIAL.getMetricCode(), MetricUtilities.ZERO);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 7e1780d..47b8c7d 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -53,9 +53,11 @@
             CreateCredentialRequest request,
             ICreateCredentialCallback callback,
             CallingAppInfo callingAppInfo,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_CREATE,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        setupInitialPhaseMetric(ApiName.CREATE_CREDENTIAL.getMetricCode(), MetricUtilities.UNIT);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 55973fa..a58dbe5 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.credentials.ClearCredentialStateRequest;
 import android.credentials.CreateCredentialException;
 import android.credentials.CreateCredentialRequest;
@@ -229,8 +228,10 @@
         if (hasPermission(android.Manifest.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS)) {
             return;
         }
-        
-        throw new SecurityException("Caller is missing permission: QUERY_ALL_PACKAGES or LIST_ENABLED_CREDENTIAL_PROVIDERS");
+
+        throw new SecurityException(
+                "Caller is missing permission: QUERY_ALL_PACKAGES or "
+                        + "LIST_ENABLED_CREDENTIAL_PROVIDERS");
     }
 
     private boolean hasPermission(String permission) {
@@ -402,6 +403,7 @@
                 GetCredentialRequest request,
                 IGetCredentialCallback callback,
                 final String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting executeGetCredential with callingPackage: " + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
 
@@ -423,7 +425,8 @@
                             callback,
                             request,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             processGetCredential(request, callback, session);
             return cancelTransport;
@@ -502,6 +505,9 @@
                                     + e.getMessage());
                 }
             }
+
+            finalizeAndEmitInitialPhaseMetric(session);
+            // TODO(b/271135048) - May still be worth emitting in the empty cases above.
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
@@ -510,6 +516,7 @@
                 CreateCredentialRequest request,
                 ICreateCredentialCallback callback,
                 String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting executeCreateCredential with callingPackage: "
                     + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
@@ -532,7 +539,8 @@
                             request,
                             callback,
                             constructCallingAppInfo(callingPackage, userId, request.getOrigin()),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             processCreateCredential(request, callback, session);
             return cancelTransport;
@@ -560,10 +568,17 @@
                 }
             }
 
+            finalizeAndEmitInitialPhaseMetric(session);
             // Iterate over all provider sessions and invoke the request
             providerSessions.forEach(ProviderSession::invokeSession);
         }
 
+        private void finalizeAndEmitInitialPhaseMetric(RequestSession session) {
+            var initMetric = session.mInitialPhaseMetric;
+            initMetric.setCredentialServiceBeginQueryTimeNanoseconds(System.nanoTime());
+            MetricUtilities.logApiCalled(initMetric);
+        }
+
         @Override
         public void setEnabledProviders(
                 List<String> providers, int userId, ISetEnabledProvidersCallback callback) {
@@ -648,6 +663,7 @@
                         }
                         MetricUtilities.logApiCalled(ApiName.IS_ENABLED_CREDENTIAL_PROVIDER_SERVICE,
                                 ApiStatus.SUCCESS, callingUid);
+                        // TODO(b/271135048) - Update asap to use the new logging types
                         return true;
                     }
                 }
@@ -677,12 +693,22 @@
                     mContext, userId, providerFilter, getEnabledProviders());
         }
 
-        private Set<ServiceInfo> getEnabledProviders() {
-            Set<ServiceInfo> enabledProviders = new HashSet<>();
+        @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same
+        // this.mLock
+        private Set<ComponentName> getEnabledProviders() {
+            Set<ComponentName> enabledProviders = new HashSet<>();
             synchronized (mLock) {
                 runForUser(
                         (service) -> {
-                            enabledProviders.add(service.getCredentialProviderInfo().getServiceInfo());
+                            try {
+                                enabledProviders.add(
+                                        service.getCredentialProviderInfo()
+                                                .getServiceInfo().getComponentName());
+                            } catch (NullPointerException e) {
+                                // Safe check
+                                Log.i(TAG, "Skipping provider as either the providerInfo"
+                                        + "or serviceInfo is null - weird");
+                            }
                         });
             }
             return enabledProviders;
@@ -693,6 +719,7 @@
                 ClearCredentialStateRequest request,
                 IClearCredentialStateCallback callback,
                 String callingPackage) {
+            final long timestampBegan = System.nanoTime();
             Log.i(TAG, "starting clearCredentialState with callingPackage: " + callingPackage);
             final int userId = UserHandle.getCallingUserId();
             int callingUid = Binder.getCallingUid();
@@ -710,7 +737,8 @@
                             callback,
                             request,
                             constructCallingAppInfo(callingPackage, userId, null),
-                            CancellationSignal.fromTransport(cancelTransport));
+                            CancellationSignal.fromTransport(cancelTransport),
+                            timestampBegan);
 
             // Initiate all provider sessions
             // TODO: Determine if provider needs to have clear capability in their manifest
@@ -729,6 +757,8 @@
                 }
             }
 
+            finalizeAndEmitInitialPhaseMetric(session);
+
             // Iterate over all provider sessions and invoke the request
             providerSessions.forEach(ProviderSession::invokeSession);
             return cancelTransport;
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index f59b32c..8e90c09 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.credentials.CredentialOption;
 import android.credentials.CredentialProviderInfo;
 import android.credentials.GetCredentialException;
 import android.credentials.GetCredentialRequest;
@@ -36,6 +37,7 @@
 import com.android.server.credentials.metrics.ProviderStatusForMetrics;
 
 import java.util.ArrayList;
+import java.util.stream.Collectors;
 
 /**
  * Central session for a single getCredentials request. This class listens to the
@@ -47,9 +49,14 @@
     private static final String TAG = "GetRequestSession";
     public GetRequestSession(Context context, int userId, int callingUid,
             IGetCredentialCallback callback, GetCredentialRequest request,
-            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal) {
+            CallingAppInfo callingAppInfo, CancellationSignal cancellationSignal,
+            long startedTimestamp) {
         super(context, userId, callingUid, request, callback, RequestInfo.TYPE_GET,
-                callingAppInfo, cancellationSignal);
+                callingAppInfo, cancellationSignal, startedTimestamp);
+        int numTypes = (request.getCredentialOptions().stream()
+                .map(CredentialOption::getType).collect(
+                Collectors.toSet())).size(); // Dedupe type strings
+        setupInitialPhaseMetric(ApiName.GET_CREDENTIAL.getMetricCode(), numTypes);
     }
 
     /**
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index 2dc930d..1b3e37a 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -26,6 +26,7 @@
 import com.android.server.credentials.metrics.ApiStatus;
 import com.android.server.credentials.metrics.CandidatePhaseMetric;
 import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
 
 import java.util.Map;
 
@@ -39,7 +40,10 @@
 
     public static final int DEFAULT_INT_32 = -1;
     public static final int[] DEFAULT_REPEATED_INT_32 = new int[0];
-
+    // Used for single count metric emits, such as singular amounts of various types
+    public static final int UNIT = 1;
+    // Used for zero count metric emits, such as zero amounts of various types
+    public static final int ZERO = 0;
 
     /**
      * This retrieves the uid of any package name, given a context and a component name for the
@@ -155,4 +159,18 @@
         }
     }
 
+    /**
+     * Handles the metric emit for the initial phase.
+     *
+     * @param initialPhaseMetric contains all the data for this emit
+     */
+    protected static void logApiCalled(InitialPhaseMetric initialPhaseMetric) {
+        /*
+        FrameworkStatsLog.write(FrameworkStatsLog.INITIAL_PHASE,
+        .. session_id .. initialPhaseMetric.getSessionId(),
+        ...
+        TODO Immediately - Fill in asap now that the split atom is checked in.
+         */
+    }
+
 }
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 42ec42b..ebd155a 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -38,6 +38,7 @@
 import com.android.server.credentials.metrics.ApiStatus;
 import com.android.server.credentials.metrics.CandidatePhaseMetric;
 import com.android.server.credentials.metrics.ChosenProviderMetric;
+import com.android.server.credentials.metrics.InitialPhaseMetric;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -75,7 +76,7 @@
 
     protected final Map<String, ProviderSession> mProviders = new HashMap<>();
     protected ChosenProviderMetric mChosenProviderMetric = new ChosenProviderMetric();
-    //TODO improve design to allow grouped metrics per request
+    protected InitialPhaseMetric mInitialPhaseMetric = new InitialPhaseMetric();
     protected final String mHybridService;
 
     @NonNull
@@ -96,7 +97,7 @@
             @UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
             @NonNull String requestType,
             CallingAppInfo callingAppInfo,
-            CancellationSignal cancellationSignal) {
+            CancellationSignal cancellationSignal, long timestampStarted) {
         mContext = context;
         mUserId = userId;
         mCallingUid = callingUid;
@@ -111,6 +112,9 @@
                 mUserId, this);
         mHybridService = context.getResources().getString(
                 R.string.config_defaultCredentialManagerHybridService);
+        mInitialPhaseMetric.setCredentialServiceStartedTimeNanoseconds(timestampStarted);
+        mInitialPhaseMetric.setSessionId(mRequestId.hashCode());
+        mInitialPhaseMetric.setCallerUid(mCallingUid);
     }
 
     public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -118,6 +122,12 @@
 
     protected abstract void launchUiWithProviderData(ArrayList<ProviderData> providerDataList);
 
+    // Sets up the initial metric collector for use across all request session impls
+    protected void setupInitialPhaseMetric(int metricCode, int requestClassType) {
+        this.mInitialPhaseMetric.setApiName(metricCode);
+        this.mInitialPhaseMetric.setCountRequestClassType(requestClassType);
+    }
+
     // UI callbacks
 
     @Override // from CredentialManagerUiCallbacks
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 5f062b0..31c6f6f 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -25,6 +25,8 @@
  */
 public class InitialPhaseMetric {
     private static final String TAG = "PreCandidateMetric";
+    // A sequence id to order united emits, due to split, this will statically always be 1
+    public static final int SEQUENCE_ID = 1;
 
     // The api being called, default set to unknown
     private int mApiName = ApiName.UNKNOWN.getMetricCode();
@@ -32,8 +34,6 @@
     private int mCallerUid = -1;
     // The session id to unite multiple atom emits, default to -1
     private long mSessionId = -1;
-    // A sequence id to order united emits, default to -1
-    private int mSequenceId = -1;
     private int mCountRequestClassType = -1;
 
     // Raw timestamps in nanoseconds, *the only* one logged as such (i.e. 64 bits) since it is a
@@ -50,7 +50,7 @@
 
     /* ---------- Latencies ---------- */
 
-    /* -- Direct Latencies -- */
+    /* -- Direct Latency Utility -- */
 
     public int getServiceStartToQueryLatencyMicroseconds() {
         return (int) ((this.mCredentialServiceStartedTimeNanoseconds
@@ -108,15 +108,6 @@
         return mSessionId;
     }
 
-    /* ------ SequenceId ------ */
-
-    public void setSequenceId(int sequenceId) {
-        mSequenceId = sequenceId;
-    }
-
-    public int getSequenceId() {
-        return mSequenceId;
-    }
 
     /* ------ Count Request Class Types ------ */
 
diff --git a/services/tests/servicestests/src/com/android/server/dreams/OWNERS b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
new file mode 100644
index 0000000..2f19cf5
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/dreams/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/service/dreams/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index c9612cd..ad63da5 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -39,6 +39,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.hardware.authsecret.IAuthSecret;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
@@ -97,6 +98,7 @@
     MockLockSettingsContext mContext;
     LockSettingsStorageTestable mStorage;
 
+    Resources mResources;
     FakeGateKeeperService mGateKeeperService;
     NotificationManager mNotificationManager;
     UserManager mUserManager;
@@ -122,6 +124,7 @@
 
     @Before
     public void setUp_baseServices() throws Exception {
+        mResources = createMockResources();
         mGateKeeperService = new FakeGateKeeperService();
         mNotificationManager = mock(NotificationManager.class);
         mUserManager = mock(UserManager.class);
@@ -146,7 +149,7 @@
         LocalServices.addService(WindowManagerInternal.class, mMockWindowManager);
 
         final Context origContext = InstrumentationRegistry.getContext();
-        mContext = new MockLockSettingsContext(origContext,
+        mContext = new MockLockSettingsContext(origContext, mResources,
                 mSettingsRule.mockContentResolver(origContext), mUserManager, mNotificationManager,
                 mDevicePolicyManager, mock(StorageManager.class), mock(TrustManager.class),
                 mock(KeyguardManager.class), mFingerprintManager, mFaceManager, mPackageManager);
@@ -245,6 +248,22 @@
         mLocalService = LocalServices.getService(LockSettingsInternal.class);
     }
 
+    private Resources createMockResources() {
+        Resources res = mock(Resources.class);
+
+        // Set up some default configs, copied from core/res/res/values/config.xml
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_disableLockscreenByDefault)))
+                .thenReturn(false);
+        when(res.getBoolean(
+                eq(com.android.internal.R.bool.config_enableCredentialFactoryResetProtection)))
+                .thenReturn(true);
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_isMainUserPermanentAdmin)))
+                .thenReturn(true);
+        when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot)))
+                .thenReturn(true);
+        return res;
+    }
+
     protected void setDeviceProvisioned(boolean provisioned) {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.DEVICE_PROVISIONED, provisioned ? 1 : 0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 05208441e..10ed882 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.content.res.Resources;
 import android.database.sqlite.SQLiteDatabase;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
@@ -106,8 +107,8 @@
         when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
 
         MockLockSettingsContext context = new MockLockSettingsContext(origContext,
-                mSettingsRule.mockContentResolver(origContext), mockUserManager,
-                mock(NotificationManager.class), mock(DevicePolicyManager.class),
+                mock(Resources.class), mSettingsRule.mockContentResolver(origContext),
+                mockUserManager, mock(NotificationManager.class), mock(DevicePolicyManager.class),
                 mock(StorageManager.class), mock(TrustManager.class), mock(KeyguardManager.class),
                 mock(FingerprintManager.class), mock(FaceManager.class),
                 mock(PackageManager.class));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
index 21c367b..96d7cbe 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/MockLockSettingsContext.java
@@ -27,6 +27,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.Handler;
@@ -36,6 +37,7 @@
 
 public class MockLockSettingsContext extends ContextWrapper {
 
+    private final Resources mResources;
     private final ContentResolver mContentResolver;
     private final UserManager mUserManager;
     private final NotificationManager mNotificationManager;
@@ -47,13 +49,14 @@
     private final FaceManager mFaceManager;
     private final PackageManager mPackageManager;
 
-    public MockLockSettingsContext(Context base, ContentResolver contentResolver,
-            UserManager userManager, NotificationManager notificationManager,
-            DevicePolicyManager devicePolicyManager, StorageManager storageManager,
-            TrustManager trustManager, KeyguardManager keyguardManager,
-            FingerprintManager fingerprintManager, FaceManager faceManager,
-            PackageManager packageManager) {
+    public MockLockSettingsContext(Context base, Resources resources,
+            ContentResolver contentResolver, UserManager userManager,
+            NotificationManager notificationManager, DevicePolicyManager devicePolicyManager,
+            StorageManager storageManager, TrustManager trustManager,
+            KeyguardManager keyguardManager, FingerprintManager fingerprintManager,
+            FaceManager faceManager, PackageManager packageManager) {
         super(base);
+        mResources = resources;
         mContentResolver = contentResolver;
         mUserManager = userManager;
         mNotificationManager = notificationManager;
@@ -67,6 +70,11 @@
     }
 
     @Override
+    public Resources getResources() {
+        return mResources;
+    }
+
+    @Override
     public ContentResolver getContentResolver() {
         return mContentResolver;
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 2affe92..8b178dd 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -41,7 +41,7 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.RemoteLockscreenValidationResult;
-import android.app.StartLockscreenValidationRequest;
+import android.app.RemoteLockscreenValidationSession;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Binder;
@@ -1326,11 +1326,10 @@
         when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
                 LockPatternUtils.CREDENTIAL_TYPE_PIN);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PIN);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PIN);
         assertThat(request.getRemainingAttempts()).isEqualTo(5);
         verify(mLockSettingsService).getCredentialType(anyInt());
     }
@@ -1340,11 +1339,10 @@
                 LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
         mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 3);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PATTERN);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PATTERN);
         assertThat(request.getRemainingAttempts()).isEqualTo(2);
     }
     @Test
@@ -1353,24 +1351,23 @@
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
         mRecoverableKeyStoreDb.setBadRemoteGuessCounter(mUserId, 7);
 
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        int credetialsType = request.getLockscreenUiType();
         assertThat(request.getRemainingAttempts()).isEqualTo(0);
-        assertThat(credetialsType).isEqualTo(KeyguardManager.PASSWORD);
+        assertThat(request.getLockType()).isEqualTo(KeyguardManager.PASSWORD);
     }
     @Test
     public void validateRemoteLockscreen_noActiveSession() throws Exception {
         when(mLockSettingsService.getCredentialType(anyInt())).thenReturn(
-                LockPatternUtils.CREDENTIAL_TYPE_NONE);
-        try {
-            mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
-                    mLockSettingsService);
-            fail("should have thrown");
-        } catch (IllegalStateException e) {
-            assertThat(e.getMessage()).contains("session");
-        }
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
+
+        RemoteLockscreenValidationResult result =
+                mRecoverableKeyStoreManager.validateRemoteLockscreen(INVALID_GUESS,
+                        mLockSettingsService);
+
+        assertThat(result.getResultCode()).isEqualTo(
+                RemoteLockscreenValidationResult.RESULT_SESSION_EXPIRED);
     }
     @Test
     public void validateRemoteLockscreen_decryptionError() throws Exception {
@@ -1456,7 +1453,7 @@
     }
 
     private byte[] encryptCredentialsForNewSession(byte[] credentials) throws Exception {
-        StartLockscreenValidationRequest request =
+        RemoteLockscreenValidationSession request =
                 mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
         PublicKey publicKey = SecureBox.decodePublicKey(request.getSourcePublicKey());
         return SecureBox.encrypt(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index e3cb5fb..b46a3b9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -1728,7 +1728,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
@@ -1754,7 +1754,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
@@ -1780,7 +1780,7 @@
         final ActivityInfo info = new ActivityInfo();
         info.applicationInfo = new ApplicationInfo();
         info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID,
-                0 /* launchMode */);
+                0 /* launchMode */, null /* componentName */);
         info.requiredDisplayCategory = "automotive";
         final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info)
                 .build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 74dd361..12f9a9e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -448,12 +448,15 @@
         final String taskAffinity = "affinity";
         final int uid = 10123;
         final Task task1 = createTaskBuilder(".Task1").build();
-        task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        final ComponentName componentName = getUniqueComponentName();
+        task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+                componentName);
         mRecentTasks.add(task1);
 
         // Add another task to recents, and make sure the previous task was removed.
         final Task task2 = createTaskBuilder(".Task2").build();
-        task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE);
+        task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE,
+                componentName);
         mRecentTasks.add(task2);
         assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
@@ -461,7 +464,7 @@
         // Add another single-instance task to recents, and make sure no task is removed.
         final Task task3 = createTaskBuilder(".Task3").build();
         task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid,
-                LAUNCH_SINGLE_INSTANCE);
+                LAUNCH_SINGLE_INSTANCE, componentName);
         mRecentTasks.add(task3);
         assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
                 true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index c4d760f..dd7e2d7 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -997,7 +997,8 @@
      */
     public static final int IWLAN_CONGESTION = 0x3C8C;
 
-    /** IKE configuration error resulting in failure  */
+    // Below IWLAN error codes are defined by the UE and do not relate to any 3GPP spec value
+    /** IKE configuration error resulting in failure */
     public static final int IWLAN_IKEV2_CONFIG_FAILURE = 0x4000;
     /**
      * Sent in the response to an IKE_AUTH message when, for some reason,
@@ -1014,6 +1015,57 @@
     public static final int IWLAN_DNS_RESOLUTION_TIMEOUT = 0x4005;
     /** Expected to update or bring down an ePDG tunnel, but no tunnel found*/
     public static final int IWLAN_TUNNEL_NOT_FOUND = 0x4006;
+    /**
+     * Failed to apply tunnel transform
+     *
+     * @hide
+     */
+    public static final int IWLAN_TUNNEL_TRANSFORM_FAILED = 0x4007;
+    /**
+     * IWLAN PDN setup failed due to Wi-Fi lost during IKE tunnel setup,
+     * match exception reported by IKE module
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_NETWORK_LOST_EXCEPTION = 0x4008;
+    /**
+     * Carrier-specific error codes during IKEv2 SA setup
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_PRIVATE_PROTOCOL_ERROR = 0x4009;
+    /**
+     * IKE Session closed before child session opened
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED = 0x400A;
+    /**
+     * IKE Init timeout, no response from EPDG
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_INIT_TIMEOUT = 0x400B;
+    /**
+     * DPD message does not get an ack after the re-tx attempts and duration, i.e., times out.
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_DPD_TIMEOUT = 0x400C;
+    /**
+     * The Wi-Fi to Wi-Fi handover of the IMS PDN fails because the network does not respond to the
+     * MOBIKE/rekey mobility message in the expected manner
+     *
+     * @hide
+     */
+    public static final int IWLAN_IKE_MOBILITY_TIMEOUT = 0x400D;
+    /**
+     * IKE client sent "IKE AUTH request 3" to the network but got "Internal address failure" from
+     * the network since no internal addresses can be assigned.
+     *
+     * @hide
+     */
+    public static final int IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE = 0x400E;
 
     // OEM sepecific error codes. To be used by OEMs when they don't
     // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
@@ -1508,6 +1560,16 @@
         sFailCauseMap.put(IWLAN_DNS_RESOLUTION_NAME_FAILURE, "IWLAN_DNS_RESOLUTION_NAME_FAILURE");
         sFailCauseMap.put(IWLAN_DNS_RESOLUTION_TIMEOUT, "IWLAN_DNS_RESOLUTION_TIMEOUT");
         sFailCauseMap.put(IWLAN_TUNNEL_NOT_FOUND, "IWLAN_TUNNEL_NOT_FOUND");
+        sFailCauseMap.put(IWLAN_TUNNEL_TRANSFORM_FAILED, "IWLAN_TUNNEL_TRANSFORM_FAILED");
+        sFailCauseMap.put(IWLAN_IKE_INIT_TIMEOUT, "IWLAN_IKE_INIT_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_NETWORK_LOST_EXCEPTION, "IWLAN_IKE_NETWORK_LOST_EXCEPTION");
+        sFailCauseMap.put(IWLAN_IKE_PRIVATE_PROTOCOL_ERROR, "IWLAN_IKE_PRIVATE_PROTOCOL_ERROR");
+        sFailCauseMap.put(IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED,
+                "IWLAN_IKE_SESSION_CLOSED_BEFORE_CHILD_SESSION_OPENED");
+        sFailCauseMap.put(IWLAN_IKE_DPD_TIMEOUT, "IWLAN_IKE_DPD_TIMEOUT");
+        sFailCauseMap.put(IWLAN_IKE_MOBILITY_TIMEOUT, "IWLAN_IKE_MOBILITY_TIMEOUT");
+        sFailCauseMap.put(IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE,
+                "IWLAN_EPDG_INTERNAL_ADDRESS_FAILURE");
         sFailCauseMap.put(OEM_DCFAILCAUSE_1, "OEM_DCFAILCAUSE_1");
         sFailCauseMap.put(OEM_DCFAILCAUSE_2, "OEM_DCFAILCAUSE_2");
         sFailCauseMap.put(OEM_DCFAILCAUSE_3, "OEM_DCFAILCAUSE_3");
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 758372a..5cbbe37 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -4374,7 +4374,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
     public void setSubscriptionUserHandle(int subscriptionId, @Nullable UserHandle userHandle) {
         if (!isValidSubscriptionId(subscriptionId)) {
@@ -4409,7 +4408,6 @@
      *
      * @hide
      */
-    @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION)
     public @Nullable UserHandle getSubscriptionUserHandle(int subscriptionId) {
         if (!isValidSubscriptionId(subscriptionId)) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 0e852b6..6e4e937 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -58,7 +58,51 @@
     }
 
     /**
-     * Expands the PIP window my using the pinch out gesture.
+     * Drags the PIP window away from the screen edge while not crossing the display center.
+     *
+     * @throws IllegalStateException if default display bounds are not available
+     * @return initial bounds of the PIP window
+     */
+    fun dragPipWindowAwayFromEdge(wmHelper: WindowManagerStateHelper, steps: Int): Rect {
+        val initWindowRect = getWindowRect(wmHelper).clone()
+
+        // initial pointer at the center of the window
+        val startX = initWindowRect.centerX()
+        val y = initWindowRect.centerY()
+
+        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+                ?: throw IllegalStateException("Default display is null")
+
+        // the offset to the right of the display center to drag the window to
+        val offset = 20
+
+        // the actual final x coordinate with the offset included
+        // if the pip window is closer to the right edge of the display the offset is positive
+        // otherwise the offset is negative
+        val endX = displayRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) 1 else -1)
+
+        // drag the window to the left but not beyond the center of the display
+        uiDevice.drag(startX, y, endX, y, steps)
+
+        return initWindowRect
+    }
+
+    /**
+     * Returns true if PIP window is closer to the right edge of the display than left.
+     *
+     * @throws IllegalStateException if default display bounds are not available
+     */
+    private fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
+        val windowRect = getWindowRect(wmHelper)
+
+        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+                ?: throw IllegalStateException("Default display is null")
+
+        return windowRect.centerX() > displayRect.centerX()
+    }
+
+    /**
+     * Expands the PIP window by using the pinch out gesture.
      *
      * @param percent The percentage by which to increase the pip window size.
      * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -106,7 +150,7 @@
     }
 
     /**
-     * Minimizes the PIP window my using the pinch in gesture.
+     * Minimizes the PIP window by using the pinch in gesture.
      *
      * @param percent The percentage by which to decrease the pip window size.
      * @throws IllegalArgumentException if percentage isn't between 0.0f and 1.0f
@@ -307,6 +351,27 @@
             .waitForAndVerify()
     }
 
+    /**
+     * Waits until the PIP window snaps horizontally to the provided bounds.
+     *
+     * @param finalRightX the final x coordinate of the right edge of the pip window
+     */
+    fun waitForPipToSnapTo(wmHelper: WindowManagerStateHelper, finalBounds: android.graphics.Rect) {
+        wmHelper
+            .StateSyncBuilder()
+            .add("pipWindowSnapped") {
+                val pipAppWindow =
+                    it.wmState.visibleWindows.firstOrNull { window ->
+                        this.windowMatchesAnyOf(window)
+                    }
+                        ?: return@add false
+                val pipRegionBounds = pipAppWindow.frameRegion.bounds
+                return@add pipRegionBounds.left == finalBounds.left &&
+                    pipRegionBounds.right == finalBounds.right
+            }
+            .waitForAndVerify()
+    }
+
     companion object {
         private const val TAG = "PipAppHelper"
         private const val ENTER_PIP_BUTTON_ID = "enter_pip"
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index e2668bc..0f04d6a 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -33,11 +33,15 @@
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 
+import androidx.annotation.Nullable;
+
 import org.junit.rules.TestRule;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
 
+import java.util.ArrayList;
+
 /**
  * A ContextWrapper with utilities specifically designed to make Testing easier.
  *
@@ -61,6 +65,7 @@
     private final TestableContentResolver mTestableContentResolver;
     private final TestableSettingsProvider mSettingsProvider;
 
+    private ArrayList<MockServiceResolver> mMockServiceResolvers;
     private ArrayMap<String, Object> mMockSystemServices;
     private ArrayMap<ComponentName, IBinder> mMockServices;
     private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
@@ -214,12 +219,15 @@
     /**
      * Adds a mock service to be connected to by a bindService call.
      * <p>
-     *     Normally a TestableContext will pass through all bind requests to the base context
-     *     but when addMockService has been called for a ComponentName being bound, then
-     *     TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
-     *     with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
-     *     when the service is unbound.
+     * Normally a TestableContext will pass through all bind requests to the base context
+     * but when addMockService has been called for a ComponentName being bound, then
+     * TestableContext will immediately trigger a {@link ServiceConnection#onServiceConnected}
+     * with the specified service, and will call {@link ServiceConnection#onServiceDisconnected}
+     * when the service is unbound.
      * </p>
+     *
+     * @see #addMockServiceResolver(MockServiceResolver) for custom resolution of service Intents to
+     * ComponentNames
      */
     public void addMockService(ComponentName component, IBinder service) {
         if (mMockServices == null) mMockServices = new ArrayMap<>();
@@ -227,12 +235,38 @@
     }
 
     /**
+     * Strategy to resolve a service {@link Intent} to a mock service {@link ComponentName}.
+     */
+    public interface MockServiceResolver {
+        @Nullable
+        ComponentName resolve(Intent service);
+    }
+
+    /**
+     * Registers a strategy to resolve service intents to registered mock services.
+     * <p>
+     * The result of the first {@link MockServiceResolver} to return a non-null
+     * {@link ComponentName} is used to look up a mock service. The mock service must be registered
+     * via {@link #addMockService(ComponentName, IBinder)} separately, using the same component
+     * name.
+     *
+     * If none of the resolvers return a non-null value, or the first returned component name
+     * does not link to a registered mock service, the bind requests are passed to the base context
+     *
+     * The resolvers are queried in order of registration.
+     */
+    public void addMockServiceResolver(MockServiceResolver resolver) {
+        if (mMockServiceResolvers == null) mMockServiceResolvers = new ArrayList<>();
+        mMockServiceResolvers.add(resolver);
+    }
+
+    /**
      * @see #addMockService(ComponentName, IBinder)
      */
     @Override
     public boolean bindService(Intent service, ServiceConnection conn, int flags) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindService(service, conn, flags);
     }
 
@@ -243,7 +277,7 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             Handler handler, UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, handler, user);
     }
 
@@ -254,18 +288,36 @@
     public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
             UserHandle user) {
         if (mService != null) mService.getLeakInfo(conn).addAllocation(new Throwable());
-        if (checkMocks(service.getComponent(), conn)) return true;
+        if (checkMocks(service, conn)) return true;
         return super.bindServiceAsUser(service, conn, flags, user);
     }
 
-    private boolean checkMocks(ComponentName component, ServiceConnection conn) {
-        if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
-            if (mActiveServices == null) mActiveServices = new ArrayMap<>();
-            mActiveServices.put(conn, component);
-            conn.onServiceConnected(component, mMockServices.get(component));
-            return true;
+    private boolean checkMocks(Intent service, ServiceConnection conn) {
+        if (mMockServices == null) return false;
+
+        ComponentName serviceComponent = resolveMockServiceComponent(service);
+        if (serviceComponent == null) return false;
+
+        IBinder serviceImpl = mMockServices.get(serviceComponent);
+        if (serviceImpl == null) return false;
+
+        if (mActiveServices == null) mActiveServices = new ArrayMap<>();
+        mActiveServices.put(conn, serviceComponent);
+        conn.onServiceConnected(serviceComponent, serviceImpl);
+        return true;
+    }
+
+    private ComponentName resolveMockServiceComponent(Intent service) {
+        ComponentName specifiedComponentName = service.getComponent();
+        if (specifiedComponentName != null) return specifiedComponentName;
+
+        if (mMockServiceResolvers == null) return null;
+
+        for (MockServiceResolver resolver : mMockServiceResolvers) {
+            ComponentName resolvedComponent = resolver.resolve(service);
+            if (resolvedComponent != null) return resolvedComponent;
         }
-        return false;
+        return null;
     }
 
     /**
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
index d3b7b12..9bfeb63 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/HotspotNetwork.java
@@ -207,10 +207,14 @@
         }
     }
 
-    private static void validate(long deviceId, @NetworkType int networkType, String networkName) {
+    private static void validate(long deviceId, @NetworkType int networkType, String networkName,
+            NetworkProviderInfo networkProviderInfo) {
         if (deviceId < 0) {
             throw new IllegalArgumentException("DeviceId must be set");
         }
+        if (Objects.isNull(networkProviderInfo)) {
+            throw new IllegalArgumentException("NetworkProviderInfo must be set");
+        }
         if (networkType != NETWORK_TYPE_CELLULAR && networkType != NETWORK_TYPE_WIFI
                 && networkType != NETWORK_TYPE_ETHERNET && networkType != NETWORK_TYPE_UNKNOWN) {
             throw new IllegalArgumentException("Illegal network type");
@@ -230,7 +234,8 @@
             @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) {
         validate(deviceId,
                 networkType,
-                networkName);
+                networkName,
+                networkProviderInfo);
         mDeviceId = deviceId;
         mNetworkProviderInfo = networkProviderInfo;
         mNetworkType = networkType;
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
index 8e396b6..8302094 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/HotspotNetworkTest.java
@@ -26,12 +26,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+
 import android.os.Parcel;
 import android.util.ArraySet;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.Arrays;
 
@@ -39,6 +43,7 @@
  * Unit tests for {@link HotspotNetwork}.
  */
 @SmallTest
+@RunWith(AndroidJUnit4.class)
 public class HotspotNetworkTest {
     private static final long DEVICE_ID = 11L;
     private static final NetworkProviderInfo NETWORK_PROVIDER_INFO =
@@ -67,7 +72,7 @@
      */
     @Test
     public void testParcelOperation() {
-        HotspotNetwork network = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
 
         Parcel parcelW = Parcel.obtain();
         network.writeToParcel(parcelW, 0);
@@ -88,30 +93,30 @@
      */
     @Test
     public void testEqualsOperation() {
-        HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
-        HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+        HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
         assertThat(network1).isEqualTo(network2);
 
-        HotspotNetwork.Builder builder = buildHotspotNetworkBuilder().setDeviceId(DEVICE_ID_1);
+        HotspotNetwork.Builder builder = buildHotspotNetworkBuilder(true).setDeviceId(DEVICE_ID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
+        builder = buildHotspotNetworkBuilder(true).setNetworkProviderInfo(NETWORK_PROVIDER_INFO1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHostNetworkType(NETWORK_TYPE_1);
+        builder = buildHotspotNetworkBuilder(true).setHostNetworkType(NETWORK_TYPE_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setNetworkName(NETWORK_NAME_1);
+        builder = buildHotspotNetworkBuilder(true).setNetworkName(NETWORK_NAME_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1);
+        builder = buildHotspotNetworkBuilder(true).setHotspotSsid(HOTSPOT_SSID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1);
+        builder = buildHotspotNetworkBuilder(true).setHotspotBssid(HOTSPOT_BSSID_1);
         assertThat(builder.build()).isNotEqualTo(network1);
 
-        builder = buildHotspotNetworkBuilder();
-        HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder();
+        builder = buildHotspotNetworkBuilder(true);
+        HotspotNetwork.Builder builder1 = buildHotspotNetworkBuilder(true);
         Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
 
         assertThat(builder1.build()).isNotEqualTo(builder.build());
@@ -122,7 +127,7 @@
      */
     @Test
     public void testGetMethods() {
-        HotspotNetwork network = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network = buildHotspotNetworkBuilder(true).build();
         ArraySet<Integer> securityTypes = new ArraySet<>();
         Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add);
 
@@ -137,21 +142,30 @@
 
     @Test
     public void testHashCode() {
-        HotspotNetwork network1 = buildHotspotNetworkBuilder().build();
-        HotspotNetwork network2 = buildHotspotNetworkBuilder().build();
+        HotspotNetwork network1 = buildHotspotNetworkBuilder(true).build();
+        HotspotNetwork network2 = buildHotspotNetworkBuilder(true).build();
 
         assertThat(network1.hashCode()).isEqualTo(network2.hashCode());
     }
 
-    private HotspotNetwork.Builder buildHotspotNetworkBuilder() {
+    @Test
+    public void networkProviderInfoNotSet_shouldThrowException() {
+        Exception e = assertThrows(IllegalArgumentException.class,
+                () -> buildHotspotNetworkBuilder(false).build());
+        assertThat(e.getMessage()).contains("NetworkProviderInfo");
+    }
+
+    private HotspotNetwork.Builder buildHotspotNetworkBuilder(boolean withNetworkProviderInfo) {
         HotspotNetwork.Builder builder = new HotspotNetwork.Builder()
                 .setDeviceId(DEVICE_ID)
-                .setNetworkProviderInfo(NETWORK_PROVIDER_INFO)
                 .setHostNetworkType(NETWORK_TYPE)
                 .setNetworkName(NETWORK_NAME)
                 .setHotspotSsid(HOTSPOT_SSID)
                 .setHotspotBssid(HOTSPOT_BSSID);
         Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType);
+        if (withNetworkProviderInfo) {
+            builder.setNetworkProviderInfo(NETWORK_PROVIDER_INFO);
+        }
         return builder;
     }
 }