Merge "Clear identity in LockSettingsInternal.getUserPasswordMetrics"
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 217b8b6..c956bf5 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -17,9 +17,12 @@
 package com.android.server.job;
 
 import android.annotation.Nullable;
+import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.List;
+
 /**
  * JobScheduler local system service interface.
  * {@hide} Only for use within the system server.
@@ -27,6 +30,11 @@
 public interface JobSchedulerInternal {
 
     /**
+     * Returns a list of jobs scheduled by the system service for itself.
+     */
+    List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace);
+
+    /**
      * Cancel the jobs for a given uid (e.g. when app data is cleared)
      *
      * @param includeProxiedJobs Include jobs scheduled for this UID by other apps
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index fce75a2..e786342 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3536,6 +3536,21 @@
     final class LocalService implements JobSchedulerInternal {
 
         @Override
+        public List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace) {
+            synchronized (mLock) {
+                final List<JobInfo> ownJobs = new ArrayList<>();
+                mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
+                    if (job.getSourceUid() == Process.SYSTEM_UID
+                            && Objects.equals(job.getNamespace(), namespace)
+                            && "android".equals(job.getSourcePackageName())) {
+                        ownJobs.add(job.getJob());
+                    }
+                });
+                return ownJobs;
+            }
+        }
+
+        @Override
         public void cancelJobsForUid(int uid, boolean includeProxiedJobs,
                 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
             JobSchedulerService.this.cancelJobsForUid(uid,
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 39b7385..b43421d 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -5525,6 +5525,8 @@
 android.nfc.NfcAdapter
 android.nfc.NfcControllerAlwaysOnListener
 android.nfc.NfcManager
+android.nfc.NfcServiceManager$ServiceRegisterer
+android.nfc.NfcServiceManager
 android.nfc.Tag$1
 android.nfc.Tag
 android.nfc.TechListParcel$1
diff --git a/core/api/current.txt b/core/api/current.txt
index ed81918..fcb3a14 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -13465,7 +13465,7 @@
     ctor public CreateCredentialException(@NonNull String);
     method @NonNull public String getType();
     field @NonNull public static final String TYPE_INTERRUPTED = "android.credentials.CreateCredentialException.TYPE_INTERRUPTED";
-    field @NonNull public static final String TYPE_NO_CREDENTIAL = "android.credentials.CreateCredentialException.TYPE_NO_CREDENTIAL";
+    field @NonNull public static final String TYPE_NO_CREATE_OPTIONS = "android.credentials.CreateCredentialException.TYPE_NO_CREATE_OPTIONS";
     field @NonNull public static final String TYPE_UNKNOWN = "android.credentials.CreateCredentialException.TYPE_UNKNOWN";
     field @NonNull public static final String TYPE_USER_CANCELED = "android.credentials.CreateCredentialException.TYPE_USER_CANCELED";
   }
@@ -15644,26 +15644,31 @@
   }
 
   public class MeshSpecification {
-    method @NonNull public static android.graphics.MeshSpecification make(@NonNull java.util.List<android.graphics.MeshSpecification.Attribute>, int, @NonNull java.util.List<android.graphics.MeshSpecification.Varying>, @NonNull String, @NonNull String);
-    method @NonNull public static android.graphics.MeshSpecification make(@NonNull java.util.List<android.graphics.MeshSpecification.Attribute>, int, @NonNull java.util.List<android.graphics.MeshSpecification.Varying>, @NonNull String, @NonNull String, @NonNull android.graphics.ColorSpace);
-    method @NonNull public static android.graphics.MeshSpecification make(@NonNull java.util.List<android.graphics.MeshSpecification.Attribute>, int, @NonNull java.util.List<android.graphics.MeshSpecification.Varying>, @NonNull String, @NonNull String, @NonNull android.graphics.ColorSpace, int);
-    field public static final int FLOAT = 0; // 0x0
-    field public static final int FLOAT2 = 1; // 0x1
-    field public static final int FLOAT3 = 2; // 0x2
-    field public static final int FLOAT4 = 3; // 0x3
-    field public static final int OPAQUE = 1; // 0x1
-    field public static final int PREMUL = 2; // 0x2
-    field public static final int UBYTE4 = 4; // 0x4
-    field public static final int UNKNOWN = 0; // 0x0
-    field public static final int UNPREMULT = 3; // 0x3
+    method @NonNull public static android.graphics.MeshSpecification make(@NonNull @Size(max=8) android.graphics.MeshSpecification.Attribute[], @IntRange(from=1, to=1024) int, @NonNull @Size(max=6) android.graphics.MeshSpecification.Varying[], @NonNull String, @NonNull String);
+    method @NonNull public static android.graphics.MeshSpecification make(@NonNull @Size(max=8) android.graphics.MeshSpecification.Attribute[], @IntRange(from=1, to=1024) int, @NonNull @Size(max=6) android.graphics.MeshSpecification.Varying[], @NonNull String, @NonNull String, @NonNull android.graphics.ColorSpace);
+    method @NonNull public static android.graphics.MeshSpecification make(@NonNull @Size(max=8) android.graphics.MeshSpecification.Attribute[], @IntRange(from=1, to=1024) int, @NonNull @Size(max=6) android.graphics.MeshSpecification.Varying[], @NonNull String, @NonNull String, @NonNull android.graphics.ColorSpace, int);
+    field public static final int ALPHA_TYPE_OPAQUE = 1; // 0x1
+    field public static final int ALPHA_TYPE_PREMUL = 2; // 0x2
+    field public static final int ALPHA_TYPE_PREMULT = 3; // 0x3
+    field public static final int ALPHA_TYPE_UNKNOWN = 0; // 0x0
+    field public static final int TYPE_FLOAT = 0; // 0x0
+    field public static final int TYPE_FLOAT2 = 1; // 0x1
+    field public static final int TYPE_FLOAT3 = 2; // 0x2
+    field public static final int TYPE_FLOAT4 = 3; // 0x3
+    field public static final int TYPE_UBYTE4 = 4; // 0x4
   }
 
   public static class MeshSpecification.Attribute {
     ctor public MeshSpecification.Attribute(int, int, @NonNull String);
+    method @NonNull public String getName();
+    method public int getOffset();
+    method public int getType();
   }
 
   public static class MeshSpecification.Varying {
     ctor public MeshSpecification.Varying(int, @NonNull String);
+    method @NonNull public String getName();
+    method public int getType();
   }
 
   @Deprecated public class Movie {
@@ -26536,8 +26541,16 @@
     method public int getTableName();
     method public int getVersion();
     field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TableRequest> CREATOR;
+    field public static final int TABLE_NAME_BAT = 4; // 0x4
+    field public static final int TABLE_NAME_CAT = 2; // 0x2
+    field public static final int TABLE_NAME_EIT = 6; // 0x6
+    field public static final int TABLE_NAME_NIT = 3; // 0x3
     field public static final int TABLE_NAME_PAT = 0; // 0x0
     field public static final int TABLE_NAME_PMT = 1; // 0x1
+    field public static final int TABLE_NAME_SDT = 5; // 0x5
+    field public static final int TABLE_NAME_SIT = 9; // 0x9
+    field public static final int TABLE_NAME_TDT = 7; // 0x7
+    field public static final int TABLE_NAME_TOT = 8; // 0x8
   }
 
   public final class TableResponse extends android.media.tv.BroadcastInfoResponse implements android.os.Parcelable {
@@ -27450,9 +27463,12 @@
     field public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = "command_change_channel_quietly";
     field public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
     field public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id";
+    field public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode";
     field public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id";
     field public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type";
     field public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume";
+    field public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK = 1; // 0x1
+    field public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE = 2; // 0x2
     field public static final String PLAYBACK_COMMAND_TYPE_SELECT_TRACK = "select_track";
     field public static final String PLAYBACK_COMMAND_TYPE_SET_STREAM_VOLUME = "set_stream_volume";
     field public static final String PLAYBACK_COMMAND_TYPE_STOP = "stop";
@@ -27481,6 +27497,7 @@
     method public void onCurrentChannelLcn(int);
     method public void onCurrentChannelUri(@Nullable android.net.Uri);
     method public void onCurrentTvInputId(@Nullable String);
+    method public void onCurrentVideoBounds(@NonNull android.graphics.Rect);
     method public void onDestroyBiInteractiveAppRequest(@NonNull String);
     method public void onError(@NonNull String, @NonNull android.os.Bundle);
     method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent);
@@ -27518,6 +27535,7 @@
     method @CallSuper public void requestCurrentChannelLcn();
     method @CallSuper public void requestCurrentChannelUri();
     method @CallSuper public void requestCurrentTvInputId();
+    method @CallSuper public void requestCurrentVideoBounds();
     method @CallSuper public void requestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method @CallSuper public void requestStartRecording(@Nullable android.net.Uri);
     method @CallSuper public void requestStopRecording(@NonNull String);
@@ -27570,6 +27588,7 @@
     method public void sendCurrentChannelLcn(int);
     method public void sendCurrentChannelUri(@Nullable android.net.Uri);
     method public void sendCurrentTvInputId(@Nullable String);
+    method public void sendCurrentVideoBounds(@NonNull android.graphics.Rect);
     method public void sendSigningResult(@NonNull String, @NonNull byte[]);
     method public void sendStreamVolume(float);
     method public void sendTrackInfoList(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
@@ -27600,6 +27619,7 @@
     method public void onRequestCurrentChannelLcn(@NonNull String);
     method public void onRequestCurrentChannelUri(@NonNull String);
     method public void onRequestCurrentTvInputId(@NonNull String);
+    method public void onRequestCurrentVideoBounds(@NonNull String);
     method public void onRequestSigning(@NonNull String, @NonNull String, @NonNull String, @NonNull String, @NonNull byte[]);
     method public void onRequestStartRecording(@NonNull String, @Nullable android.net.Uri);
     method public void onRequestStopRecording(@NonNull String, @NonNull String);
@@ -39475,6 +39495,7 @@
     method @NonNull public android.service.autofill.Dataset build();
     method @NonNull public android.service.autofill.Dataset.Builder setAuthentication(@Nullable android.content.IntentSender);
     method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull android.view.autofill.AutofillId, @Nullable android.service.autofill.Field);
+    method @NonNull public android.service.autofill.Dataset.Builder setField(@NonNull String, @NonNull android.service.autofill.Field);
     method @NonNull public android.service.autofill.Dataset.Builder setId(@Nullable String);
     method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation);
     method @Deprecated @NonNull public android.service.autofill.Dataset.Builder setInlinePresentation(@NonNull android.service.autofill.InlinePresentation, @NonNull android.service.autofill.InlinePresentation);
@@ -39583,6 +39604,7 @@
     method @Nullable public android.content.IntentSender getDelayedFillIntentSender();
     method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts();
     method public int getFlags();
+    method @NonNull public java.util.List<java.lang.String> getHints();
     method public int getId();
     method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -39611,6 +39633,7 @@
     method @Deprecated @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation, @Nullable android.service.autofill.InlinePresentation);
     method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.service.autofill.Presentations);
     method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
+    method @NonNull public android.service.autofill.FillResponse.Builder setDetectedFieldClassifications(@NonNull java.util.Set<android.service.assist.classification.FieldClassification>);
     method @NonNull public android.service.autofill.FillResponse.Builder setDialogHeader(@NonNull android.widget.RemoteViews);
     method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
     method @NonNull public android.service.autofill.FillResponse.Builder setFillDialogTriggerIds(@NonNull android.view.autofill.AutofillId...);
@@ -40242,6 +40265,7 @@
   }
 
   public final class BeginCreateCredentialResponse implements android.os.Parcelable {
+    ctor public BeginCreateCredentialResponse();
     method public int describeContents();
     method @NonNull public java.util.List<android.service.credentials.CreateEntry> getCreateEntries();
     method @Nullable public android.service.credentials.CreateEntry getRemoteCreateEntry();
@@ -40283,6 +40307,7 @@
   }
 
   public final class BeginGetCredentialResponse implements android.os.Parcelable {
+    ctor public BeginGetCredentialResponse();
     method public int describeContents();
     method @NonNull public java.util.List<android.service.credentials.Action> getActions();
     method @NonNull public java.util.List<android.service.credentials.Action> getAuthenticationActions();
@@ -40976,7 +41001,7 @@
     method public void onRequestCompleteVoice(android.service.voice.VoiceInteractionSession.CompleteVoiceRequest);
     method public void onRequestConfirmation(android.service.voice.VoiceInteractionSession.ConfirmationRequest);
     method public void onRequestPickOption(android.service.voice.VoiceInteractionSession.PickOptionRequest);
-    method public void onShow(android.os.Bundle, int);
+    method public void onShow(@Nullable android.os.Bundle, int);
     method public void onTaskFinished(android.content.Intent, int);
     method public void onTaskStarted(android.content.Intent, int);
     method public void onTrimMemory(int);
@@ -44008,6 +44033,7 @@
     field public static final int IWLAN_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 8241; // 0x2031
     field public static final int IWLAN_SYNTACTICAL_ERRORS_IN_PACKET_FILTERS = 8245; // 0x2035
     field public static final int IWLAN_SYNTACTICAL_ERROR_IN_THE_TFT_OPERATION = 8242; // 0x2032
+    field public static final int IWLAN_TUNNEL_NOT_FOUND = 16390; // 0x4006
     field public static final int IWLAN_UNAUTHENTICATED_EMERGENCY_NOT_SUPPORTED = 11055; // 0x2b2f
     field public static final int IWLAN_USER_UNKNOWN = 9001; // 0x2329
     field public static final int LIMITED_TO_IPV4 = 2234; // 0x8ba
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index af35d96..5171027 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -316,6 +316,30 @@
 
 }
 
+package android.nfc {
+
+  public class NfcFrameworkInitializer {
+    method public static void registerServiceWrappers();
+    method public static void setNfcServiceManager(@NonNull android.nfc.NfcServiceManager);
+  }
+
+  public class NfcServiceManager {
+    method @NonNull public android.nfc.NfcServiceManager.ServiceRegisterer getNfcManagerServiceRegisterer();
+  }
+
+  public static class NfcServiceManager.ServiceNotFoundException extends java.lang.Exception {
+    ctor public NfcServiceManager.ServiceNotFoundException(@NonNull String);
+  }
+
+  public static final class NfcServiceManager.ServiceRegisterer {
+    method @Nullable public android.os.IBinder get();
+    method @NonNull public android.os.IBinder getOrThrow() throws android.nfc.NfcServiceManager.ServiceNotFoundException;
+    method public void register(@NonNull android.os.IBinder);
+    method @Nullable public android.os.IBinder tryGet();
+  }
+
+}
+
 package android.os {
 
   public class ArtModuleServiceManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 20b3a26..58299fd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -100,6 +100,7 @@
     field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
     field public static final String CHANGE_APP_LAUNCH_TIME_ESTIMATE = "android.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE";
     field public static final String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
+    field public static final String CHECK_REMOTE_LOCKSCREEN = "android.permission.CHECK_REMOTE_LOCKSCREEN";
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
@@ -919,7 +920,9 @@
     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 @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
     field public static final int PATTERN = 2; // 0x2
     field public static final int PIN = 1; // 0x1
@@ -996,6 +999,25 @@
     field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL";
   }
 
+  public final class RemoteLockscreenValidationResult implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getResultCode();
+    method public long getTimeoutMillis();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.RemoteLockscreenValidationResult> CREATOR;
+    field public static final int RESULT_GUESS_INVALID = 2; // 0x2
+    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
+  }
+
+  public static final class RemoteLockscreenValidationResult.Builder {
+    ctor public RemoteLockscreenValidationResult.Builder();
+    method @NonNull public android.app.RemoteLockscreenValidationResult build();
+    method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setResultCode(int);
+    method @NonNull public android.app.RemoteLockscreenValidationResult.Builder setTimeoutMillis(long);
+  }
+
   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();
@@ -1013,6 +1035,23 @@
     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();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 85861c3..51e90d2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2529,6 +2529,7 @@
 
   public final class Dataset implements android.os.Parcelable {
     method @Nullable public android.content.IntentSender getAuthentication();
+    method @Nullable public java.util.ArrayList<java.lang.String> getAutofillDatatypes();
     method @Nullable public android.content.ClipData getFieldContent();
     method @Nullable public java.util.ArrayList<android.view.autofill.AutofillId> getFieldIds();
     method @Nullable public java.util.ArrayList<android.view.autofill.AutofillValue> getFieldValues();
@@ -2549,6 +2550,7 @@
   }
 
   public final class FillResponse implements android.os.Parcelable {
+    method @NonNull public java.util.Set<android.service.assist.classification.FieldClassification> getDetectedFieldClassifications();
     method public int getFlags();
   }
 
@@ -3278,6 +3280,7 @@
     field public static final String DEVICE_CONFIG_AUTOFILL_CREDENTIAL_MANAGER_IGNORE_VIEWS = "autofill_credential_manager_ignore_views";
     field public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_ENABLED = "autofill_dialog_enabled";
     field public static final String DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED = "pcc_classification_enabled";
+    field public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS = "pcc_classification_hints";
     field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes";
     field public static final String DEVICE_CONFIG_NON_AUTOFILLABLE_IME_ACTION_IDS = "non_autofillable_ime_action_ids";
     field public static final String DEVICE_CONFIG_PACKAGE_DENYLIST_FOR_UNIMPORTANT_VIEW = "package_deny_list_for_unimportant_view";
diff --git a/core/java/android/accessibilityservice/MagnificationConfig.java b/core/java/android/accessibilityservice/MagnificationConfig.java
index 486dc50..5019aee 100644
--- a/core/java/android/accessibilityservice/MagnificationConfig.java
+++ b/core/java/android/accessibilityservice/MagnificationConfig.java
@@ -107,7 +107,7 @@
      * Returns the activated state of the controlling magnifier. The controlling magnifier can be
      * activated even if the scale returned by {@link MagnificationConfig#getScale()} equals to 1.0.
      *
-     * @return {@code true} if the magnifier is showing on screen,
+     * @return {@code true} if the magnifier is activated and showing on screen,
      *         {@code false} otherwise.
      */
     public boolean isActivated() {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index eb57b3d..5496191 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -109,6 +109,8 @@
 import android.net.Proxy;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.nfc.NfcFrameworkInitializer;
+import android.nfc.NfcServiceManager;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.BluetoothServiceManager;
@@ -786,10 +788,10 @@
     static final class ReceiverData extends BroadcastReceiver.PendingResult {
         public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                 boolean ordered, boolean sticky, boolean assumeDelivered, IBinder token,
-                int sendingUser, int sentFromUid, String sentFromPackage) {
+                int sendingUser, int sendingUid, String sendingPackage) {
             super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky,
-                    assumeDelivered, token, sendingUser, intent.getFlags(), sentFromUid,
-                    sentFromPackage);
+                    assumeDelivered, token, sendingUser, intent.getFlags(), sendingUid,
+                    sendingPackage);
             this.intent = intent;
         }
 
@@ -1045,11 +1047,11 @@
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
                 boolean ordered, boolean assumeDelivered, int sendingUser, int processState,
-                int sentFromUid, String sentFromPackage) {
+                int sendingUid, String sendingPackage) {
             updateProcessState(processState, false);
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     ordered, false, assumeDelivered, mAppThread.asBinder(), sendingUser,
-                    sentFromUid, sentFromPackage);
+                    sendingUid, sendingPackage);
             r.info = info;
             sendMessage(H.RECEIVER, r);
         }
@@ -1061,12 +1063,12 @@
                     scheduleRegisteredReceiver(r.receiver, r.intent,
                             r.resultCode, r.data, r.extras, r.ordered, r.sticky,
                             r.assumeDelivered, r.sendingUser, r.processState,
-                            r.sentFromUid, r.sentFromPackage);
+                            r.sendingUid, r.sendingPackage);
                 } else {
                     scheduleReceiver(r.intent, r.activityInfo, r.compatInfo,
                             r.resultCode, r.data, r.extras, r.sync,
                             r.assumeDelivered, r.sendingUser, r.processState,
-                            r.sentFromUid, r.sentFromPackage);
+                            r.sendingUid, r.sendingPackage);
                 }
             }
         }
@@ -1297,7 +1299,7 @@
         public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                 int resultCode, String dataStr, Bundle extras, boolean ordered,
                 boolean sticky, boolean assumeDelivered, int sendingUser, int processState,
-                int sentFromUid, String sentFromPackage)
+                int sendingUid, String sendingPackage)
                 throws RemoteException {
             updateProcessState(processState, false);
 
@@ -1308,16 +1310,16 @@
             if (receiver instanceof LoadedApk.ReceiverDispatcher.InnerReceiver) {
                 ((LoadedApk.ReceiverDispatcher.InnerReceiver) receiver).performReceive(intent,
                         resultCode, dataStr, extras, ordered, sticky, assumeDelivered, sendingUser,
-                        sentFromUid, sentFromPackage);
+                        sendingUid, sendingPackage);
             } else {
                 if (!assumeDelivered) {
                     Log.wtf(TAG, "scheduleRegisteredReceiver() called for " + receiver
                             + " and " + intent + " without mechanism to finish delivery");
                 }
-                if (sentFromUid != Process.INVALID_UID || sentFromPackage != null) {
+                if (sendingUid != Process.INVALID_UID || sendingPackage != null) {
                     Log.wtf(TAG,
                             "scheduleRegisteredReceiver() called for " + receiver + " and " + intent
-                                    + " from " + sentFromPackage + " (UID: " + sentFromUid
+                                    + " from " + sendingPackage + " (UID: " + sendingUid
                                     + ") without mechanism to propagate the sender's identity");
                 }
                 receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky,
@@ -8135,6 +8137,7 @@
         BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
         BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
             BinderCallsStats.startForBluetooth(context); });
+        NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager());
     }
 
     private void purgePendingResources() {
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 5ef29e4..a7824a8 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -107,9 +107,8 @@
      *
      * @hide
      */
-    // TODO (b/254661666): Change to @EnabledAfter(T)
     @ChangeId
-    @Disabled
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
     @Overridable
     public static final long FGS_TYPE_NONE_DISABLED_CHANGE_ID = 255038118L;
 
@@ -142,9 +141,8 @@
      *
      * @hide
      */
-    // TODO (b/254661666): Change to @EnabledAfter(T)
     @ChangeId
-    @Disabled
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU)
     @Overridable
     public static final long FGS_TYPE_PERMISSION_CHANGE_ID = 254662522L;
 
@@ -286,10 +284,7 @@
             new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
                 new RegularPermission(Manifest.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION)
             }, true),
-            new ForegroundServiceTypePermissions(new ForegroundServiceTypePermission[] {
-                new RegularPermission(Manifest.permission.CAPTURE_VIDEO_OUTPUT),
-                new AppOpPermission(AppOpsManager.OP_PROJECT_MEDIA)
-            }, false)
+            null
     );
 
     /**
@@ -1058,7 +1053,7 @@
             if (policy.isTypeDisabled(callerUid)) {
                 return FGS_TYPE_POLICY_CHECK_DISABLED;
             }
-            int permissionResult = PERMISSION_DENIED;
+            int permissionResult = PERMISSION_GRANTED;
             // Do we have the permission to start FGS with this type.
             if (policy.mAllOfPermissions != null) {
                 permissionResult = policy.mAllOfPermissions.checkPermissions(context,
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 88c5064..be3d5a6 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -1075,6 +1075,46 @@
         return response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
     }
 
+    /** Starts a session to verify lockscreen credentials provided by a remote device.
+     *
+     * The session and corresponding public key will be removed when
+     * {@code validateRemoteLockScreen} provides a correct guess or after 10 minutes of inactivity.
+     *
+     * @return information necessary to perform remote lock screen credentials check, including
+
+     * short lived public key used to send encrypted guess and lock screen type.
+     *
+     * @throws IllegalStateException if lock screen is not set
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
+    @NonNull
+    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+        return mLockPatternUtils.startRemoteLockscreenValidation();
+    }
+
+    /**
+     * Verifies credentials guess from a remote device.
+     *
+     * <p>Secret must be encrypted using {@code SecureBox} library
+     * with public key from {@code StartLockscreenValidationRequest}
+     * 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.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN)
+    @NonNull
+    public RemoteLockscreenValidationResult validateRemoteLockscreen(
+            @NonNull byte[] encryptedCredential) {
+        return mLockPatternUtils.validateRemoteLockscreen(encryptedCredential);
+    }
+
     private LockscreenCredential createLockscreenCredential(
             @LockTypes int lockType, @Nullable byte[] password) {
         if (password == null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c13da0b..dd6b8b5 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1683,13 +1683,13 @@
                 performReceive(intent, resultCode, data, extras, ordered, sticky,
                         BroadcastReceiver.PendingResult.guessAssumeDelivered(
                                 BroadcastReceiver.PendingResult.TYPE_REGISTERED, ordered),
-                        sendingUser, /*sentFromUid=*/ Process.INVALID_UID,
-                        /*sentFromPackage=*/ null);
+                        sendingUser, /*sendingUid=*/ Process.INVALID_UID,
+                        /*sendingPackage=*/ null);
             }
 
             public void performReceive(Intent intent, int resultCode, String data,
                     Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
-                    int sendingUser, int sentFromUid, String sentFromPackage) {
+                    int sendingUser, int sendingUid, String sendingPackage) {
                 final LoadedApk.ReceiverDispatcher rd;
                 if (intent == null) {
                     Log.wtf(TAG, "Null intent received");
@@ -1705,7 +1705,7 @@
                 if (rd != null) {
                     rd.performReceive(intent, resultCode, data, extras,
                             ordered, sticky, assumeDelivered, sendingUser,
-                            sentFromUid, sentFromPackage);
+                            sendingUid, sendingPackage);
                 } else if (!assumeDelivered) {
                     // The activity manager dispatched a broadcast to a registered
                     // receiver in this process, but before it could be delivered the
@@ -1746,11 +1746,11 @@
 
             public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                     boolean ordered, boolean sticky, boolean assumeDelivered, int sendingUser,
-                    int sentFromUid, String sentFromPackage) {
+                    int sendingUid, String sendingPackage) {
                 super(resultCode, resultData, resultExtras,
                         mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                         sticky, assumeDelivered, mAppThread.asBinder(), sendingUser,
-                        intent.getFlags(), sentFromUid, sentFromPackage);
+                        intent.getFlags(), sendingUid, sendingPackage);
                 mCurIntent = intent;
             }
 
@@ -1874,9 +1874,9 @@
 
         public void performReceive(Intent intent, int resultCode, String data,
                 Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
-                int sendingUser, int sentFromUid, String sentFromPackage) {
+                int sendingUser, int sendingUid, String sendingPackage) {
             final Args args = new Args(intent, resultCode, data, extras, ordered,
-                    sticky, assumeDelivered, sendingUser, sentFromUid, sentFromPackage);
+                    sticky, assumeDelivered, sendingUser, sendingUid, sendingPackage);
             if (intent == null) {
                 Log.wtf(TAG, "Null intent received");
             } else {
diff --git a/core/java/android/app/ReceiverInfo.aidl b/core/java/android/app/ReceiverInfo.aidl
index 7364d0f..6916f71 100644
--- a/core/java/android/app/ReceiverInfo.aidl
+++ b/core/java/android/app/ReceiverInfo.aidl
@@ -38,8 +38,8 @@
     int sendingUser;
     int processState;
     int resultCode;
-    int sentFromUid = -1;
-    String sentFromPackage;
+    int sendingUid = -1;
+    String sendingPackage;
 
     /**
      * True if this instance represents a registered receiver and false if this instance
diff --git a/core/java/android/app/RemoteLockscreenValidationResult.java b/core/java/android/app/RemoteLockscreenValidationResult.java
index 4f15be2..0245f8c 100644
--- a/core/java/android/app/RemoteLockscreenValidationResult.java
+++ b/core/java/android/app/RemoteLockscreenValidationResult.java
@@ -19,6 +19,7 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,6 +30,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class RemoteLockscreenValidationResult implements Parcelable {
 
     /**
diff --git a/core/java/android/app/StartLockscreenValidationRequest.java b/core/java/android/app/StartLockscreenValidationRequest.java
index 69c268bcb..e818195 100644
--- a/core/java/android/app/StartLockscreenValidationRequest.java
+++ b/core/java/android/app/StartLockscreenValidationRequest.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.app.KeyguardManager.LockTypes;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -28,6 +29,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class StartLockscreenValidationRequest implements Parcelable {
 
     @LockTypes
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 9e31011..f74be22 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -940,7 +940,7 @@
      * @param tileLabel label of the tile to show to the user.
      * @param icon icon to use in the tile shown to the user.
      * @param resultExecutor an executor to run the callback on
-     * @param resultCallback callback to indicate the {@link RequestResult}.
+     * @param resultCallback callback to indicate the result of the request.
      *
      * @see android.service.quicksettings.TileService
      */
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0365f8c..64538ec 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -157,7 +157,7 @@
 import android.net.wifi.WifiFrameworkInitializer;
 import android.net.wifi.nl80211.WifiNl80211Manager;
 import android.net.wifi.sharedconnectivity.app.SharedConnectivityManager;
-import android.nfc.NfcManager;
+import android.nfc.NfcFrameworkInitializer;
 import android.ondevicepersonalization.OnDevicePersonalizationFrameworkInitializer;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -484,13 +484,6 @@
                 return new BatteryManager(ctx, stats, registrar);
             }});
 
-        registerService(Context.NFC_SERVICE, NfcManager.class,
-                new CachedServiceFetcher<NfcManager>() {
-            @Override
-            public NfcManager createService(ContextImpl ctx) {
-                return new NfcManager(ctx);
-            }});
-
         registerService(Context.DROPBOX_SERVICE, DropBoxManager.class,
                 new CachedServiceFetcher<DropBoxManager>() {
             @Override
@@ -1589,6 +1582,7 @@
             JobSchedulerFrameworkInitializer.registerServiceWrappers();
             BlobStoreManagerFrameworkInitializer.initialize();
             BluetoothFrameworkInitializer.registerServiceWrappers();
+            NfcFrameworkInitializer.registerServiceWrappers();
             TelephonyFrameworkInitializer.registerServiceWrappers();
             AppSearchManagerFrameworkInitializer.initialize();
             HealthServicesInitializer.registerServiceWrappers();
diff --git a/core/java/android/credentials/CreateCredentialException.java b/core/java/android/credentials/CreateCredentialException.java
index 84cc9a8..c344004 100644
--- a/core/java/android/credentials/CreateCredentialException.java
+++ b/core/java/android/credentials/CreateCredentialException.java
@@ -40,13 +40,13 @@
             "android.credentials.CreateCredentialException.TYPE_UNKNOWN";
 
     /**
-     * The error type value for when no credential is available for the given {@link
-     * CredentialManager#createCredential(CreateCredentialRequest, Activity,
+     * The error type value for when no create options are available from any provider(s),
+     * for the given {@link CredentialManager#createCredential(CreateCredentialRequest, Activity,
      * CancellationSignal, Executor, OutcomeReceiver)} request.
      */
     @NonNull
-    public static final String TYPE_NO_CREDENTIAL =
-            "android.credentials.CreateCredentialException.TYPE_NO_CREDENTIAL";
+    public static final String TYPE_NO_CREATE_OPTIONS =
+            "android.credentials.CreateCredentialException.TYPE_NO_CREATE_OPTIONS";
     /**
      * The error type value for when the user intentionally cancelled the request.
      *
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 6dc80cf..1bb44af 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -43,7 +43,6 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Log;
 
 import java.io.IOException;
@@ -426,6 +425,7 @@
     // recovery
     @UnsupportedAppUsage
     static INfcAdapter sService;
+    static NfcServiceManager.ServiceRegisterer sServiceRegisterer;
     static INfcTag sTagService;
     static INfcCardEmulation sCardEmulationService;
     static INfcFCardEmulation sNfcFCardEmulationService;
@@ -624,6 +624,12 @@
                 Log.v(TAG, "this device does not have NFC support");
                 throw new UnsupportedOperationException();
             }
+            NfcServiceManager manager = NfcFrameworkInitializer.getNfcServiceManager();
+            if (manager == null) {
+                Log.e(TAG, "NfcServiceManager is null");
+                throw new UnsupportedOperationException();
+            }
+            sServiceRegisterer = manager.getNfcManagerServiceRegisterer();
             sService = getServiceInterface();
             if (sService == null) {
                 Log.e(TAG, "could not retrieve NFC service");
@@ -665,7 +671,7 @@
     /** get handle to NFC service interface */
     private static INfcAdapter getServiceInterface() {
         /* get a handle to NFC service */
-        IBinder b = ServiceManager.getService("nfc");
+        IBinder b = sServiceRegisterer.get();
         if (b == null) {
             return null;
         }
@@ -695,12 +701,13 @@
                     "context not associated with any application (using a mock context?)");
         }
 
-        if (getServiceInterface() == null) {
-            // NFC is not available
-            return null;
+        if (sIsInitialized && sServiceRegisterer.tryGet() == null) {
+            synchronized (NfcAdapter.class) {
+                /* Stale sService pointer */
+                if (sIsInitialized) sIsInitialized = false;
+            }
         }
-
-        /* use getSystemService() for consistency */
+        /* Try to initialize the service */
         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
         if (manager == null) {
             // NFC not available
diff --git a/core/java/android/nfc/NfcFrameworkInitializer.java b/core/java/android/nfc/NfcFrameworkInitializer.java
new file mode 100644
index 0000000..1ab8a1e
--- /dev/null
+++ b/core/java/android/nfc/NfcFrameworkInitializer.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.nfc;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.SystemServiceRegistry;
+import android.content.Context;
+
+/**
+ * Class for performing registration for Nfc service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class NfcFrameworkInitializer {
+    private NfcFrameworkInitializer() {}
+
+    private static volatile NfcServiceManager sNfcServiceManager;
+
+    /**
+     * Sets an instance of {@link NfcServiceManager} that allows
+     * the nfc mainline module to register/obtain nfc binder services. This is called
+     * by the platform during the system initialization.
+     *
+     * @param nfcServiceManager instance of {@link NfcServiceManager} that allows
+     * the nfc mainline module to register/obtain nfcd binder services.
+     */
+    public static void setNfcServiceManager(
+            @NonNull NfcServiceManager nfcServiceManager) {
+        if (sNfcServiceManager != null) {
+            throw new IllegalStateException("setNfcServiceManager called twice!");
+        }
+
+        if (nfcServiceManager == null) {
+            throw new IllegalArgumentException("nfcServiceManager must not be null");
+        }
+
+        sNfcServiceManager = nfcServiceManager;
+    }
+
+    /** @hide */
+    public static NfcServiceManager getNfcServiceManager() {
+        return sNfcServiceManager;
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers NFC service
+     * to {@link Context}, so that {@link Context#getSystemService} can return them.
+     *
+     * @throws IllegalStateException if this is called from anywhere besides
+     * {@link SystemServiceRegistry}
+     */
+    public static void registerServiceWrappers() {
+        SystemServiceRegistry.registerContextAwareService(Context.NFC_SERVICE,
+                NfcManager.class, context -> new NfcManager(context));
+    }
+}
diff --git a/core/java/android/nfc/NfcServiceManager.java b/core/java/android/nfc/NfcServiceManager.java
new file mode 100644
index 0000000..5582f11
--- /dev/null
+++ b/core/java/android/nfc/NfcServiceManager.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+
+/**********************************************************************
+ * This file is not a part of the NFC mainline modure                 *
+ * *******************************************************************/
+
+package android.nfc;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Provides a way to register and obtain the system service binder objects managed by the nfc
+ * service.
+ *
+ * @hide
+ */
+@SystemApi(client = Client.MODULE_LIBRARIES)
+public class NfcServiceManager {
+
+    /**
+     * @hide
+     */
+    public NfcServiceManager() {
+    }
+
+    /**
+     * A class that exposes the methods to register and obtain each system service.
+     */
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         */
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         */
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         */
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         */
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     */
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the "nfc" service.
+     */
+    @NonNull
+    public ServiceRegisterer getNfcManagerServiceRegisterer() {
+        return new ServiceRegisterer(Context.NFC_SERVICE);
+    }
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 7ad1735..810bd63 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -34,7 +34,6 @@
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
-import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ContentProvider;
@@ -46,7 +45,6 @@
 import android.system.OsConstants;
 import android.system.StructStat;
 import android.util.Log;
-import android.util.Slog;
 
 import dalvik.system.CloseGuard;
 import dalvik.system.VMRuntime;
@@ -331,17 +329,6 @@
     }
 
     private static FileDescriptor openInternal(File file, int mode) throws FileNotFoundException {
-        if ((mode & MODE_WRITE_ONLY) != 0 && (mode & MODE_APPEND) == 0
-                && (mode & MODE_TRUNCATE) == 0 && ((mode & MODE_READ_ONLY) == 0)
-                && file.exists()) {
-            String packageName = ActivityThread.currentApplication().getApplicationContext()
-                    .getPackageName();
-            Slog.wtfQuiet(TAG, "ParcelFileDescriptor.open is called with w without t or a or r, "
-                    + "which will have a different behavior beginning in Android Q."
-                    + "\nPackage Name: " + packageName + "\nMode: " + mode
-                    + "\nFilename: " + file.getPath());
-        }
-
         final int flags = FileUtils.translateModePfdToPosix(mode) | ifAtLeastQ(O_CLOEXEC);
 
         int realMode = S_IRWXU | S_IRWXG;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index e9f099a..0fa5e3e 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -120,6 +120,8 @@
     private final ArrayList<InlinePresentation> mFieldInlinePresentations;
     private final ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
     private final ArrayList<DatasetFieldFilter> mFieldFilters;
+    private final ArrayList<String> mAutofillDatatypes;
+
     @Nullable private final ClipData mFieldContent;
     private final RemoteViews mPresentation;
     private final RemoteViews mDialogPresentation;
@@ -143,6 +145,14 @@
         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
         mAuthentication = builder.mAuthentication;
         mId = builder.mId;
+        mAutofillDatatypes = builder.mAutofillDatatypes;
+    }
+
+    /** @hide */
+    @TestApi
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    public @Nullable ArrayList<String> getAutofillDatatypes() {
+        return mAutofillDatatypes;
     }
 
     /** @hide */
@@ -293,6 +303,7 @@
         private ArrayList<InlinePresentation> mFieldInlinePresentations;
         private ArrayList<InlinePresentation> mFieldInlineTooltipPresentations;
         private ArrayList<DatasetFieldFilter> mFieldFilters;
+        private ArrayList<String> mAutofillDatatypes;
         @Nullable private ClipData mFieldContent;
         private RemoteViews mPresentation;
         private RemoteViews mDialogPresentation;
@@ -922,6 +933,55 @@
         }
 
         /**
+         * Adds a field to this Dataset with a specific type and no
+         * AutofillId. This is used to send back Field information
+         * when Autofilling with platform detections is on.
+         * Platform detections are on when receiving a populated list from
+         * FillRequest#getHints().
+         *
+         * Populate every field/type known for this user for this app.
+         *
+         * For example, if getHints() contains "username" and "password",
+         * a new Dataset should be created that calls this method twice,
+         * one for the username, then another for the password (assuming
+         * the only one credential pair is found for the user). If a user
+         * has two credential pairs, then two Datasets should be created,
+         * and so on.
+         *
+         * Using this will remove any data populated with
+         * setField(@NonNull AutofillId id, @Nullable Field field).
+         *
+         * @param hint An autofill hint returned from {@link
+         *         FillRequest#getHints()}.
+         *
+         * @param field the fill information about the field.
+         *
+         * @throws IllegalStateException if {@link #build()} was already called
+         * or this builder also contains AutofillId information
+         *
+         * @return this builder.
+         */
+        public @NonNull Dataset.Builder setField(
+                @NonNull String hint, @NonNull Field field) {
+            throwIfDestroyed();
+
+            final DatasetFieldFilter filter = field.getDatasetFieldFilter();
+            final Presentations presentations = field.getPresentations();
+            if (presentations == null) {
+                setLifeTheUniverseAndEverything(hint, field.getValue(), null, null, null,
+                        filter, null);
+            } else {
+                setLifeTheUniverseAndEverything(hint, field.getValue(),
+                        presentations.getMenuPresentation(),
+                        presentations.getInlinePresentation(),
+                        presentations.getInlineTooltipPresentation(), filter,
+                        presentations.getDialogPresentation());
+            }
+
+            return this;
+        }
+
+        /**
          * Sets the value of a field with an <a href="#Filtering">explicit filter</a>, and using an
          * {@link InlinePresentation} to visualize it as an inline suggestion.
          *
@@ -958,6 +1018,32 @@
             return this;
         }
 
+        private void setLifeTheUniverseAndEverything(String datatype,
+                @Nullable AutofillValue value,
+                @Nullable RemoteViews presentation,
+                @Nullable InlinePresentation inlinePresentation,
+                @Nullable InlinePresentation tooltip,
+                @Nullable DatasetFieldFilter filter,
+                @Nullable RemoteViews dialogPresentation) {
+            if (mAutofillDatatypes == null) {
+                mFieldValues = new ArrayList<>();
+                mFieldPresentations = new ArrayList<>();
+                mFieldDialogPresentations = new ArrayList<>();
+                mFieldInlinePresentations = new ArrayList<>();
+                mFieldInlineTooltipPresentations = new ArrayList<>();
+                mFieldFilters = new ArrayList<>();
+                mAutofillDatatypes = new ArrayList<>();
+                mFieldIds = null;
+            }
+            mFieldValues.add(value);
+            mFieldPresentations.add(presentation);
+            mFieldDialogPresentations.add(dialogPresentation);
+            mFieldInlinePresentations.add(inlinePresentation);
+            mFieldInlineTooltipPresentations.add(tooltip);
+            mFieldFilters.add(filter);
+            mAutofillDatatypes.add(datatype);
+        }
+
         private void setLifeTheUniverseAndEverything(@NonNull AutofillId id,
                 @Nullable AutofillValue value, @Nullable RemoteViews presentation,
                 @Nullable InlinePresentation inlinePresentation,
@@ -984,6 +1070,7 @@
                 mFieldInlinePresentations = new ArrayList<>();
                 mFieldInlineTooltipPresentations = new ArrayList<>();
                 mFieldFilters = new ArrayList<>();
+                mAutofillDatatypes = null;
             }
             mFieldIds.add(id);
             mFieldValues.add(value);
@@ -1007,9 +1094,14 @@
         public @NonNull Dataset build() {
             throwIfDestroyed();
             mDestroyed = true;
-            if (mFieldIds == null) {
+            if (mFieldIds == null && mAutofillDatatypes == null) {
                 throw new IllegalStateException("at least one value must be set");
             }
+            if (mFieldIds != null && mAutofillDatatypes != null) {
+                if (mFieldIds.size() > 0 && mAutofillDatatypes.size() > 0) {
+                    throw new IllegalStateException("both field and datatype were populated");
+                }
+            }
             if (mFieldContent != null) {
                 if (mFieldIds.size() > 1) {
                     throw new IllegalStateException(
@@ -1051,6 +1143,7 @@
         parcel.writeTypedList(mFieldInlinePresentations, flags);
         parcel.writeTypedList(mFieldInlineTooltipPresentations, flags);
         parcel.writeTypedList(mFieldFilters, flags);
+        parcel.writeStringList(mAutofillDatatypes);
         parcel.writeParcelable(mFieldContent, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeString(mId);
@@ -1081,6 +1174,8 @@
                     parcel.createTypedArrayList(InlinePresentation.CREATOR);
             final ArrayList<DatasetFieldFilter> filters =
                     parcel.createTypedArrayList(DatasetFieldFilter.CREATOR);
+            final ArrayList<String> datatypes =
+                    parcel.createStringArrayList();
             final ClipData fieldContent = parcel.readParcelable(null,
                     android.content.ClipData.class);
             final IntentSender authentication = parcel.readParcelable(null,
@@ -1114,19 +1209,37 @@
                 builder.setContent(ids.get(0), fieldContent);
             }
             final int inlinePresentationsSize = inlinePresentations.size();
-            for (int i = 0; i < ids.size(); i++) {
-                final AutofillId id = ids.get(i);
-                final AutofillValue value = values.get(i);
-                final RemoteViews fieldPresentation = presentations.get(i);
-                final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
-                final InlinePresentation fieldInlinePresentation =
-                        i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
-                final InlinePresentation fieldInlineTooltipPresentation =
-                        i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
-                final DatasetFieldFilter filter = filters.get(i);
-                builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
-                        fieldInlinePresentation, fieldInlineTooltipPresentation, filter,
-                        fieldDialogPresentation);
+
+            if (ids.size() == 0 && datatypes.size() > 0) {
+                for (int i = 0; i < ids.size(); i++) {
+                    final String datatype = datatypes.get(i);
+                    final AutofillValue value = values.get(i);
+                    final RemoteViews fieldPresentation = presentations.get(i);
+                    final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
+                    final InlinePresentation fieldInlinePresentation =
+                            i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
+                    final InlinePresentation fieldInlineTooltipPresentation =
+                            i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
+                    final DatasetFieldFilter filter = filters.get(i);
+                    builder.setLifeTheUniverseAndEverything(
+                            datatype, value, fieldPresentation, fieldInlinePresentation,
+                            fieldInlineTooltipPresentation, filter, fieldDialogPresentation);
+                }
+            } else {
+                for (int i = 0; i < ids.size(); i++) {
+                    final AutofillId id = ids.get(i);
+                    final AutofillValue value = values.get(i);
+                    final RemoteViews fieldPresentation = presentations.get(i);
+                    final RemoteViews fieldDialogPresentation = dialogPresentations.get(i);
+                    final InlinePresentation fieldInlinePresentation =
+                            i < inlinePresentationsSize ? inlinePresentations.get(i) : null;
+                    final InlinePresentation fieldInlineTooltipPresentation =
+                            i < inlinePresentationsSize ? inlineTooltipPresentations.get(i) : null;
+                    final DatasetFieldFilter filter = filters.get(i);
+                    builder.setLifeTheUniverseAndEverything(id, value, fieldPresentation,
+                            fieldInlinePresentation, fieldInlineTooltipPresentation, filter,
+                            fieldDialogPresentation);
+                }
             }
             builder.setAuthentication(authentication);
             builder.setId(datasetId);
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index eb5e893..4a848dd 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -141,6 +141,19 @@
     private final @NonNull List<FillContext> mFillContexts;
 
     /**
+     * Sends a list of datatypes for the Autofill Provider.
+     *
+     * If this is populated, Autofill Provider should return data
+     * for the autofill hints requested here,
+     * even though the Autofill Provider may not have detected these types.
+     * The hints would be part of HintConstants:
+     * https://developer.android.com/reference/androidx/autofill/HintConstants
+     *
+     * This is populated if the platform's field detection is enabled.
+     */
+    private final @NonNull List<String> mHints;
+
+    /**
      * Gets the latest client state bundle set by the service in a
      * {@link FillResponse.Builder#setClientState(Bundle) fill response}.
      *
@@ -196,6 +209,7 @@
 
     private void onConstructed() {
         Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts");
+        Preconditions.checkCollectionElementsNotNull(mHints, "hints");
     }
 
 
@@ -269,6 +283,11 @@
      *   <p><b>Note:</b> Starting on Android {@link android.os.Build.VERSION_CODES#Q}, it could also
      *   include contexts from requests whose {@link SaveInfo} had the
      *   {@link SaveInfo#FLAG_DELAY_SAVE} flag.
+     * @param hints
+     *   Autofill Provider should return data for the autofill hints requested here,
+     *   even though the Autofill Provider may not have detected these types.
+     *   The hints would be part of HintConstants:
+     *   https://developer.android.com/reference/androidx/autofill/HintConstants
      * @param clientState
      *   Gets the latest client state bundle set by the service in a
      *   {@link FillResponse.Builder#setClientState(Bundle) fill response}.
@@ -312,6 +331,7 @@
     public FillRequest(
             int id,
             @NonNull List<FillContext> fillContexts,
+            @NonNull List<String> hints,
             @Nullable Bundle clientState,
             @RequestFlags int flags,
             @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
@@ -320,6 +340,9 @@
         this.mFillContexts = fillContexts;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mFillContexts);
+        this.mHints = hints;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHints);
         this.mClientState = clientState;
         this.mFlags = flags;
 
@@ -360,6 +383,17 @@
     }
 
     /**
+     * Autofill Provider should return data for the autofill hints requested here,
+     * even though the Autofill Provider may not have detected these types.
+     * The hints would be part of HintConstants:
+     * https://developer.android.com/reference/androidx/autofill/HintConstants
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getHints() {
+        return mHints;
+    }
+
+    /**
      * Gets the latest client state bundle set by the service in a
      * {@link FillResponse.Builder#setClientState(Bundle) fill response}.
      *
@@ -433,6 +467,7 @@
         return "FillRequest { " +
                 "id = " + mId + ", " +
                 "fillContexts = " + mFillContexts + ", " +
+                "hints = " + mHints + ", " +
                 "clientState = " + mClientState + ", " +
                 "flags = " + requestFlagsToString(mFlags) + ", " +
                 "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " +
@@ -447,12 +482,13 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mClientState != null) flg |= 0x4;
-        if (mInlineSuggestionsRequest != null) flg |= 0x10;
-        if (mDelayedFillIntentSender != null) flg |= 0x20;
+        if (mClientState != null) flg |= 0x8;
+        if (mInlineSuggestionsRequest != null) flg |= 0x20;
+        if (mDelayedFillIntentSender != null) flg |= 0x40;
         dest.writeByte(flg);
         dest.writeInt(mId);
         dest.writeParcelableList(mFillContexts, flags);
+        dest.writeStringList(mHints);
         if (mClientState != null) dest.writeBundle(mClientState);
         dest.writeInt(mFlags);
         if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags);
@@ -474,15 +510,20 @@
         int id = in.readInt();
         List<FillContext> fillContexts = new ArrayList<>();
         in.readParcelableList(fillContexts, FillContext.class.getClassLoader());
-        Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle();
+        List<String> hints = new ArrayList<>();
+        in.readStringList(hints);
+        Bundle clientState = (flg & 0x8) == 0 ? null : in.readBundle();
         int flags = in.readInt();
-        InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
-        IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR);
+        InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x20) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR);
+        IntentSender delayedFillIntentSender = (flg & 0x40) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR);
 
         this.mId = id;
         this.mFillContexts = fillContexts;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mFillContexts);
+        this.mHints = hints;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHints);
         this.mClientState = clientState;
         this.mFlags = flags;
 
@@ -517,10 +558,10 @@
     };
 
     @DataClass.Generated(
-            time = 1675460688829L,
+            time = 1675711417112L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java",
-            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
+            inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_SUPPORTS_FILL_DIALOG\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_IME_SHOWING\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_RESET_FILL_DIALOG_STATE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PCC_DETECTION\npublic static final  int INVALID_REQUEST_ID\nprivate final  int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mHints\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate  void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 385b0aa..fa7ace3 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -34,6 +34,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.service.assist.classification.FieldClassification;
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
 
@@ -45,6 +46,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Response for an {@link
@@ -113,6 +115,7 @@
     private final @StringRes int mServiceDisplayNameResourceId;
     private final boolean mShowFillDialogIcon;
     private final boolean mShowSaveDialogIcon;
+    private final @Nullable FieldClassification[] mDetectedFieldTypes;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -140,6 +143,14 @@
         mServiceDisplayNameResourceId = builder.mServiceDisplayNameResourceId;
         mShowFillDialogIcon = builder.mShowFillDialogIcon;
         mShowSaveDialogIcon = builder.mShowSaveDialogIcon;
+        mDetectedFieldTypes = builder.mDetectedFieldTypes;
+    }
+
+    /** @hide */
+    @TestApi
+    @NonNull
+    public Set<FieldClassification> getDetectedFieldClassifications() {
+        return Set.of(mDetectedFieldTypes);
     }
 
     /** @hide */
@@ -312,6 +323,28 @@
         private int mServiceDisplayNameResourceId;
         private boolean mShowFillDialogIcon = true;
         private boolean mShowSaveDialogIcon = true;
+        private FieldClassification[] mDetectedFieldTypes;
+
+        /**
+         * Adds a new {@link FieldClassification} to this response, to
+         * help the platform provide more accurate detection results.
+         *
+         * Call this when a field has been detected with a type.
+         *
+         * Altough similiarly named with {@link setFieldClassificationIds},
+         * it provides a different functionality - setFieldClassificationIds should
+         * be used when a field is only suspected to be Autofillable.
+         * This method should be used when a field is certainly Autofillable
+         * with a certain type.
+         */
+        @NonNull
+        public Builder setDetectedFieldClassifications(
+                @NonNull Set<FieldClassification> fieldInfos) {
+            throwIfDestroyed();
+            throwIfDisableAutofillCalled();
+            mDetectedFieldTypes = fieldInfos.toArray(new FieldClassification[0]);
+            return this;
+        }
 
         /**
          * Triggers a custom UI before autofilling the screen with any data set in this
@@ -1122,6 +1155,7 @@
         parcel.writeParcelableArray(mIgnoredIds, flags);
         parcel.writeLong(mDisableDuration);
         parcel.writeParcelableArray(mFieldClassificationIds, flags);
+        parcel.writeParcelableArray(mDetectedFieldTypes, flags);
         parcel.writeInt(mIconResourceId);
         parcel.writeInt(mServiceDisplayNameResourceId);
         parcel.writeBoolean(mShowFillDialogIcon);
@@ -1192,6 +1226,12 @@
                 builder.setFieldClassificationIds(fieldClassifactionIds);
             }
 
+            final FieldClassification[] detectedFields =
+                    parcel.readParcelableArray(null, FieldClassification.class);
+            if (detectedFields != null) {
+                builder.setDetectedFieldClassifications(Set.of(detectedFields));
+            }
+
             builder.setIconResourceId(parcel.readInt());
             builder.setServiceDisplayNameResourceId(parcel.readInt());
             builder.setShowFillDialogIcon(parcel.readBoolean());
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 5fe1d4f..828e466 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -308,7 +308,7 @@
      * username field, another for password).
      */
     // TODO(b/113281366): improve documentation: add example, document relationship with other
-    // flagss, etc...
+    // flags, etc...
     public static final int FLAG_DELAY_SAVE = 0x4;
 
     /** @hide */
@@ -777,17 +777,13 @@
         /**
          * Builds a new {@link SaveInfo} instance.
          *
-         * @throws IllegalStateException if no
-         * {@link #Builder(int, AutofillId[]) required ids},
+         * If no {@link #Builder(int, AutofillId[]) required ids},
          * or {@link #setOptionalIds(AutofillId[]) optional ids}, or {@link #FLAG_DELAY_SAVE}
-         * were set
+         * were set, Save Dialog will only be triggered if platform detection is enabled, which
+         * is indicated when {@link FillRequest.getHints()} is not empty.
          */
         public SaveInfo build() {
             throwIfDestroyed();
-            Preconditions.checkState(
-                    !ArrayUtils.isEmpty(mRequiredIds) || !ArrayUtils.isEmpty(mOptionalIds)
-                            || (mFlags & FLAG_DELAY_SAVE) != 0,
-                    "must have at least one required or optional id or FLAG_DELAYED_SAVE");
             mDestroyed = true;
             return new SaveInfo(this);
         }
diff --git a/core/java/android/service/credentials/BeginCreateCredentialResponse.java b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
index 8ca3a1a..f0f954d 100644
--- a/core/java/android/service/credentials/BeginCreateCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginCreateCredentialResponse.java
@@ -34,6 +34,14 @@
     private final @NonNull List<CreateEntry> mCreateEntries;
     private final @Nullable CreateEntry mRemoteCreateEntry;
 
+    /**
+     * Creates an empty response instance, to be used when there are no {@link CreateEntry}
+     * to return.
+     */
+    public BeginCreateCredentialResponse() {
+        this(/*createEntries=*/new ArrayList<>(), /*remoteCreateEntry=*/null);
+    }
+
     private BeginCreateCredentialResponse(@NonNull Parcel in) {
         List<CreateEntry> createEntries = new ArrayList<>();
         in.readTypedList(createEntries, CreateEntry.CREATOR);
@@ -137,13 +145,8 @@
 
         /**
          * Builds a new instance of {@link BeginCreateCredentialResponse}.
-         *
-         * @throws NullPointerException If {@code createEntries} is null.
-         * @throws IllegalArgumentException If {@code createEntries} is empty.
          */
         public @NonNull BeginCreateCredentialResponse build() {
-            Preconditions.checkCollectionNotEmpty(mCreateEntries, "createEntries must "
-                    + "not be null, or empty");
             return new BeginCreateCredentialResponse(mCreateEntries, mRemoteCreateEntry);
         }
     }
diff --git a/core/java/android/service/credentials/BeginGetCredentialOption.java b/core/java/android/service/credentials/BeginGetCredentialOption.java
index 1df908a..3d39f65 100644
--- a/core/java/android/service/credentials/BeginGetCredentialOption.java
+++ b/core/java/android/service/credentials/BeginGetCredentialOption.java
@@ -39,6 +39,8 @@
  */
 @SuppressLint("ParcelNotFinal")
 public class BeginGetCredentialOption implements Parcelable {
+    private static final String BUNDLE_ID_KEY =
+            "android.service.credentials.BeginGetCredentialOption.BUNDLE_ID_KEY";
     /**
      * A unique id associated with this request option.
      */
@@ -129,6 +131,11 @@
         mType = Preconditions.checkStringNotEmpty(type, "type must not be empty");
         mCandidateQueryData = requireNonNull(
                 candidateQueryData, "candidateQueryData must not be null");
+        addIdToBundle();
+    }
+
+    private void addIdToBundle() {
+        mCandidateQueryData.putString(BUNDLE_ID_KEY, mId);
     }
 
     private BeginGetCredentialOption(@NonNull Parcel in) {
diff --git a/core/java/android/service/credentials/BeginGetCredentialResponse.java b/core/java/android/service/credentials/BeginGetCredentialResponse.java
index 0f64c63..3652742 100644
--- a/core/java/android/service/credentials/BeginGetCredentialResponse.java
+++ b/core/java/android/service/credentials/BeginGetCredentialResponse.java
@@ -44,6 +44,17 @@
     /** Remote credential entry to get the response from a different device. */
     private final @Nullable CredentialEntry mRemoteCredentialEntry;
 
+    /**
+     * Creates an empty response instance, to be used when there are no {@link CredentialEntry},
+     * or {@link Action} to return.
+     */
+    public BeginGetCredentialResponse() {
+        this(/*credentialEntries=*/new ArrayList<>(),
+                /*authenticationActions=*/new ArrayList<>(),
+                /*actions=*/new ArrayList<>(),
+                /*remoteCredentialEntry=*/null);
+    }
+
     private BeginGetCredentialResponse(@NonNull List<CredentialEntry> credentialEntries,
             @NonNull List<Action> authenticationEntries, @NonNull List<Action> actions,
             @Nullable CredentialEntry remoteCredentialEntry) {
@@ -243,16 +254,8 @@
 
         /**
          * Builds a {@link BeginGetCredentialResponse} instance.
-         *
-         * @throws IllegalStateException if {@code credentialEntries}, {@code actions}
-         *                               and {@code remoteCredentialEntry} are all null or empty.
          */
         public @NonNull BeginGetCredentialResponse build() {
-            if (mCredentialEntries.isEmpty() && mActions.isEmpty()
-                    && mRemoteCredentialEntry == null && mAuthenticationEntries.isEmpty()) {
-                throw new IllegalStateException("must set either an authentication, "
-                        + "credential, action or remote entry");
-            }
             return new BeginGetCredentialResponse(mCredentialEntries, mAuthenticationEntries,
                     mActions, mRemoteCredentialEntry);
         }
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index c47fdd3..270f848 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -39,7 +39,7 @@
      * @param options A Bundle of private arguments to the current voice interaction service
      */
     public abstract void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
-            @Nullable String attributionTag, @NonNull Bundle options);
+            @Nullable String attributionTag, @Nullable Bundle options);
 
     /**
      * Returns whether the currently selected voice interaction service supports local voice
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 0d51395..5778518 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1781,11 +1781,13 @@
      * Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing
      * in milliseconds of the KeyEvent that triggered Assistant and
      * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
-     *  referring to the device that sent the request.
+     *  referring to the device that sent the request. Starting from Android 14, the system will
+     * add {@link VoiceInteractionService#KEY_SHOW_SESSION_ID}, the Bundle is not null. But the
+     * application should handle null case before Android 14.
      * @param showFlags The show flags originally provided to
      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
      */
-    public void onShow(Bundle args, int showFlags) {
+    public void onShow(@Nullable Bundle args, int showFlags) {
     }
 
     /**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8663013..7da141b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -479,6 +479,14 @@
 
     private WeakReference<View> mLocalOwnerView;
 
+    // A throwable with the stack filled when this SurfaceControl is released (only if
+    // sDebugUsageAfterRelease) is enabled
+    private Throwable mReleaseStack = null;
+
+    // Triggers the stack to be saved when any SurfaceControl in this process is released, which can
+    // be dumped as additional context
+    private static volatile boolean sDebugUsageAfterRelease = false;
+
     static GlobalTransactionWrapper sGlobalTransaction;
     static long sTransactionNestCount = 0;
 
@@ -751,6 +759,11 @@
         }
         mNativeObject = nativeObject;
         mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
+        if (sDebugUsageAfterRelease && mNativeObject == 0) {
+            mReleaseStack = new Throwable("Assigned invalid nativeObject");
+        } else {
+            mReleaseStack = null;
+        }
     }
 
     /**
@@ -1246,6 +1259,9 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        if (sDebugUsageAfterRelease) {
+            checkNotReleased();
+        }
         dest.writeString8(mName);
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
@@ -1262,6 +1278,18 @@
     }
 
     /**
+     * Enables additional debug logs to track usage-after-release of all SurfaceControls in this
+     * process.
+     * @hide
+     */
+    public static void setDebugUsageAfterRelease(boolean debug) {
+        if (!Build.isDebuggable()) {
+            return;
+        }
+        sDebugUsageAfterRelease = debug;
+    }
+
+    /**
      * Checks whether two {@link SurfaceControl} objects represent the same surface.
      *
      * @param other The other object to check
@@ -1382,6 +1410,9 @@
             mFreeNativeResources.run();
             mNativeObject = 0;
             mNativeHandle = 0;
+            if (sDebugUsageAfterRelease) {
+                mReleaseStack = new Throwable("Released");
+            }
             mCloseGuard.close();
             synchronized (mChoreographerLock) {
                 if (mChoreographer != null) {
@@ -1403,8 +1434,15 @@
     }
 
     private void checkNotReleased() {
-        if (mNativeObject == 0) throw new NullPointerException(
-                "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
+        if (mNativeObject == 0) {
+            if (mReleaseStack != null) {
+                throw new IllegalStateException("Invalid usage after release of " + this,
+                        mReleaseStack);
+            } else {
+                throw new NullPointerException("mNativeObject of " + this
+                        + " is null. Have you called release() already?");
+            }
+        }
     }
 
     /**
@@ -2738,7 +2776,7 @@
          */
         @NonNull
         public Transaction setFrameRateSelectionPriority(@NonNull SurfaceControl sc, int priority) {
-            sc.checkNotReleased();
+            checkPreconditions(sc);
             nativeSetFrameRateSelectionPriority(mNativeObject, sc.mNativeObject, priority);
             return this;
         }
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index e7c610b..864ba92 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -82,6 +82,12 @@
             "autofill_dialog_enabled";
 
     /**
+     * Indicates that PCC Autofill detection feature is enabled or not.
+     */
+    public static final String DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS =
+            "pcc_classification_hints";
+
+    /**
      * Sets the autofill hints allowed list for the fields that can trigger the fill dialog
      * feature at Activity starting.
      *
@@ -178,6 +184,22 @@
     public static final String DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED =
             "pcc_classification_enabled";
 
+    /**
+     * Give preference to autofill provider's detection.
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC = "prefer_provider_over_pcc";
+
+
+    /**
+     * Use data from secondary source if primary not present .
+     * For eg: if we prefer PCC over provider, and PCC detection didn't classify a field, however,
+     * autofill provider did, this flag would decide whether we use that result, and show some
+     * presentation for that particular field.
+     * @hide
+     */
+    public static final String DEVICE_CONFIG_PCC_USE_FALLBACK = "pcc_use_fallback";
+
     // END AUTOFILL PCC CLASSIFICATION FLAGS
 
 
@@ -190,9 +212,11 @@
             "autofill_inline_tooltip_first_show_delay";
 
     private static final String DIALOG_HINTS_DELIMITER = ":";
+    private static final String PCC_HINTS_DELIMITER = ",";
 
     private static final boolean DEFAULT_HAS_FILL_DIALOG_UI_FEATURE = false;
     private static final String DEFAULT_FILL_DIALOG_ENABLED_HINTS = "";
+    private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = "";
 
 
     // CREDENTIAL MANAGER DEFAULTS
@@ -206,7 +230,8 @@
 
     // AUTOFILL PCC CLASSIFICATION FLAGS DEFAULTS
     // Default for whether the pcc classification is enabled for autofill.
-    private static final boolean DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED = false;
+    /** @hide */
+    public static final boolean DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED = false;
     // END AUTOFILL PCC CLASSIFICATION FLAGS DEFAULTS
 
 
@@ -225,6 +250,25 @@
     }
 
     /**
+     * The list of datatypes that is supported by framework
+     * detection.
+     *
+     * @hide
+     */
+    public static String[] getTypeHintsForProvider() {
+        final String typeHints = DeviceConfig.getString(
+                DeviceConfig.NAMESPACE_AUTOFILL,
+                DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS,
+                DEFAULT_PCC_FEATURE_PROVIDER_HINTS);
+        if (TextUtils.isEmpty(typeHints)) {
+            return new String[0];
+        }
+
+        return ArrayUtils.filter(typeHints.split(PCC_HINTS_DELIMITER), String[]::new,
+                (str) -> !TextUtils.isEmpty(str));
+    }
+
+    /**
      * Gets fill dialog enabled hints.
      *
      * @hide
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 136846a..3f452f8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -124,13 +124,11 @@
 import android.view.animation.LinearInterpolator;
 import android.view.inputmethod.CorrectionInfo;
 import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorBoundsInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.TextAppearanceInfo;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationManager;
 import android.widget.AdapterView.OnItemClickListener;
@@ -4667,7 +4665,7 @@
      * {@link InputMethodManager#isWatchingCursor(View)} returns false.
      */
     private final class CursorAnchorInfoNotifier implements TextViewPositionListener {
-        final CursorAnchorInfo.Builder mSelectionInfoBuilder = new CursorAnchorInfo.Builder();
+        final CursorAnchorInfo.Builder mCursorAnchorInfoBuilder = new CursorAnchorInfo.Builder();
         final Matrix mViewToScreenMatrix = new Matrix();
 
         @Override
@@ -4687,165 +4685,21 @@
             // Skip if the IME has not requested the cursor/anchor position.
             final int knownCursorAnchorInfoModes =
                     InputConnection.CURSOR_UPDATE_IMMEDIATE | InputConnection.CURSOR_UPDATE_MONITOR;
-            if ((mInputMethodState.mUpdateCursorAnchorInfoMode & knownCursorAnchorInfoModes) == 0) {
+            if ((ims.mUpdateCursorAnchorInfoMode & knownCursorAnchorInfoModes) == 0) {
                 return;
             }
-            Layout layout = mTextView.getLayout();
-            if (layout == null) {
-                return;
+
+            final CursorAnchorInfo cursorAnchorInfo =
+                    mTextView.getCursorAnchorInfo(ims.mUpdateCursorAnchorInfoFilter,
+                            mCursorAnchorInfoBuilder, mViewToScreenMatrix);
+
+            if (cursorAnchorInfo != null) {
+                imm.updateCursorAnchorInfo(mTextView, cursorAnchorInfo);
+
+                // Drop the immediate flag if any.
+                mInputMethodState.mUpdateCursorAnchorInfoMode &=
+                        ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
             }
-            final int filter = mInputMethodState.mUpdateCursorAnchorInfoFilter;
-            boolean includeEditorBounds =
-                    (filter & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0;
-            boolean includeCharacterBounds =
-                    (filter & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0;
-            boolean includeInsertionMarker =
-                    (filter & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0;
-            boolean includeVisibleLineBounds =
-                    (filter & InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS) != 0;
-            boolean includeTextAppearance =
-                    (filter & InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE) != 0;
-            boolean includeAll =
-                    (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker
-                    && !includeVisibleLineBounds && !includeTextAppearance);
-
-            includeEditorBounds |= includeAll;
-            includeCharacterBounds |= includeAll;
-            includeInsertionMarker |= includeAll;
-            includeVisibleLineBounds |= includeAll;
-            includeTextAppearance |= includeAll;
-
-            final CursorAnchorInfo.Builder builder = mSelectionInfoBuilder;
-            builder.reset();
-
-            final int selectionStart = mTextView.getSelectionStart();
-            builder.setSelectionRange(selectionStart, mTextView.getSelectionEnd());
-
-            // Construct transformation matrix from view local coordinates to screen coordinates.
-            mViewToScreenMatrix.reset();
-            mTextView.transformMatrixToGlobal(mViewToScreenMatrix);
-            builder.setMatrix(mViewToScreenMatrix);
-
-            if (includeEditorBounds) {
-                final RectF editorBounds = new RectF();
-                editorBounds.set(0 /* left */, 0 /* top */,
-                        mTextView.getWidth(), mTextView.getHeight());
-                final RectF handwritingBounds = new RectF(
-                        -mTextView.getHandwritingBoundsOffsetLeft(),
-                        -mTextView.getHandwritingBoundsOffsetTop(),
-                        mTextView.getWidth() + mTextView.getHandwritingBoundsOffsetRight(),
-                        mTextView.getHeight() + mTextView.getHandwritingBoundsOffsetBottom());
-                EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
-                EditorBoundsInfo editorBoundsInfo = boundsBuilder.setEditorBounds(editorBounds)
-                        .setHandwritingBounds(handwritingBounds).build();
-                builder.setEditorBoundsInfo(editorBoundsInfo);
-            }
-
-            if (includeCharacterBounds || includeInsertionMarker || includeVisibleLineBounds) {
-                final float viewportToContentHorizontalOffset =
-                        mTextView.viewportToContentHorizontalOffset();
-                final float viewportToContentVerticalOffset =
-                        mTextView.viewportToContentVerticalOffset();
-                final boolean isTextTransformed = (mTextView.getTransformationMethod() != null
-                        && mTextView.getTransformed() instanceof OffsetMapping);
-                if (includeCharacterBounds && !isTextTransformed) {
-                    final CharSequence text = mTextView.getText();
-                    if (text instanceof Spannable) {
-                        final Spannable sp = (Spannable) text;
-                        int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
-                        int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
-                        if (composingTextEnd < composingTextStart) {
-                            final int temp = composingTextEnd;
-                            composingTextEnd = composingTextStart;
-                            composingTextStart = temp;
-                        }
-                        final boolean hasComposingText =
-                                (0 <= composingTextStart) && (composingTextStart
-                                        < composingTextEnd);
-                        if (hasComposingText) {
-                            final CharSequence composingText = text.subSequence(composingTextStart,
-                                    composingTextEnd);
-                            builder.setComposingText(composingTextStart, composingText);
-                            mTextView.populateCharacterBounds(builder, composingTextStart,
-                                    composingTextEnd, viewportToContentHorizontalOffset,
-                                    viewportToContentVerticalOffset);
-                        }
-                    }
-                }
-
-                if (includeInsertionMarker) {
-                    // Treat selectionStart as the insertion point.
-                    if (0 <= selectionStart) {
-                        final int offsetTransformed = mTextView.originalToTransformed(
-                                selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR);
-                        final int line = layout.getLineForOffset(offsetTransformed);
-                        final float insertionMarkerX =
-                                layout.getPrimaryHorizontal(offsetTransformed)
-                                        + viewportToContentHorizontalOffset;
-                        final float insertionMarkerTop = layout.getLineTop(line)
-                                + viewportToContentVerticalOffset;
-                        final float insertionMarkerBaseline = layout.getLineBaseline(line)
-                                + viewportToContentVerticalOffset;
-                        final float insertionMarkerBottom =
-                                layout.getLineBottom(line, /* includeLineSpacing= */ false)
-                                        + viewportToContentVerticalOffset;
-                        final boolean isTopVisible = mTextView
-                                .isPositionVisible(insertionMarkerX, insertionMarkerTop);
-                        final boolean isBottomVisible = mTextView
-                                .isPositionVisible(insertionMarkerX, insertionMarkerBottom);
-                        int insertionMarkerFlags = 0;
-                        if (isTopVisible || isBottomVisible) {
-                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
-                        }
-                        if (!isTopVisible || !isBottomVisible) {
-                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
-                        }
-                        if (layout.isRtlCharAt(offsetTransformed)) {
-                            insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
-                        }
-                        builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
-                                insertionMarkerBaseline, insertionMarkerBottom,
-                                insertionMarkerFlags);
-                    }
-                }
-
-                if (includeVisibleLineBounds) {
-                    final Rect visibleRect = new Rect();
-                    if (mTextView.getContentVisibleRect(visibleRect)) {
-                        // Subtract the viewportToContentVerticalOffset to convert the view
-                        // coordinates to layout coordinates.
-                        final float visibleTop =
-                                visibleRect.top - viewportToContentVerticalOffset;
-                        final float visibleBottom =
-                                visibleRect.bottom - viewportToContentVerticalOffset;
-                        final int firstLine =
-                                layout.getLineForVertical((int) Math.floor(visibleTop));
-                        final int lastLine =
-                                layout.getLineForVertical((int) Math.ceil(visibleBottom));
-
-                        for (int line = firstLine; line <= lastLine; ++line) {
-                            final float left = layout.getLineLeft(line)
-                                    + viewportToContentHorizontalOffset;
-                            final float top = layout.getLineTop(line)
-                                    + viewportToContentVerticalOffset;
-                            final float right = layout.getLineRight(line)
-                                    + viewportToContentHorizontalOffset;
-                            final float bottom = layout.getLineBottom(line, false)
-                                    + viewportToContentVerticalOffset;
-                            builder.addVisibleLineBounds(left, top, right, bottom);
-                        }
-                    }
-                }
-            }
-
-            if (includeTextAppearance) {
-                builder.setTextAppearanceInfo(TextAppearanceInfo.createFromTextView(mTextView));
-            }
-            imm.updateCursorAnchorInfo(mTextView, builder.build());
-
-            // Drop the immediate flag if any.
-            mInputMethodState.mUpdateCursorAnchorInfoMode &=
-                    ~InputConnection.CURSOR_UPDATE_IMMEDIATE;
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 77df1f1..626df48 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -196,6 +196,7 @@
 import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.DeleteGesture;
 import android.view.inputmethod.DeleteRangeGesture;
+import android.view.inputmethod.EditorBoundsInfo;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -209,6 +210,7 @@
 import android.view.inputmethod.RemoveSpaceGesture;
 import android.view.inputmethod.SelectGesture;
 import android.view.inputmethod.SelectRangeGesture;
+import android.view.inputmethod.TextAppearanceInfo;
 import android.view.inputmethod.TextBoundsInfo;
 import android.view.inspector.InspectableProperty;
 import android.view.inspector.InspectableProperty.EnumEntry;
@@ -13658,7 +13660,7 @@
      * @return true if at least part of the text content is visible; false if the text content is
      * completely clipped or translated out of the visible area.
      */
-    boolean getContentVisibleRect(Rect rect) {
+    private boolean getContentVisibleRect(Rect rect) {
         if (!getLocalVisibleRect(rect)) {
             return false;
         }
@@ -13744,6 +13746,176 @@
     }
 
     /**
+     * Compute {@link CursorAnchorInfo} from this {@link TextView}.
+     *
+     * @param filter the {@link CursorAnchorInfo} update filter which specified the needed
+     *               information from IME.
+     * @param cursorAnchorInfoBuilder a cached {@link CursorAnchorInfo.Builder} object used to build
+     *                                the result {@link CursorAnchorInfo}.
+     * @param viewToScreenMatrix a cached {@link Matrix} object used to compute the view to screen
+     *                           matrix.
+     * @return the result {@link CursorAnchorInfo} to be passed to IME.
+     * @hide
+     */
+    @VisibleForTesting
+    @Nullable
+    public CursorAnchorInfo getCursorAnchorInfo(@InputConnection.CursorUpdateFilter int filter,
+            @NonNull CursorAnchorInfo.Builder cursorAnchorInfoBuilder,
+            @NonNull Matrix viewToScreenMatrix) {
+        Layout layout = getLayout();
+        if (layout == null) {
+            return null;
+        }
+        boolean includeEditorBounds =
+                (filter & InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS) != 0;
+        boolean includeCharacterBounds =
+                (filter & InputConnection.CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS) != 0;
+        boolean includeInsertionMarker =
+                (filter & InputConnection.CURSOR_UPDATE_FILTER_INSERTION_MARKER) != 0;
+        boolean includeVisibleLineBounds =
+                (filter & InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS) != 0;
+        boolean includeTextAppearance =
+                (filter & InputConnection.CURSOR_UPDATE_FILTER_TEXT_APPEARANCE) != 0;
+        boolean includeAll =
+                (!includeEditorBounds && !includeCharacterBounds && !includeInsertionMarker
+                        && !includeVisibleLineBounds && !includeTextAppearance);
+
+        includeEditorBounds |= includeAll;
+        includeCharacterBounds |= includeAll;
+        includeInsertionMarker |= includeAll;
+        includeVisibleLineBounds |= includeAll;
+        includeTextAppearance |= includeAll;
+
+        final CursorAnchorInfo.Builder builder = cursorAnchorInfoBuilder;
+        builder.reset();
+
+        final int selectionStart = getSelectionStart();
+        builder.setSelectionRange(selectionStart, getSelectionEnd());
+
+        // Construct transformation matrix from view local coordinates to screen coordinates.
+        viewToScreenMatrix.reset();
+        transformMatrixToGlobal(viewToScreenMatrix);
+        builder.setMatrix(viewToScreenMatrix);
+
+        if (includeEditorBounds) {
+            final RectF editorBounds = new RectF();
+            editorBounds.set(0 /* left */, 0 /* top */,
+                    getWidth(), getHeight());
+            final RectF handwritingBounds = new RectF(
+                    -getHandwritingBoundsOffsetLeft(),
+                    -getHandwritingBoundsOffsetTop(),
+                    getWidth() + getHandwritingBoundsOffsetRight(),
+                    getHeight() + getHandwritingBoundsOffsetBottom());
+            EditorBoundsInfo.Builder boundsBuilder = new EditorBoundsInfo.Builder();
+            EditorBoundsInfo editorBoundsInfo = boundsBuilder.setEditorBounds(editorBounds)
+                    .setHandwritingBounds(handwritingBounds).build();
+            builder.setEditorBoundsInfo(editorBoundsInfo);
+        }
+
+        if (includeCharacterBounds || includeInsertionMarker || includeVisibleLineBounds) {
+            final float viewportToContentHorizontalOffset =
+                    viewportToContentHorizontalOffset();
+            final float viewportToContentVerticalOffset =
+                    viewportToContentVerticalOffset();
+            final boolean isTextTransformed = (getTransformationMethod() != null
+                    && getTransformed() instanceof OffsetMapping);
+            if (includeCharacterBounds && !isTextTransformed) {
+                final CharSequence text = getText();
+                if (text instanceof Spannable) {
+                    final Spannable sp = (Spannable) text;
+                    int composingTextStart = EditableInputConnection.getComposingSpanStart(sp);
+                    int composingTextEnd = EditableInputConnection.getComposingSpanEnd(sp);
+                    if (composingTextEnd < composingTextStart) {
+                        final int temp = composingTextEnd;
+                        composingTextEnd = composingTextStart;
+                        composingTextStart = temp;
+                    }
+                    final boolean hasComposingText =
+                            (0 <= composingTextStart) && (composingTextStart
+                                    < composingTextEnd);
+                    if (hasComposingText) {
+                        final CharSequence composingText = text.subSequence(composingTextStart,
+                                composingTextEnd);
+                        builder.setComposingText(composingTextStart, composingText);
+                        populateCharacterBounds(builder, composingTextStart,
+                                composingTextEnd, viewportToContentHorizontalOffset,
+                                viewportToContentVerticalOffset);
+                    }
+                }
+            }
+
+            if (includeInsertionMarker) {
+                // Treat selectionStart as the insertion point.
+                if (0 <= selectionStart) {
+                    final int offsetTransformed = originalToTransformed(
+                            selectionStart, OffsetMapping.MAP_STRATEGY_CURSOR);
+                    final int line = layout.getLineForOffset(offsetTransformed);
+                    final float insertionMarkerX =
+                            layout.getPrimaryHorizontal(offsetTransformed)
+                                    + viewportToContentHorizontalOffset;
+                    final float insertionMarkerTop = layout.getLineTop(line)
+                            + viewportToContentVerticalOffset;
+                    final float insertionMarkerBaseline = layout.getLineBaseline(line)
+                            + viewportToContentVerticalOffset;
+                    final float insertionMarkerBottom =
+                            layout.getLineBottom(line, /* includeLineSpacing= */ false)
+                                    + viewportToContentVerticalOffset;
+                    final boolean isTopVisible =
+                            isPositionVisible(insertionMarkerX, insertionMarkerTop);
+                    final boolean isBottomVisible =
+                            isPositionVisible(insertionMarkerX, insertionMarkerBottom);
+                    int insertionMarkerFlags = 0;
+                    if (isTopVisible || isBottomVisible) {
+                        insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
+                    }
+                    if (!isTopVisible || !isBottomVisible) {
+                        insertionMarkerFlags |= CursorAnchorInfo.FLAG_HAS_INVISIBLE_REGION;
+                    }
+                    if (layout.isRtlCharAt(offsetTransformed)) {
+                        insertionMarkerFlags |= CursorAnchorInfo.FLAG_IS_RTL;
+                    }
+                    builder.setInsertionMarkerLocation(insertionMarkerX, insertionMarkerTop,
+                            insertionMarkerBaseline, insertionMarkerBottom,
+                            insertionMarkerFlags);
+                }
+            }
+
+            if (includeVisibleLineBounds) {
+                final Rect visibleRect = new Rect();
+                if (getContentVisibleRect(visibleRect)) {
+                    // Subtract the viewportToContentVerticalOffset to convert the view
+                    // coordinates to layout coordinates.
+                    final float visibleTop =
+                            visibleRect.top - viewportToContentVerticalOffset;
+                    final float visibleBottom =
+                            visibleRect.bottom - viewportToContentVerticalOffset;
+                    final int firstLine =
+                            layout.getLineForVertical((int) Math.floor(visibleTop));
+                    final int lastLine =
+                            layout.getLineForVertical((int) Math.ceil(visibleBottom));
+
+                    for (int line = firstLine; line <= lastLine; ++line) {
+                        final float left = layout.getLineLeft(line)
+                                + viewportToContentHorizontalOffset;
+                        final float top = layout.getLineTop(line)
+                                + viewportToContentVerticalOffset;
+                        final float right = layout.getLineRight(line)
+                                + viewportToContentHorizontalOffset;
+                        final float bottom = layout.getLineBottom(line, false)
+                                + viewportToContentVerticalOffset;
+                        builder.addVisibleLineBounds(left, top, right, bottom);
+                    }
+                }
+            }
+        }
+
+        if (includeTextAppearance) {
+            builder.setTextAppearanceInfo(TextAppearanceInfo.createFromTextView(this));
+        }
+        return builder.build();
+    }
+
+    /**
      * Creates the {@link TextBoundsInfo} for the text lines that intersects with the {@code rectF}.
      * @hide
      */
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index fc5cb4b..d4ff794 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -81,7 +81,7 @@
      *             IVoiceInteractionSessionShowCallback, IBinder)} instead
      */
     @Deprecated
-    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+    public boolean showSessionForActiveService(@Nullable Bundle args, int sourceFlags,
             @Nullable IVoiceInteractionSessionShowCallback showCallback,
             @Nullable IBinder activityToken) {
         return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
@@ -99,7 +99,7 @@
      * @param showCallback optional callback to be notified when the session was shown
      * @param activityToken optional token of activity that needs to be on top
      */
-    public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+    public boolean showSessionForActiveService(@Nullable Bundle args, int sourceFlags,
             @Nullable String attributionTag,
             @Nullable IVoiceInteractionSessionShowCallback showCallback,
             @Nullable IBinder activityToken) {
@@ -107,7 +107,7 @@
                 activityToken);
     }
 
-    private boolean showSessionForActiveServiceInternal(@NonNull Bundle args, int sourceFlags,
+    private boolean showSessionForActiveServiceInternal(@Nullable Bundle args, int sourceFlags,
             @Nullable String attributionTag,
             @Nullable IVoiceInteractionSessionShowCallback showCallback,
             @Nullable IBinder activityToken) {
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 3494c9e..a646df3 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -17,6 +17,8 @@
 package com.android.internal.widget;
 
 import android.app.PendingIntent;
+import android.app.RemoteLockscreenValidationResult;
+import android.app.StartLockscreenValidationRequest;
 import android.app.trust.IStrongAuthTracker;
 import android.os.Bundle;
 import android.security.keystore.recovery.WrappedApplicationKey;
@@ -93,6 +95,8 @@
             in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
+    StartLockscreenValidationRequest startRemoteLockscreenValidation();
+    RemoteLockscreenValidationResult validateRemoteLockscreen(in byte[] encryptedCredential);
     boolean hasSecureLockScreen();
     boolean tryUnlockWithCachedUnifiedChallenge(int userId);
     void removeCachedUnifiedChallenge(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 4d820ac..44c37dc 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -28,6 +28,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.PropertyInvalidatedCache;
+import android.app.RemoteLockscreenValidationResult;
+import android.app.StartLockscreenValidationRequest;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
@@ -1822,4 +1824,29 @@
     public void removeUser(@UserIdInt int userId) {
         getLockSettingsInternal().removeUser(userId);
     }
+
+   /**
+     * Starts a session to verify lockscreen credentials provided by a remote device.
+     */
+    @NonNull
+    public StartLockscreenValidationRequest startRemoteLockscreenValidation() {
+        try {
+            return getLockSettings().startRemoteLockscreenValidation();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+   /**
+     * Verifies credentials guess from a remote device.
+     */
+    @NonNull
+    public RemoteLockscreenValidationResult validateRemoteLockscreen(
+            @NonNull byte[] encryptedCredential) {
+        try {
+            return getLockSettings().validateRemoteLockscreen(encryptedCredential);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/proto/android/server/syncstorageengine.proto b/core/proto/android/server/syncstorageengine.proto
index 2f35a07..7fa01d8 100644
--- a/core/proto/android/server/syncstorageengine.proto
+++ b/core/proto/android/server/syncstorageengine.proto
@@ -85,4 +85,5 @@
   repeated StatusInfo status = 1;
 
   optional bool is_job_namespace_migrated = 2;
+  optional bool is_job_attribution_fixed = 3;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0b6b0a1..5b77e3e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5975,6 +5975,11 @@
     <permission android:name="android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"
                 android:protectionLevel="signature"/>
 
+    <!-- @SystemApi Allows application to verify lockscreen credentials provided by a remote device.
+         @hide -->
+    <permission android:name="android.permission.CHECK_REMOTE_LOCKSCREEN"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
     <permission android:name="android.permission.MANAGE_FINGERPRINT"
         android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3e30469..a1e18a7 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4375,6 +4375,18 @@
     -->
     <string name="config_defaultCredentialManagerHybridService" translatable="false"></string>
 
+    <!-- The component name, flattened to a string, for the system's credential manager
+      provider service. This service allows credential retrieval and storage od credentials.
+
+     This service must be trusted, as it can be activated without explicit consent of the user.
+     If no service with the specified name exists on the device, there will be no user configurable
+     provider to service credential manager requests. However, credential manager system
+     services that do not require user consent, will still work.
+
+     See android.credentials.CredentialManager
+    -->
+    <string name="config_defaultCredentialProviderService" translatable="false"></string>
+
     <!-- The package name for the system's smartspace service.
      This service returns smartspace results.
 
@@ -6266,6 +6278,9 @@
     <string-array name="config_healthConnectMigrationKnownSigners">
     </string-array>
 
+    <!-- Package name of Health Connect data migrator application.  -->
+    <string name="config_healthConnectMigratorPackageName"></string>
+
     <!-- The Universal Stylus Initiative (USI) protocol version supported by each display.
          (@see https://universalstylus.org/).
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f237ece..5ccc248 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3729,6 +3729,7 @@
   <java-symbol type="string" name="config_defaultAppPredictionService" />
   <java-symbol type="string" name="config_defaultContentSuggestionsService" />
   <java-symbol type="string" name="config_defaultCredentialManagerHybridService" />
+  <java-symbol type="string" name="config_defaultCredentialProviderService" />
   <java-symbol type="string" name="config_defaultSearchUiService" />
   <java-symbol type="string" name="config_defaultSmartspaceService" />
   <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" />
diff --git a/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java b/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java
new file mode 100644
index 0000000..1a01987
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/EditTextCursorAnchorInfoTest.java
@@ -0,0 +1,576 @@
+/*
+ * 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 android.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class EditTextCursorAnchorInfoTest {
+    private static final CursorAnchorInfo.Builder sCursorAnchorInfoBuilder =
+            new CursorAnchorInfo.Builder();
+    private static final Matrix sMatrix = new Matrix();
+    private static final int[] sLocationOnScreen = new int[2];
+    private static Typeface sTypeface;
+    private static final float TEXT_SIZE = 1f;
+    // The line height of the test font font is 1.2 * textSize.
+    private static final int LINE_HEIGHT = 12;
+    private static final CharSequence DEFAULT_TEXT = "X\nXX\nXXX\nXXXX\nXXXXX";
+    private static final ImmutableList<RectF> DEFAULT_LINE_BOUNDS = ImmutableList.of(
+            new RectF(0f, 0f, 10f, LINE_HEIGHT),
+            new RectF(0f, LINE_HEIGHT, 20f, 2 * LINE_HEIGHT),
+            new RectF(0f, 2 * LINE_HEIGHT, 30f, 3 * LINE_HEIGHT),
+            new RectF(0f, 3 * LINE_HEIGHT, 40f, 4 * LINE_HEIGHT),
+            new RectF(0f, 4 * LINE_HEIGHT, 50f, 5 * LINE_HEIGHT));
+
+    @Rule
+    public ActivityTestRule<TextViewActivity> mActivityRule = new ActivityTestRule<>(
+            TextViewActivity.class);
+    private Activity mActivity;
+    private TextView mEditText;
+
+    @BeforeClass
+    public static void setupClass() {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+
+        // The test font has following coverage and width.
+        // U+0020: 10em
+        // U+002E (.): 10em
+        // U+0043 (C): 100em
+        // U+0049 (I): 1em
+        // U+004C (L): 50em
+        // U+0056 (V): 5em
+        // U+0058 (X): 10em
+        // U+005F (_): 0em
+        // U+05D0    : 1em  // HEBREW LETTER ALEF
+        // U+05D1    : 5em  // HEBREW LETTER BET
+        // U+FFFD (invalid surrogate will be replaced to this): 7em
+        // U+10331 (\uD800\uDF31): 10em
+        // Undefined : 0.5em
+        sTypeface = Typeface.createFromAsset(instrumentation.getTargetContext().getAssets(),
+                "fonts/StaticLayoutLineBreakingTestFont.ttf");
+    }
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testMatrix() {
+        setupEditText("", /* height= */ 100);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        Matrix actualMatrix = cursorAnchorInfo.getMatrix();
+        Matrix expectedMatrix = new Matrix();
+        expectedMatrix.setTranslate(sLocationOnScreen[0], sLocationOnScreen[1]);
+
+        assertThat(actualMatrix).isEqualTo(expectedMatrix);
+    }
+
+    @Test
+    public void testMatrix_withTranslation() {
+        float translationX = 10f;
+        float translationY = 20f;
+        createEditText("");
+        mEditText.setTranslationX(translationX);
+        mEditText.setTranslationY(translationY);
+        measureEditText(100);
+
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        Matrix actualMatrix = cursorAnchorInfo.getMatrix();
+        Matrix expectedMatrix = new Matrix();
+        expectedMatrix.setTranslate(sLocationOnScreen[0] + translationX,
+                sLocationOnScreen[1] + translationY);
+
+        assertThat(actualMatrix).isEqualTo(expectedMatrix);
+    }
+
+    @Test
+    public void testVisibleLineBounds_allVisible() {
+        setupEditText(DEFAULT_TEXT, /* height= */ 100);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        assertThat(lineBounds).isEqualTo(DEFAULT_LINE_BOUNDS);
+    }
+
+    @Test
+    public void testVisibleLineBounds_allVisible_withLineSpacing() {
+        float lineSpacing = 10f;
+        setupEditText("X\nXX\nXXX", /* height= */ 100, lineSpacing,
+                /* lineMultiplier=*/ 1f);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        assertThat(lineBounds.size()).isEqualTo(3);
+        assertThat(lineBounds.get(0)).isEqualTo(new RectF(0f, 0f, 10f, LINE_HEIGHT));
+
+        float line1Top = LINE_HEIGHT + lineSpacing;
+        float line1Bottom = line1Top + LINE_HEIGHT;
+        assertThat(lineBounds.get(1)).isEqualTo(new RectF(0f, line1Top, 20f, line1Bottom));
+
+        float line2Top = 2 * (LINE_HEIGHT + lineSpacing);
+        float line2Bottom = line2Top + LINE_HEIGHT;
+        assertThat(lineBounds.get(2)).isEqualTo(new RectF(0f, line2Top, 30f, line2Bottom));
+    }
+
+    @Test
+    public void testVisibleLineBounds_allVisible_withLineMultiplier() {
+        float lineMultiplier = 2f;
+        setupEditText("X\nXX\nXXX", /* height= */ 100, /* lineSpacing= */ 0f,
+                /* lineMultiplier=*/ lineMultiplier);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        assertThat(lineBounds.size()).isEqualTo(3);
+        assertThat(lineBounds.get(0)).isEqualTo(new RectF(0f, 0f, 10f, LINE_HEIGHT));
+
+        float line1Top = LINE_HEIGHT * lineMultiplier;
+        float line1Bottom = line1Top + LINE_HEIGHT;
+        assertThat(lineBounds.get(1)).isEqualTo(new RectF(0f, line1Top, 20f, line1Bottom));
+
+        float line2Top = 2 * LINE_HEIGHT * lineMultiplier;
+        float line2Bottom = line2Top + LINE_HEIGHT;
+        assertThat(lineBounds.get(2)).isEqualTo(new RectF(0f, line2Top, 30f, line2Bottom));
+    }
+
+    @Test
+    public void testVisibleLineBounds_cutBottomLines() {
+        // Line top is inclusive and line bottom is exclusive. And if the visible area's
+        // bottom equals to the line top, this line is still visible. So the line height is
+        // 3 * LINE_HEIGHT - 1 to avoid including the line 3.
+        setupEditText(DEFAULT_TEXT, /* height= */ 3 * LINE_HEIGHT - 1);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        assertThat(lineBounds).isEqualTo(DEFAULT_LINE_BOUNDS.subList(0, 3));
+    }
+
+    @Test
+    public void testVisibleLineBounds_scrolled_cutTopLines() {
+        // First 2 lines are cut.
+        int scrollY = 2 * LINE_HEIGHT;
+        setupEditText(/* height= */ 3 * LINE_HEIGHT,
+                /* scrollY= */ scrollY);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 5);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0, -scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_scrolled_cutTopAndBottomLines() {
+        // Line top is inclusive and line bottom is exclusive. And if the visible area's
+        // bottom equals to the line top, this line is still visible. So the line height is
+        // 2 * LINE_HEIGHT - 1 which only shows 2 lines.
+        int scrollY = 2 * LINE_HEIGHT;
+        setupEditText(/* height= */ 2 * LINE_HEIGHT - 1,
+                /* scrollY= */ scrollY);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 4);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0, -scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_scrolled_partiallyVisibleLines() {
+        // The first 2 lines are completely cut, line 2 and 3 are partially visible.
+        int scrollY = 2 * LINE_HEIGHT + LINE_HEIGHT / 2;
+        setupEditText(/* height= */ LINE_HEIGHT,
+                /* scrollY= */ scrollY);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 2, 4);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, -scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withCompoundDrawable_allVisible() {
+        int topDrawableHeight = LINE_HEIGHT;
+        Drawable topDrawable = createDrawable(topDrawableHeight);
+        Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT);
+        setupEditText(/* height= */ 100,
+                /* scrollY= */ 0, topDrawable, bottomDrawable);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = copy(DEFAULT_LINE_BOUNDS);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withCompoundDrawable_cutBottomLines() {
+        // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT.
+        // Only first 2 lines are visible.
+        int topDrawableHeight = LINE_HEIGHT;
+        Drawable topDrawable = createDrawable(topDrawableHeight);
+        Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1);
+        setupEditText(/* height= */ 5 * LINE_HEIGHT,
+                /* scrollY= */ 0, topDrawable, bottomDrawable);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 2);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withCompoundDrawable_scrolled() {
+        // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT.
+        // So 2 lines are visible. Because the view is scrolled vertically by LINE_HEIGHT,
+        // the line 1 and 2 are visible.
+        int topDrawableHeight = LINE_HEIGHT;
+        Drawable topDrawable = createDrawable(topDrawableHeight);
+        Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1);
+        int scrollY = LINE_HEIGHT;
+        setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY,
+                topDrawable, bottomDrawable);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 3);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight - scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withCompoundDrawable_partiallyVisible() {
+        // The view's totally height is 5 * LINE_HEIGHT, and drawables take 3 * LINE_HEIGHT.
+        // And because the view is scrolled vertically by 0.5 * LINE_HEIGHT,
+        // the line 0, 1 and 2 are visible.
+        int topDrawableHeight = LINE_HEIGHT;
+        Drawable topDrawable = createDrawable(topDrawableHeight);
+        Drawable bottomDrawable = createDrawable(2 * LINE_HEIGHT + 1);
+        int scrollY = LINE_HEIGHT / 2;
+        setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY,
+                topDrawable, bottomDrawable);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 3);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topDrawableHeight - scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withPaddings_allVisible() {
+        int topPadding = LINE_HEIGHT;
+        int bottomPadding = LINE_HEIGHT;
+        setupEditText(/* height= */ 100, /* scrollY= */ 0, topPadding, bottomPadding);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = copy(DEFAULT_LINE_BOUNDS);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withPaddings_cutBottomLines() {
+        // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT.
+        // So 2 lines are visible.
+        int topPadding = LINE_HEIGHT;
+        int bottomPadding = 2 * LINE_HEIGHT + 1;
+        setupEditText(/* height= */ 5 * LINE_HEIGHT, /* scrollY= */ 0, topPadding, bottomPadding);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 2);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withPaddings_scrolled() {
+        // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT.
+        // So 2 lines are visible. Because the view is scrolled vertically by LINE_HEIGHT,
+        // the line 1 and 2 are visible.
+        int topPadding = LINE_HEIGHT;
+        int bottomPadding = 2 * LINE_HEIGHT + 1;
+        int scrollY = LINE_HEIGHT;
+        setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY,
+                topPadding, bottomPadding);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 3);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding - scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_withPadding_partiallyVisible() {
+        // The view's totally height is 5 * LINE_HEIGHT, and paddings take 3 * LINE_HEIGHT.
+        // And because the view is scrolled vertically by 0.5 * LINE_HEIGHT, the line 0, 1 and 2
+        // are visible.
+        int topPadding = LINE_HEIGHT;
+        int bottomPadding = 2 * LINE_HEIGHT + 1;
+        int scrollY = LINE_HEIGHT / 2;
+        setupEditText(/* height= */ 5 * LINE_HEIGHT, scrollY,
+                topPadding, bottomPadding);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 3);
+        expectedLineBounds.forEach(rectF -> rectF.offset(0f, topPadding - scrollY));
+
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_clippedTop() {
+        // The first line is clipped off.
+        setupVerticalClippedEditText(LINE_HEIGHT, 5 * LINE_HEIGHT);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 5);
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_clippedBottom() {
+        // The last line is clipped off.
+        setupVerticalClippedEditText(0, 4 * LINE_HEIGHT - 1);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 0, 4);
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    @Test
+    public void testVisibleLineBounds_clippedTopAndBottom() {
+        // The first and last line are clipped off.
+        setupVerticalClippedEditText(LINE_HEIGHT, 4 * LINE_HEIGHT - 1);
+        CursorAnchorInfo cursorAnchorInfo =
+                mEditText.getCursorAnchorInfo(0, sCursorAnchorInfoBuilder, sMatrix);
+
+        List<RectF> lineBounds = cursorAnchorInfo.getVisibleLineBounds();
+
+        List<RectF> expectedLineBounds = subList(DEFAULT_LINE_BOUNDS, 1, 4);
+        assertThat(lineBounds).isEqualTo(expectedLineBounds);
+    }
+
+    private List<RectF> copy(List<RectF> rectFList) {
+        List<RectF> result = new ArrayList<>();
+        for (RectF rectF : rectFList) {
+            result.add(new RectF(rectF));
+        }
+        return result;
+    }
+    private List<RectF> subList(List<RectF> rectFList, int start, int end) {
+        List<RectF> result = new ArrayList<>();
+        for (int index = start; index < end; ++index) {
+            result.add(new RectF(rectFList.get(index)));
+        }
+        return result;
+    }
+
+    private void setupVerticalClippedEditText(int visibleTop, int visibleBottom) {
+        ScrollView scrollView = new ScrollView(mActivity);
+        mEditText = new EditText(mActivity);
+        mEditText.setTypeface(sTypeface);
+        mEditText.setText(DEFAULT_TEXT);
+        mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE);
+
+        mEditText.setPadding(0, 0, 0, 0);
+        mEditText.setCompoundDrawables(null, null, null, null);
+        mEditText.setCompoundDrawablePadding(0);
+
+        mEditText.scrollTo(0, 0);
+        mEditText.setLineSpacing(0f, 1f);
+
+        // Place the text layout top to the view's top.
+        mEditText.setGravity(Gravity.TOP);
+        int width = 1000;
+        int height = visibleBottom - visibleTop;
+
+        scrollView.addView(mEditText, new FrameLayout.LayoutParams(
+                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(5 * LINE_HEIGHT, View.MeasureSpec.EXACTLY)));
+        scrollView.measure(
+                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+        scrollView.layout(0, 0, width, height);
+
+        scrollView.scrollTo(0, visibleTop);
+    }
+
+    private void setupEditText(CharSequence text, int height) {
+        createEditText(text);
+        measureEditText(height);
+    }
+
+    private void setupEditText(CharSequence text, int height, float lineSpacing,
+            float lineMultiplier) {
+        createEditText(text);
+        mEditText.setLineSpacing(lineSpacing, lineMultiplier);
+        measureEditText(height);
+    }
+
+    private void setupEditText(int height, int scrollY) {
+        createEditText();
+        mEditText.scrollTo(0, scrollY);
+        measureEditText(height);
+    }
+
+    private void setupEditText(int height, int scrollY, Drawable drawableTop,
+            Drawable drawableBottom) {
+        createEditText();
+        mEditText.scrollTo(0, scrollY);
+        mEditText.setCompoundDrawables(null, drawableTop, null, drawableBottom);
+        measureEditText(height);
+    }
+
+    private void setupEditText(int height, int scrollY, int paddingTop,
+            int paddingBottom) {
+        createEditText();
+        mEditText.scrollTo(0, scrollY);
+        mEditText.setPadding(0, paddingTop, 0, paddingBottom);
+        measureEditText(height);
+    }
+
+    private void createEditText() {
+        createEditText(DEFAULT_TEXT);
+    }
+
+    private void createEditText(CharSequence text) {
+        mEditText = new EditText(mActivity);
+        mEditText.setTypeface(sTypeface);
+        mEditText.setText(text);
+        mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE);
+
+        mEditText.setPadding(0, 0, 0, 0);
+        mEditText.setCompoundDrawables(null, null, null, null);
+        mEditText.setCompoundDrawablePadding(0);
+
+        mEditText.scrollTo(0, 0);
+        mEditText.setLineSpacing(0f, 1f);
+
+        // Place the text layout top to the view's top.
+        mEditText.setGravity(Gravity.TOP);
+    }
+
+    private void measureEditText(int height) {
+        // width equals to 1000 is enough to avoid line break for all test cases.
+        measureEditText(1000, height);
+    }
+
+    private void measureEditText(int width, int height) {
+        mEditText.measure(
+                View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+        mEditText.layout(0, 0, width, height);
+
+        mEditText.getLocationOnScreen(sLocationOnScreen);
+    }
+
+    private Drawable createDrawable(int height) {
+        // width is not important for this drawable, make it 1 pixel.
+        return createDrawable(1, height);
+    }
+
+    private Drawable createDrawable(int width, int height) {
+        ShapeDrawable drawable = new ShapeDrawable();
+        drawable.setBounds(new Rect(0, 0, width, height));
+        return drawable;
+    }
+}
diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java
index 52b4002..6b5f1da 100644
--- a/graphics/java/android/graphics/MeshSpecification.java
+++ b/graphics/java/android/graphics/MeshSpecification.java
@@ -17,11 +17,15 @@
 package android.graphics;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Size;
+import android.annotation.SuppressLint;
 
 import libcore.util.NativeAllocationRegistry;
 
-import java.util.List;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Class responsible for holding specifications for {@link Mesh} creations. This class
@@ -43,100 +47,162 @@
     long mNativeMeshSpec;
 
     /**
-     * Constants for {@link #make(List, int, List, String, String)}
+     * Constants for {@link #make(Attribute[], int, Varying[], String, String)}
      * to determine alpha type. Describes how to interpret the alpha component of a pixel.
+     *
+     * @hide
      */
-    @IntDef({UNKNOWN, OPAQUE, PREMUL, UNPREMULT})
+    @IntDef({ALPHA_TYPE_UNKNOWN, ALPHA_TYPE_OPAQUE, ALPHA_TYPE_PREMUL, ALPHA_TYPE_PREMULT})
+    @Retention(RetentionPolicy.SOURCE)
     private @interface AlphaType {}
 
     /**
      * uninitialized.
      */
-    public static final int UNKNOWN = 0;
+    public static final int ALPHA_TYPE_UNKNOWN = 0;
 
     /**
      * Pixel is opaque.
      */
-    public static final int OPAQUE = 1;
+    public static final int ALPHA_TYPE_OPAQUE = 1;
 
     /**
      * Pixel components are premultiplied by alpha.
      */
-    public static final int PREMUL = 2;
+    public static final int ALPHA_TYPE_PREMUL = 2;
 
     /**
      * Pixel components are independent of alpha.
      */
-    public static final int UNPREMULT = 3;
+    public static final int ALPHA_TYPE_PREMULT = 3;
 
     /**
      * Constants for {@link Attribute} and {@link Varying} for determining the data type.
+     *
+     * @hide
      */
-    @IntDef({FLOAT, FLOAT2, FLOAT3, FLOAT4, UBYTE4})
+    @IntDef({TYPE_FLOAT, TYPE_FLOAT2, TYPE_FLOAT3, TYPE_FLOAT4, TYPE_UBYTE4})
+    @Retention(RetentionPolicy.SOURCE)
     private @interface Type {}
 
     /**
      * Represents one float. Its equivalent shader type is float.
      */
-    public static final int FLOAT = 0;
+    public static final int TYPE_FLOAT = 0;
 
     /**
      * Represents two floats. Its equivalent shader type is float2.
      */
-    public static final int FLOAT2 = 1;
+    public static final int TYPE_FLOAT2 = 1;
 
     /**
      * Represents three floats. Its equivalent shader type is float3.
      */
-    public static final int FLOAT3 = 2;
+    public static final int TYPE_FLOAT3 = 2;
 
     /**
      * Represents four floats. Its equivalent shader type is float4.
      */
-    public static final int FLOAT4 = 3;
+    public static final int TYPE_FLOAT4 = 3;
 
     /**
      * Represents four bytes. Its equivalent shader type is half4.
      */
-    public static final int UBYTE4 = 4;
+    public static final int TYPE_UBYTE4 = 4;
 
     /**
-     * Data class to represent a single attribute in a shader. Note that type parameter must be
-     * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
+     * Data class to represent a single attribute in a shader.
      *
      * Note that offset is the offset in number of bytes. For example, if we had two attributes
      *
+     * <pre>
      * Float3 att1
      * Float att2
+     * </pre>
      *
      * att1 would have an offset of 0, while att2 would have an offset of 12 bytes.
      */
     public static class Attribute {
         @Type
-        private int mType;
-        private int mOffset;
-        private String mName;
+        private final int mType;
+        private final int mOffset;
+        private final String mName;
 
         public Attribute(@Type int type, int offset, @NonNull String name) {
             mType = type;
             mOffset = offset;
             mName = name;
         }
+
+        /**
+         * Return the corresponding data type for this {@link Attribute}.
+         */
+        @Type
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Return the offset of the attribute in bytes
+         */
+        public int getOffset() {
+            return mOffset;
+        }
+
+        /**
+         * Return the name of this {@link Attribute}
+         */
+        @NonNull
+        public String getName() {
+            return mName;
+        }
+
+        @Override
+        public String toString() {
+            return "Attribute{"
+                    + "mType=" + mType
+                    + ", mOffset=" + mOffset
+                    + ", mName='" + mName + '\''
+                    + '}';
+        }
     }
 
     /**
-     * Data class to represent a single varying variable. Note that type parameter must be
-     * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
+     * Data class to represent a single varying variable.
      */
     public static class Varying {
         @Type
-        private int mType;
-        private String mName;
+        private final int mType;
+        private final String mName;
 
         public Varying(@Type int type, @NonNull String name) {
             mType = type;
             mName = name;
         }
+
+        /**
+         * Return the corresponding data type for this {@link Varying}.
+         */
+        @Type
+        public int getType() {
+            return mType;
+        }
+
+        /**
+         * Return the name of this {@link Varying}
+         */
+        @NonNull
+        public String getName() {
+            return mName;
+        }
+
+        @Override
+        public String toString() {
+            return "Varying{"
+                    + "mType=" + mType
+                    + ", mName='" + mName + '\''
+                    + '}';
+        }
     }
 
     private static class MeshSpecificationHolder {
@@ -146,7 +212,9 @@
     }
 
     /**
-     * Creates a {@link MeshSpecification} object for use within {@link Mesh}.
+     * Creates a {@link MeshSpecification} object for use within {@link Mesh}. This uses a default
+     * color space of {@link ColorSpace.Named#SRGB} and {@link AlphaType} of
+     * {@link #ALPHA_TYPE_PREMUL}.
      *
      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
      *                       8.
@@ -162,11 +230,14 @@
      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
      */
     @NonNull
-    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
-            @NonNull List<Varying> varyings, @NonNull String vertexShader,
+    public static MeshSpecification make(
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
+            @IntRange(from = 1, to = 1024) int vertexStride,
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
+            @NonNull String vertexShader,
             @NonNull String fragmentShader) {
-        long nativeMeshSpec = nativeMake(attributes.toArray(new Attribute[attributes.size()]),
-                vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
+        long nativeMeshSpec = nativeMake(attributes,
+                vertexStride, varyings, vertexShader,
                 fragmentShader);
         if (nativeMeshSpec == 0) {
             throw new IllegalArgumentException("MeshSpecification construction failed");
@@ -175,7 +246,8 @@
     }
 
     /**
-     * Creates a {@link MeshSpecification} object.
+     * Creates a {@link MeshSpecification} object.  This uses a default {@link AlphaType} of
+     * {@link #ALPHA_TYPE_PREMUL}.
      *
      * @param attributes     list of attributes represented by {@link Attribute}. Can hold a max of
      *                       8.
@@ -192,11 +264,16 @@
      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
      */
     @NonNull
-    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
-            @NonNull List<Varying> varyings, @NonNull String vertexShader,
-            @NonNull String fragmentShader, @NonNull ColorSpace colorSpace) {
-        long nativeMeshSpec = nativeMakeWithCS(attributes.toArray(new Attribute[attributes.size()]),
-                vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
+    public static MeshSpecification make(
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
+            @IntRange(from = 1, to = 1024) int vertexStride,
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
+            @NonNull String vertexShader,
+            @NonNull String fragmentShader,
+            @NonNull ColorSpace colorSpace
+    ) {
+        long nativeMeshSpec = nativeMakeWithCS(attributes,
+                vertexStride, varyings, vertexShader,
                 fragmentShader, colorSpace.getNativeInstance());
         if (nativeMeshSpec == 0) {
             throw new IllegalArgumentException("MeshSpecification construction failed");
@@ -221,20 +298,23 @@
      * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
      * @param alphaType      Describes how to interpret the alpha component for a pixel. Must be
      *                       one of
-     *                       {@link MeshSpecification#UNKNOWN},
-     *                       {@link MeshSpecification#OPAQUE},
-     *                       {@link MeshSpecification#PREMUL}, or
-     *                       {@link MeshSpecification#UNPREMULT}
+     *                       {@link MeshSpecification#ALPHA_TYPE_UNKNOWN},
+     *                       {@link MeshSpecification#ALPHA_TYPE_OPAQUE},
+     *                       {@link MeshSpecification#ALPHA_TYPE_PREMUL}, or
+     *                       {@link MeshSpecification#ALPHA_TYPE_PREMULT}
      * @return {@link MeshSpecification} object for use when creating {@link Mesh}
      */
     @NonNull
-    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
-            @NonNull List<Varying> varyings, @NonNull String vertexShader,
-            @NonNull String fragmentShader, @NonNull ColorSpace colorSpace,
+    public static MeshSpecification make(
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 8) Attribute[] attributes,
+            @IntRange(from = 1, to = 1024) int vertexStride,
+            @SuppressLint("ArrayReturn") @NonNull @Size(max = 6) Varying[] varyings,
+            @NonNull String vertexShader,
+            @NonNull String fragmentShader,
+            @NonNull ColorSpace colorSpace,
             @AlphaType int alphaType) {
         long nativeMeshSpec =
-                nativeMakeWithAlpha(attributes.toArray(new Attribute[attributes.size()]),
-                        vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
+                nativeMakeWithAlpha(attributes, vertexStride, varyings, vertexShader,
                         fragmentShader, colorSpace.getNativeInstance(), alphaType);
         if (nativeMeshSpec == 0) {
             throw new IllegalArgumentException("MeshSpecification construction failed");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index d276002..88525aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -84,6 +84,15 @@
                 String[] groups = Arrays.copyOfRange(args, 1, args.length);
                 return mShellProtoLog.stopTextLogging(groups, pw) == 0;
             }
+            case "save-for-bugreport": {
+                if (!mShellProtoLog.isProtoEnabled()) {
+                    pw.println("Logging to proto is not enabled for WMShell.");
+                    return false;
+                }
+                mShellProtoLog.stopProtoLog(pw, true /* writeToFile */);
+                mShellProtoLog.startProtoLog(pw);
+                return true;
+            }
             default: {
                 pw.println("Invalid command: " + args[0]);
                 printShellCommandHelp(pw, "");
@@ -108,5 +117,7 @@
         pw.println(prefix + "  Enable logcat logging for given groups.");
         pw.println(prefix + "disable-text [group...]");
         pw.println(prefix + "  Disable logcat logging for given groups.");
+        pw.println(prefix + "save-for-bugreport");
+        pw.println(prefix + "  Flush proto logging to file, only if it's enabled.");
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index b2f61c2..5275e90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -829,7 +829,15 @@
         }
         mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
         active.mToken = transitionToken;
-        mActiveTransitions.add(active);
+        int insertIdx = 0;
+        for (; insertIdx < mActiveTransitions.size(); ++insertIdx) {
+            if (mActiveTransitions.get(insertIdx).mInfo == null) {
+                // A `startNewTransition` was sent to WMCore, but wasn't acknowledged before WMCore
+                // made this request, so insert this request beforehand to keep order in sync.
+                break;
+            }
+        }
+        mActiveTransitions.add(insertIdx, active);
     }
 
     /** Start a new transition directly. */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 595c3b4..a9061ae 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -45,6 +45,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.clearInvocations;
@@ -119,8 +120,8 @@
 
     @Before
     public void setUp() {
-        doAnswer(invocation -> invocation.getArguments()[1])
-                .when(mOrganizer).startTransition(any(), any());
+        doAnswer(invocation -> new Binder())
+                .when(mOrganizer).startNewTransition(anyInt(), any());
     }
 
     @Test
@@ -562,6 +563,32 @@
     }
 
     @Test
+    public void testTransitionOrderMatchesCore() {
+        Transitions transitions = createTestTransitions();
+        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+        IBinder transitToken = new Binder();
+        IBinder shellInit = transitions.startTransition(TRANSIT_CLOSE,
+                new WindowContainerTransaction(), null /* handler */);
+        // make sure we are testing the "New" API.
+        verify(mOrganizer, times(1)).startNewTransition(eq(TRANSIT_CLOSE), any());
+        // WMCore may not receive the new transition before requesting its own.
+        transitions.requestStartTransition(transitToken,
+                new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+        verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
+
+        // At this point, WM is working on its transition (the shell-initialized one is still
+        // queued), so continue the transition lifecycle for that.
+        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
+                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
+        transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
+                mock(SurfaceControl.Transaction.class));
+        // At this point, if things are not working, we'd get an NPE due to attempting to merge
+        // into the shellInit transition which hasn't started yet.
+        assertEquals(1, mDefaultHandler.activeCount());
+    }
+
+    @Test
     public void testShouldRotateSeamlessly() throws Exception {
         final RunningTaskInfo taskInfo =
                 createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index dea6097..b0cdb05 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1275,7 +1275,10 @@
                     || (preset == MediaRecorder.AudioSource.VOICE_UPLINK)
                     || (preset == MediaRecorder.AudioSource.VOICE_CALL)
                     || (preset == MediaRecorder.AudioSource.ECHO_REFERENCE)
-                    || (preset == MediaRecorder.AudioSource.ULTRASOUND)) {
+                    || (preset == MediaRecorder.AudioSource.ULTRASOUND)
+                    // AUDIO_SOURCE_INVALID is used by convention on default initialized
+                    // audio attributes
+                    || (preset == MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID)) {
                 mSource = preset;
             } else {
                 setCapturePreset(preset);
diff --git a/media/java/android/media/tv/TableRequest.java b/media/java/android/media/tv/TableRequest.java
index a9ea6d3..d9587f6 100644
--- a/media/java/android/media/tv/TableRequest.java
+++ b/media/java/android/media/tv/TableRequest.java
@@ -33,7 +33,8 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({TABLE_NAME_PAT, TABLE_NAME_PMT, TABLE_NAME_CAT})
+    @IntDef({TABLE_NAME_PAT, TABLE_NAME_PMT, TABLE_NAME_CAT, TABLE_NAME_NIT, TABLE_NAME_BAT,
+            TABLE_NAME_SDT, TABLE_NAME_EIT, TABLE_NAME_TDT, TABLE_NAME_TOT, TABLE_NAME_SIT})
     public @interface TableName {}
 
     /** Program Association Table */
@@ -42,42 +43,34 @@
     public static final int TABLE_NAME_PMT = 1;
     /**
      * Conditional Access Table
-     * @hide
      */
     public static final int TABLE_NAME_CAT = 2;
     /**
      * Network Information Table
-     * @hide
      */
     public static final int TABLE_NAME_NIT = 3;
     /**
      * Bouquet Association Table
-     * @hide
      */
     public static final int TABLE_NAME_BAT = 4;
     /**
      * Service Description Table
-     * @hide
      */
     public static final int TABLE_NAME_SDT = 5;
     /**
      * Event Information Table
-     * @hide
      */
     public static final int TABLE_NAME_EIT = 6;
     /**
      * Time and Date Table
-     * @hide
      */
     public static final int TABLE_NAME_TDT = 7;
     /**
      * Time Offset Table
-     * @hide
      */
     public static final int TABLE_NAME_TOT = 8;
     /**
      * Selection Information Table
-     * @hide
      */
     public static final int TABLE_NAME_SIT = 9;
 
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 999ff0d..80b6e21 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -145,30 +145,29 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = "PLAYBACK_COMMAND_STOP_MODE_", value = {
-            PLAYBACK_COMMAND_STOP_MODE_BLANK,
-            PLAYBACK_COMMAND_STOP_MODE_FREEZE
+    @IntDef(prefix = "COMMAND_PARAMETER_VALUE_STOP_MODE_", value = {
+            COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK,
+            COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE
     })
     public @interface PlaybackCommandStopMode {}
 
     /**
      * Playback command stop mode: show a blank screen.
-     * @hide
+     * @see #PLAYBACK_COMMAND_TYPE_STOP
      */
-    public static final int PLAYBACK_COMMAND_STOP_MODE_BLANK = 1;
+    public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_BLANK = 1;
 
     /**
      * Playback command stop mode: freeze the video.
-     * @hide
+     * @see #PLAYBACK_COMMAND_TYPE_STOP
      */
-    public static final int PLAYBACK_COMMAND_STOP_MODE_FREEZE = 2;
+    public static final int COMMAND_PARAMETER_VALUE_STOP_MODE_FREEZE = 2;
 
     /**
      * Playback command parameter: stop mode.
      * <p>Type: int
      *
      * @see #PLAYBACK_COMMAND_TYPE_STOP
-     * @hide
      */
     public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode";
 
@@ -550,7 +549,8 @@
 
         /**
          * Receives current video bounds.
-         * @hide
+         *
+         * @param bounds the rectangle area for rendering the current video.
          */
         public void onCurrentVideoBounds(@NonNull Rect bounds) {
         }
@@ -1135,7 +1135,6 @@
 
         /**
          * Requests the bounds of the current video.
-         * @hide
          */
         @CallSuper
         public void requestCurrentVideoBounds() {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 29a96f7..2c40f39 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -516,7 +516,8 @@
 
     /**
      * Sends current video bounds to related TV interactive app.
-     * @hide
+     *
+     * @param bounds the rectangle area for rendering the current video.
      */
     public void sendCurrentVideoBounds(@NonNull Rect bounds) {
         if (DEBUG) {
@@ -1131,7 +1132,6 @@
          * is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @hide
          */
         public void onRequestCurrentVideoBounds(@NonNull String iAppServiceId) {
         }
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
index 5a4d256..499d130 100644
--- a/packages/CredentialManager/AndroidManifest.xml
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -32,7 +32,6 @@
       android:supportsRtl="true"
       android:theme="@style/Theme.CredentialSelector">
 
-    <!--TODO: make sure implementing singleTop on NewIntent-->
     <activity
         android:name=".CredentialSelectorActivity"
         android:exported="true"
diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png
deleted file mode 100644
index 388d098..0000000
--- a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.png
+++ /dev/null
Binary files differ
diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml
new file mode 100644
index 0000000..91b23cc
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding.xml
@@ -0,0 +1,158 @@
+<!--
+  ~ 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.
+  -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="vector"
+    android:width="316dp"
+    android:height="168dp"
+    android:viewportWidth="316"
+    android:viewportHeight="168">
+    <path
+        android:name="path"
+        android:pathData="M 238 56 C 238 42.75 227.26 32 214 32 C 200.75 32 190 42.75 190 56 L 190 74 C 190 77.31 192.69 80 196 80 L 214 80 C 227.26 80 238 69.26 238 56 Z"
+        android:fillColor="#ffba29"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_1"
+        android:pathData="M 199.42 51.71 C 199.34 51.65 199.29 51.56 199.27 51.44 C 199.26 51.31 199.27 51.21 199.33 51.13 C 201.08 48.8 203.23 46.98 205.79 45.67 C 208.35 44.36 211.07 43.71 213.96 43.71 C 216.85 43.71 219.64 44.34 222.25 45.61 C 224.86 46.88 227.03 48.69 228.75 51.05 C 228.86 51.19 228.9 51.32 228.85 51.45 C 228.81 51.57 228.72 51.68 228.58 51.76 C 228.5 51.82 228.38 51.84 228.23 51.84 C 228.08 51.84 227.96 51.76 227.88 51.59 C 226.27 49.34 224.23 47.63 221.78 46.47 C 219.32 45.3 216.72 44.72 213.97 44.72 C 211.22 44.72 208.68 45.32 206.26 46.51 C 203.84 47.7 201.83 49.4 200.22 51.59 C 200.08 51.76 199.94 51.84 199.8 51.84 C 199.66 51.84 199.54 51.8 199.42 51.72 Z M 218.75 72.42 C 216 71.7 213.72 70.28 211.9 68.17 C 210.08 66.06 209.17 63.49 209.17 60.46 C 209.17 59.13 209.65 58.02 210.61 57.15 C 211.57 56.27 212.71 55.84 214.05 55.84 C 215.39 55.84 216.48 56.28 217.43 57.15 C 218.37 58.02 218.85 59.13 218.85 60.46 C 218.85 61.49 219.23 62.34 220 63.02 C 220.76 63.7 221.66 64.04 222.69 64.04 C 223.72 64.04 224.64 63.69 225.38 63 C 226.12 62.31 226.48 61.46 226.48 60.46 C 226.48 57.13 225.25 54.32 222.79 52.04 C 220.33 49.76 217.39 48.62 213.98 48.62 C 210.57 48.62 207.63 49.76 205.19 52.04 C 202.75 54.32 201.52 57.12 201.52 60.46 C 201.52 61.13 201.61 61.97 201.79 63 C 201.97 64.03 202.28 65.22 202.73 66.58 C 202.79 66.72 202.79 66.84 202.75 66.93 C 202.71 67.03 202.6 67.12 202.44 67.2 C 202.3 67.28 202.17 67.3 202.04 67.24 C 201.91 67.18 201.82 67.09 201.77 66.95 C 201.35 65.78 201.04 64.68 200.83 63.64 C 200.62 62.6 200.52 61.54 200.52 60.45 C 200.52 56.81 201.85 53.76 204.5 51.3 C 207.15 48.84 210.31 47.61 213.98 47.61 C 217.65 47.61 220.86 48.84 223.52 51.3 C 226.19 53.76 227.52 56.81 227.52 60.45 C 227.52 61.73 227.05 62.81 226.1 63.7 C 225.16 64.59 224.02 65.03 222.68 65.03 C 221.34 65.03 220.24 64.59 219.28 63.7 C 218.32 62.81 217.84 61.73 217.84 60.45 C 217.84 59.42 217.46 58.56 216.72 57.87 C 215.98 57.18 215.08 56.83 214.05 56.83 C 213.02 56.83 212.08 57.18 211.32 57.87 C 210.56 58.56 210.17 59.43 210.17 60.45 C 210.17 63.26 211.01 65.6 212.69 67.47 C 214.37 69.35 216.49 70.66 219.04 71.41 C 219.21 71.47 219.32 71.55 219.39 71.66 C 219.46 71.77 219.47 71.9 219.41 72.04 C 219.35 72.15 219.28 72.25 219.18 72.35 C 219.08 72.45 218.94 72.47 218.74 72.41 Z M 204.42 43.04 C 204.36 43.04 204.27 43.03 204.15 43 C 204.02 42.97 203.95 42.9 203.92 42.79 C 203.86 42.71 203.84 42.6 203.86 42.46 C 203.87 42.32 203.92 42.22 204.01 42.17 C 205.54 41.31 207.16 40.66 208.86 40.23 C 210.57 39.8 212.3 39.58 214.05 39.58 C 215.8 39.58 217.51 39.79 219.17 40.2 C 220.84 40.62 222.45 41.24 224 42.08 C 224.17 42.16 224.27 42.27 224.31 42.41 C 224.35 42.55 224.35 42.67 224.29 42.79 C 224.23 42.9 224.14 42.98 224.02 43.04 C 223.89 43.1 223.78 43.1 223.67 43.04 C 222.2 42.18 220.65 41.55 219.02 41.16 C 217.39 40.77 215.74 40.58 214.04 40.58 C 212.34 40.58 210.7 40.8 209.1 41.23 C 207.5 41.66 205.94 42.26 204.41 43.04 Z M 209.92 71.96 C 208.36 70.29 207.14 68.55 206.25 66.73 C 205.36 64.91 204.92 62.82 204.92 60.46 C 204.92 58.1 205.82 55.91 207.61 54.23 C 209.4 52.55 211.55 51.71 214.05 51.71 C 216.55 51.71 218.7 52.55 220.49 54.23 C 222.28 55.91 223.18 57.99 223.18 60.46 C 223.18 60.6 223.14 60.72 223.06 60.81 C 222.98 60.91 222.85 60.96 222.68 60.96 C 222.6 60.96 222.49 60.91 222.37 60.81 C 222.25 60.71 222.18 60.6 222.18 60.46 C 222.18 58.27 221.38 56.43 219.78 54.94 C 218.18 53.45 216.27 52.71 214.05 52.71 C 211.83 52.71 209.92 53.45 208.32 54.94 C 206.72 56.43 205.92 58.27 205.92 60.46 C 205.92 62.77 206.32 64.72 207.13 66.34 C 207.94 67.95 209.1 69.58 210.63 71.22 C 210.77 71.39 210.82 71.53 210.78 71.66 C 210.74 71.78 210.69 71.89 210.63 71.97 C 210.55 72.05 210.44 72.1 210.3 72.12 C 210.16 72.13 210.04 72.09 209.92 71.97 Z M 222.34 68.96 C 219.95 68.96 217.88 68.15 216.11 66.54 C 214.35 64.93 213.46 62.9 213.46 60.46 C 213.46 60.32 213.5 60.2 213.58 60.11 C 213.66 60.01 213.79 59.96 213.96 59.96 C 214.1 59.96 214.22 60.01 214.31 60.11 C 214.41 60.21 214.46 60.33 214.46 60.46 C 214.46 62.65 215.24 64.45 216.81 65.86 C 218.38 67.26 220.22 67.96 222.33 67.96 C 222.64 67.96 222.95 67.95 223.27 67.92 C 223.59 67.89 223.9 67.85 224.21 67.8 C 224.32 67.8 224.42 67.83 224.5 67.88 C 224.58 67.93 224.64 68.03 224.67 68.17 C 224.73 68.31 224.71 68.43 224.63 68.52 C 224.55 68.62 224.42 68.68 224.25 68.71 C 224 68.79 223.7 68.86 223.35 68.9 C 223 68.94 222.66 68.96 222.33 68.96 Z"
+        android:fillColor="#1f1f1f"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_2"
+        android:pathData="M 134 72 L 134 60 C 134 46.75 144.75 36 158 36 C 171.25 36 182 46.75 182 60 L 182 72 M 124 72 L 192 72 C 195.712 72 199.275 73.476 201.899 76.101 C 204.524 78.725 206 82.288 206 86 L 206 146 C 206 149.712 204.524 153.275 201.899 155.899 C 199.275 158.524 195.712 160 192 160 L 124 160 C 120.288 160 116.725 158.524 114.101 155.899 C 111.476 153.275 110 149.712 110 146 L 110 86 C 110 82.288 111.476 78.725 114.101 76.101 C 116.725 73.476 120.288 72 124 72"
+        android:strokeColor="#1f1f1f"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <path
+        android:name="path_3"
+        android:pathData="M 155.67 114.83 C 153.22 114.83 151.15 113.98 149.46 112.29 C 147.77 110.6 146.92 108.53 146.92 106.08 C 146.92 103.63 147.77 101.56 149.46 99.87 C 151.15 98.18 153.22 97.33 155.67 97.33 C 158.12 97.33 160.19 98.18 161.88 99.87 C 163.57 101.56 164.42 103.63 164.42 106.08 C 164.42 108.53 163.57 110.6 161.88 112.29 C 160.19 113.98 158.12 114.83 155.67 114.83 Z M 172.59 137 L 169.67 133.5 L 169.67 124.92 C 168.27 124.38 167.14 123.51 166.29 122.32 C 165.44 121.13 165.01 119.8 165.01 118.32 C 165.01 116.38 165.69 114.72 167.05 113.36 C 168.41 112 170.06 111.32 172.01 111.32 C 173.96 111.32 175.61 112 176.97 113.36 C 178.33 114.72 179.01 116.37 179.01 118.32 C 179.01 119.8 178.58 121.13 177.73 122.32 C 176.88 123.51 175.75 124.37 174.35 124.92 L 174.35 125.33 L 176.68 127.66 L 174.35 129.99 L 176.68 132.32 L 172.6 136.99 Z M 172.01 121.83 C 172.98 121.83 173.81 121.49 174.49 120.81 C 175.17 120.13 175.51 119.3 175.51 118.33 C 175.51 117.36 175.17 116.53 174.49 115.85 C 173.81 115.17 172.98 114.83 172.01 114.83 C 171.04 114.83 170.21 115.17 169.53 115.85 C 168.85 116.53 168.51 117.36 168.51 118.33 C 168.51 119.3 168.85 120.13 169.53 120.81 C 170.21 121.49 171.04 121.83 172.01 121.83 Z M 160.46 119.85 C 160.65 121.44 161.15 122.9 161.95 124.23 C 162.75 125.55 163.77 126.7 165.01 127.67 L 165.01 134.67 L 137.01 134.67 L 137.01 129.13 C 137.01 127.81 137.38 126.6 138.12 125.51 C 138.86 124.42 139.81 123.59 140.98 123 C 143.27 121.83 145.66 120.96 148.13 120.38 C 150.6 119.8 153.12 119.5 155.68 119.5 C 156.46 119.5 157.26 119.53 158.07 119.59 C 158.89 119.65 159.68 119.74 160.46 119.85 Z"
+        android:fillColor="#1f1f1f"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_4"
+        android:pathData="M 63.481 27.427 L 102.118 17.075 C 102.457 16.984 102.815 16.984 103.154 17.075 C 103.493 17.166 103.802 17.344 104.05 17.592 C 104.298 17.841 104.477 18.15 104.568 18.489 L 110.779 41.671 C 110.87 42.01 110.87 42.367 110.779 42.706 C 110.689 43.045 110.51 43.355 110.262 43.603 C 110.014 43.851 109.704 44.03 109.365 44.121 L 70.728 54.473 C 70.216 54.611 69.67 54.539 69.211 54.274 C 68.751 54.008 68.416 53.571 68.279 53.059 L 62.067 29.877 C 61.976 29.538 61.976 29.181 62.067 28.842 C 62.158 28.503 62.336 28.193 62.585 27.945 C 62.833 27.697 63.142 27.518 63.481 27.427"
+        android:strokeColor="#e1e3e1"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"
+        android:strokeMiterLimit="10"/>
+    <path
+        android:name="path_5"
+        android:pathData="M 86.189 50.327 L 93.917 48.256 L 94.952 52.12 L 87.225 54.191 Z"
+        android:fillColor="#e1e3e1"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_6"
+        android:pathData="M 83.36 55.23 L 98.81 51.08"
+        android:strokeColor="#e1e3e1"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"
+        android:strokeMiterLimit="10"/>
+    <path
+        android:name="path_7"
+        android:pathData="M 90.9 37.2 L 88.3 35.7 L 89.4 33.8 C 89.59 33.48 89.37 33.07 89 33.05 L 81.88 32.62 C 81.47 32.6 81.21 33.05 81.43 33.39 L 85.36 39.34 C 85.56 39.65 86.02 39.64 86.21 39.31 L 87.31 37.41 L 89.91 38.91 C 90.39 39.19 91 39.02 91.28 38.54 C 91.56 38.06 91.39 37.45 90.91 37.17 Z"
+        android:fillColor="#e1e3e1"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_8"
+        android:pathData="M 86 160 L 222 160"
+        android:strokeColor="#1f1f1f"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <group android:name="group">
+        <path
+            android:name="path_9"
+            android:pathData="M 98 80 C 92.088 80 86.325 81.872 81.542 85.348 C 76.759 88.823 73.197 93.725 71.37 99.348 C 69.543 104.97 69.543 111.03 71.37 116.652 C 73.197 122.275 76.759 127.177 81.542 130.652 C 86.325 134.128 92.088 136 98 136 C 103.912 136 109.675 134.128 114.458 130.652 C 119.241 127.177 122.803 122.275 124.63 116.652 C 126.457 111.03 126.457 104.97 124.63 99.348 C 122.803 93.725 119.241 88.823 114.458 85.348 C 109.675 81.872 103.912 80 98 80 Z"
+            android:fillColor="#6dd48b"
+            android:strokeWidth="1"/>
+        <path
+            android:name="path_10"
+            android:pathData="M 98 90.5 C 93.36 90.5 88.906 92.345 85.626 95.626 C 82.345 98.906 80.5 103.36 80.5 108 C 80.5 112.64 82.345 117.094 85.626 120.374 C 88.906 123.655 93.36 125.5 98 125.5 C 102.64 125.5 107.094 123.655 110.374 120.374 C 113.655 117.094 115.5 112.64 115.5 108 C 115.5 103.36 113.655 98.906 110.374 95.626 C 107.094 92.345 102.64 90.5 98 90.5 Z"
+            android:strokeColor="#1f1f1f"
+            android:strokeWidth="1"
+            android:strokeMiterLimit="10"/>
+        <path
+            android:name="path_11"
+            android:pathData="M 92.31 104.5 C 92.31 105.22 91.72 105.81 91 105.81 C 90.28 105.81 89.69 105.22 89.69 104.5 C 89.69 103.78 90.28 103.19 91 103.19 C 91.72 103.19 92.31 103.78 92.31 104.5 Z M 106.31 104.5 C 106.31 105.22 105.72 105.81 105 105.81 C 104.28 105.81 103.69 105.22 103.69 104.5 C 103.69 103.78 104.28 103.19 105 103.19 C 105.72 103.19 106.31 103.78 106.31 104.5 Z"
+            android:fillColor="#1f1f1f"
+            android:strokeWidth="1"/>
+        <path
+            android:name="path_12"
+            android:pathData="M 98.88 107.12 L 98.88 110.62 L 96.61 110.62 M 100.11 116.16 C 98.94 117.33 97.06 117.33 95.89 116.16 C 95.29 115.56 95 114.76 95.02 113.97"
+            android:strokeColor="#1f1f1f"
+            android:strokeWidth="1"
+            android:strokeMiterLimit="10"/>
+    </group>
+    <group android:name="group_2">
+        <path
+            android:name="path_13"
+            android:pathData="M 264.23 104 L 296.23 104"
+            android:strokeColor="#e1e3e1"
+            android:strokeWidth="1"
+            android:strokeLineCap="round"
+            android:strokeMiterLimit="10"/>
+        <path
+            android:name="path_14"
+            android:pathData="M 273.16 81.21 C 272.82 80.19 271.87 79.5 270.79 79.5 L 243.01 79.5 C 242.2 79.5 241.46 79.88 240.98 80.54 C 240.5 81.2 240.38 82.02 240.64 82.79 L 247.31 102.79 C 247.65 103.81 248.6 104.5 249.68 104.5 L 280.93 104.5 L 273.17 81.21 Z M 248.25 87.93 C 247.45 88.14 246.57 87.45 246.28 86.39 C 245.99 85.33 246.41 84.28 247.21 84.07 C 248.01 83.86 248.89 84.55 249.18 85.61 C 249.47 86.68 249.05 87.72 248.25 87.93 Z"
+            android:fillColor="#e1e3e1"
+            android:strokeWidth="1"/>
+        <group android:name="group_1">
+            <path
+                android:name="path_15"
+                android:pathData="M 279 83 L 285 83 M 277 77 L 285 69 M 271 75 L 271 69"
+                android:strokeColor="#e1e3e1"
+                android:strokeWidth="1"
+                android:strokeLineCap="round"/>
+        </group>
+    </group>
+    <path
+        android:name="path_16"
+        android:pathData="M 58.16 105.86 C 57.83 105.28 57.29 104.87 56.64 104.69 L 45.05 101.58 C 43.72 101.22 42.34 102.02 41.99 103.35 L 35.78 126.53 C 35.61 127.17 35.7 127.85 36.03 128.43 C 36.36 129.01 36.9 129.42 37.55 129.6 L 49.14 132.71 C 49.36 132.77 49.57 132.79 49.79 132.79 C 50.89 132.79 51.91 132.05 52.21 130.94 L 58.42 107.76 C 58.59 107.11 58.5 106.44 58.17 105.86 Z M 50.65 107.75 C 50.51 108.28 49.96 108.6 49.43 108.46 C 48.9 108.32 48.58 107.77 48.72 107.24 C 48.86 106.71 49.41 106.39 49.94 106.53 C 50.47 106.67 50.79 107.22 50.65 107.75 Z"
+        android:fillColor="#e1e3e1"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_17"
+        android:pathData="M 230 160 L 246 160"
+        android:strokeColor="#1f1f1f"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <path
+        android:name="path_18"
+        android:pathData="M 204.63 80 C 202.38 75.27 197.58 72 192 72 L 190 72 L 190 74 C 190 77.31 192.69 80 196 80 L 204.63 80 Z"
+        android:fillColor="#1f1f1f"
+        android:strokeWidth="1"/>
+    <group android:name="group_4">
+        <path
+            android:name="path_19"
+            android:pathData="M 211.37 102.29 C 210.53 102.82 209.46 102.82 208.63 102.29 C 202.93 98.67 195.29 99.35 190.31 104.32 C 185.34 109.29 184.67 116.93 188.28 122.63 C 188.81 123.46 188.81 124.54 188.28 125.38 C 184.66 131.08 185.34 138.72 190.31 143.69 C 195.29 148.66 202.93 149.34 208.63 145.72 C 209.46 145.19 210.54 145.19 211.37 145.72 C 217.07 149.34 224.71 148.66 229.68 143.69 C 234.66 138.72 235.33 131.08 231.71 125.38 C 231.18 124.55 231.18 123.47 231.71 122.63 C 235.33 116.93 234.65 109.29 229.68 104.32 C 224.71 99.35 217.07 98.67 211.37 102.29 Z"
+            android:fillColor="#0b57cf"
+            android:strokeWidth="1"
+            android:fillType="evenOdd"/>
+        <group android:name="group_3">
+            <path
+                android:name="path_20"
+                android:pathData="M 198 110 C 197.47 110 196.961 110.211 196.586 110.586 C 196.211 110.961 196 111.47 196 112 C 196 112.53 196.211 113.039 196.586 113.414 C 196.961 113.789 197.47 114 198 114 C 198.53 114 199.039 113.789 199.414 113.414 C 199.789 113.039 200 112.53 200 112 C 200 111.47 199.789 110.961 199.414 110.586 C 199.039 110.211 198.53 110 198 110 Z M 210 110 C 209.47 110 208.961 110.211 208.586 110.586 C 208.211 110.961 208 111.47 208 112 C 208 112.53 208.211 113.039 208.586 113.414 C 208.961 113.789 209.47 114 210 114 C 210.53 114 211.039 113.789 211.414 113.414 C 211.789 113.039 212 112.53 212 112 C 212 111.47 211.789 110.961 211.414 110.586 C 211.039 110.211 210.53 110 210 110 Z M 222 110 C 221.47 110 220.961 110.211 220.586 110.586 C 220.211 110.961 220 111.47 220 112 C 220 112.53 220.211 113.039 220.586 113.414 C 220.961 113.789 221.47 114 222 114 C 222.53 114 223.039 113.789 223.414 113.414 C 223.789 113.039 224 112.53 224 112 C 224 111.47 223.789 110.961 223.414 110.586 C 223.039 110.211 222.53 110 222 110 Z M 198 122 C 197.47 122 196.961 122.211 196.586 122.586 C 196.211 122.961 196 123.47 196 124 C 196 124.53 196.211 125.039 196.586 125.414 C 196.961 125.789 197.47 126 198 126 C 198.53 126 199.039 125.789 199.414 125.414 C 199.789 125.039 200 124.53 200 124 C 200 123.47 199.789 122.961 199.414 122.586 C 199.039 122.211 198.53 122 198 122 Z M 210 122 C 209.47 122 208.961 122.211 208.586 122.586 C 208.211 122.961 208 123.47 208 124 C 208 124.53 208.211 125.039 208.586 125.414 C 208.961 125.789 209.47 126 210 126 C 210.53 126 211.039 125.789 211.414 125.414 C 211.789 125.039 212 124.53 212 124 C 212 123.47 211.789 122.961 211.414 122.586 C 211.039 122.211 210.53 122 210 122 Z M 222 122 C 221.47 122 220.961 122.211 220.586 122.586 C 220.211 122.961 220 123.47 220 124 C 220 124.53 220.211 125.039 220.586 125.414 C 220.961 125.789 221.47 126 222 126 C 222.53 126 223.039 125.789 223.414 125.414 C 223.789 125.039 224 124.53 224 124 C 224 123.47 223.789 122.961 223.414 122.586 C 223.039 122.211 222.53 122 222 122 Z M 198 134 C 197.47 134 196.961 134.211 196.586 134.586 C 196.211 134.961 196 135.47 196 136 C 196 136.53 196.211 137.039 196.586 137.414 C 196.961 137.789 197.47 138 198 138 C 198.53 138 199.039 137.789 199.414 137.414 C 199.789 137.039 200 136.53 200 136 C 200 135.47 199.789 134.961 199.414 134.586 C 199.039 134.211 198.53 134 198 134 Z M 210 134 C 209.47 134 208.961 134.211 208.586 134.586 C 208.211 134.961 208 135.47 208 136 C 208 136.53 208.211 137.039 208.586 137.414 C 208.961 137.789 209.47 138 210 138 C 210.53 138 211.039 137.789 211.414 137.414 C 211.789 137.039 212 136.53 212 136 C 212 135.47 211.789 134.961 211.414 134.586 C 211.039 134.211 210.53 134 210 134 Z M 222 134 C 221.47 134 220.961 134.211 220.586 134.586 C 220.211 134.961 220 135.47 220 136 C 220 136.53 220.211 137.039 220.586 137.414 C 220.961 137.789 221.47 138 222 138 C 222.53 138 223.039 137.789 223.414 137.414 C 223.789 137.039 224 136.53 224 136 C 224 135.47 223.789 134.961 223.414 134.586 C 223.039 134.211 222.53 134 222 134 Z"
+                android:fillColor="#ffffff"
+                android:strokeWidth="1"/>
+        </group>
+        <path
+            android:name="path_21"
+            android:pathData="M 222 112 L 198 136 L 222 136"
+            android:strokeColor="#ffffff"
+            android:strokeWidth="1"
+            android:strokeLineCap="round"/>
+    </group>
+</vector>
diff --git a/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml
new file mode 100644
index 0000000..53933cb
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_passkeys_onboarding_dark.xml
@@ -0,0 +1,158 @@
+<!--
+  ~ 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.
+  -->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="vector"
+    android:width="316dp"
+    android:height="168dp"
+    android:viewportWidth="316"
+    android:viewportHeight="168">
+    <path
+        android:name="path"
+        android:pathData="M 238 56 C 238 42.75 227.26 32 214 32 C 200.75 32 190 42.75 190 56 L 190 74 C 190 77.31 192.69 80 196 80 L 214 80 C 227.26 80 238 69.26 238 56 Z"
+        android:fillColor="#f09d00"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_1"
+        android:pathData="M 199.42 51.71 C 199.34 51.65 199.29 51.56 199.27 51.44 C 199.26 51.31 199.27 51.21 199.33 51.13 C 201.08 48.8 203.23 46.98 205.79 45.67 C 208.35 44.36 211.07 43.71 213.96 43.71 C 216.85 43.71 219.64 44.34 222.25 45.61 C 224.86 46.88 227.03 48.69 228.75 51.05 C 228.86 51.19 228.9 51.32 228.85 51.45 C 228.81 51.57 228.72 51.68 228.58 51.76 C 228.5 51.82 228.38 51.84 228.23 51.84 C 228.08 51.84 227.96 51.76 227.88 51.59 C 226.27 49.34 224.23 47.63 221.78 46.47 C 219.32 45.3 216.72 44.72 213.97 44.72 C 211.22 44.72 208.68 45.32 206.26 46.51 C 203.84 47.7 201.83 49.4 200.22 51.59 C 200.08 51.76 199.94 51.84 199.8 51.84 C 199.66 51.84 199.54 51.8 199.42 51.72 Z M 218.75 72.42 C 216 71.7 213.72 70.28 211.9 68.17 C 210.08 66.06 209.17 63.49 209.17 60.46 C 209.17 59.13 209.65 58.02 210.61 57.15 C 211.57 56.27 212.71 55.84 214.05 55.84 C 215.39 55.84 216.48 56.28 217.43 57.15 C 218.37 58.02 218.85 59.13 218.85 60.46 C 218.85 61.49 219.23 62.34 220 63.02 C 220.76 63.7 221.66 64.04 222.69 64.04 C 223.72 64.04 224.64 63.69 225.38 63 C 226.12 62.31 226.48 61.46 226.48 60.46 C 226.48 57.13 225.25 54.32 222.79 52.04 C 220.33 49.76 217.39 48.62 213.98 48.62 C 210.57 48.62 207.63 49.76 205.19 52.04 C 202.75 54.32 201.52 57.12 201.52 60.46 C 201.52 61.13 201.61 61.97 201.79 63 C 201.97 64.03 202.28 65.22 202.73 66.58 C 202.79 66.72 202.79 66.84 202.75 66.93 C 202.71 67.03 202.6 67.12 202.44 67.2 C 202.3 67.28 202.17 67.3 202.04 67.24 C 201.91 67.18 201.82 67.09 201.77 66.95 C 201.35 65.78 201.04 64.68 200.83 63.64 C 200.62 62.6 200.52 61.54 200.52 60.45 C 200.52 56.81 201.85 53.76 204.5 51.3 C 207.15 48.84 210.31 47.61 213.98 47.61 C 217.65 47.61 220.86 48.84 223.52 51.3 C 226.19 53.76 227.52 56.81 227.52 60.45 C 227.52 61.73 227.05 62.81 226.1 63.7 C 225.16 64.59 224.02 65.03 222.68 65.03 C 221.34 65.03 220.24 64.59 219.28 63.7 C 218.32 62.81 217.84 61.73 217.84 60.45 C 217.84 59.42 217.46 58.56 216.72 57.87 C 215.98 57.18 215.08 56.83 214.05 56.83 C 213.02 56.83 212.08 57.18 211.32 57.87 C 210.56 58.56 210.17 59.43 210.17 60.45 C 210.17 63.26 211.01 65.6 212.69 67.47 C 214.37 69.35 216.49 70.66 219.04 71.41 C 219.21 71.47 219.32 71.55 219.39 71.66 C 219.46 71.77 219.47 71.9 219.41 72.04 C 219.35 72.15 219.28 72.25 219.18 72.35 C 219.08 72.45 218.94 72.47 218.74 72.41 Z M 204.42 43.04 C 204.36 43.04 204.27 43.03 204.15 43 C 204.02 42.97 203.95 42.9 203.92 42.79 C 203.86 42.71 203.84 42.6 203.86 42.46 C 203.87 42.32 203.92 42.22 204.01 42.17 C 205.54 41.31 207.16 40.66 208.86 40.23 C 210.57 39.8 212.3 39.58 214.05 39.58 C 215.8 39.58 217.51 39.79 219.17 40.2 C 220.84 40.62 222.45 41.24 224 42.08 C 224.17 42.16 224.27 42.27 224.31 42.41 C 224.35 42.55 224.35 42.67 224.29 42.79 C 224.23 42.9 224.14 42.98 224.02 43.04 C 223.89 43.1 223.78 43.1 223.67 43.04 C 222.2 42.18 220.65 41.55 219.02 41.16 C 217.39 40.77 215.74 40.58 214.04 40.58 C 212.34 40.58 210.7 40.8 209.1 41.23 C 207.5 41.66 205.94 42.26 204.41 43.04 Z M 209.92 71.96 C 208.36 70.29 207.14 68.55 206.25 66.73 C 205.36 64.91 204.92 62.82 204.92 60.46 C 204.92 58.1 205.82 55.91 207.61 54.23 C 209.4 52.55 211.55 51.71 214.05 51.71 C 216.55 51.71 218.7 52.55 220.49 54.23 C 222.28 55.91 223.18 57.99 223.18 60.46 C 223.18 60.6 223.14 60.72 223.06 60.81 C 222.98 60.91 222.85 60.96 222.68 60.96 C 222.6 60.96 222.49 60.91 222.37 60.81 C 222.25 60.71 222.18 60.6 222.18 60.46 C 222.18 58.27 221.38 56.43 219.78 54.94 C 218.18 53.45 216.27 52.71 214.05 52.71 C 211.83 52.71 209.92 53.45 208.32 54.94 C 206.72 56.43 205.92 58.27 205.92 60.46 C 205.92 62.77 206.32 64.72 207.13 66.34 C 207.94 67.95 209.1 69.58 210.63 71.22 C 210.77 71.39 210.82 71.53 210.78 71.66 C 210.74 71.78 210.69 71.89 210.63 71.97 C 210.55 72.05 210.44 72.1 210.3 72.12 C 210.16 72.13 210.04 72.09 209.92 71.97 Z M 222.34 68.96 C 219.95 68.96 217.88 68.15 216.11 66.54 C 214.35 64.93 213.46 62.9 213.46 60.46 C 213.46 60.32 213.5 60.2 213.58 60.11 C 213.66 60.01 213.79 59.96 213.96 59.96 C 214.1 59.96 214.22 60.01 214.31 60.11 C 214.41 60.21 214.46 60.33 214.46 60.46 C 214.46 62.65 215.24 64.45 216.81 65.86 C 218.38 67.26 220.22 67.96 222.33 67.96 C 222.64 67.96 222.95 67.95 223.27 67.92 C 223.59 67.89 223.9 67.85 224.21 67.8 C 224.32 67.8 224.42 67.83 224.5 67.88 C 224.58 67.93 224.64 68.03 224.67 68.17 C 224.73 68.31 224.71 68.43 224.63 68.52 C 224.55 68.62 224.42 68.68 224.25 68.71 C 224 68.79 223.7 68.86 223.35 68.9 C 223 68.94 222.66 68.96 222.33 68.96 Z"
+        android:fillColor="#1f1f1f"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_2"
+        android:pathData="M 134 72 L 134 60 C 134 46.75 144.75 36 158 36 C 171.25 36 182 46.75 182 60 L 182 72 M 124 72 L 192 72 C 195.712 72 199.275 73.476 201.899 76.101 C 204.524 78.725 206 82.288 206 86 L 206 146 C 206 149.712 204.524 153.275 201.899 155.899 C 199.275 158.524 195.712 160 192 160 L 124 160 C 120.288 160 116.725 158.524 114.101 155.899 C 111.476 153.275 110 149.712 110 146 L 110 86 C 110 82.288 111.476 78.725 114.101 76.101 C 116.725 73.476 120.288 72 124 72"
+        android:strokeColor="#e1e3e1"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <path
+        android:name="path_3"
+        android:pathData="M 155.67 114.83 C 153.22 114.83 151.15 113.98 149.46 112.29 C 147.77 110.6 146.92 108.53 146.92 106.08 C 146.92 103.63 147.77 101.56 149.46 99.87 C 151.15 98.18 153.22 97.33 155.67 97.33 C 158.12 97.33 160.19 98.18 161.88 99.87 C 163.57 101.56 164.42 103.63 164.42 106.08 C 164.42 108.53 163.57 110.6 161.88 112.29 C 160.19 113.98 158.12 114.83 155.67 114.83 Z M 172.59 137 L 169.67 133.5 L 169.67 124.92 C 168.27 124.38 167.14 123.51 166.29 122.32 C 165.44 121.13 165.01 119.8 165.01 118.32 C 165.01 116.38 165.69 114.72 167.05 113.36 C 168.41 112 170.06 111.32 172.01 111.32 C 173.96 111.32 175.61 112 176.97 113.36 C 178.33 114.72 179.01 116.37 179.01 118.32 C 179.01 119.8 178.58 121.13 177.73 122.32 C 176.88 123.51 175.75 124.37 174.35 124.92 L 174.35 125.33 L 176.68 127.66 L 174.35 129.99 L 176.68 132.32 L 172.6 136.99 Z M 172.01 121.83 C 172.98 121.83 173.81 121.49 174.49 120.81 C 175.17 120.13 175.51 119.3 175.51 118.33 C 175.51 117.36 175.17 116.53 174.49 115.85 C 173.81 115.17 172.98 114.83 172.01 114.83 C 171.04 114.83 170.21 115.17 169.53 115.85 C 168.85 116.53 168.51 117.36 168.51 118.33 C 168.51 119.3 168.85 120.13 169.53 120.81 C 170.21 121.49 171.04 121.83 172.01 121.83 Z M 160.46 119.85 C 160.65 121.44 161.15 122.9 161.95 124.23 C 162.75 125.55 163.77 126.7 165.01 127.67 L 165.01 134.67 L 137.01 134.67 L 137.01 129.13 C 137.01 127.81 137.38 126.6 138.12 125.51 C 138.86 124.42 139.81 123.59 140.98 123 C 143.27 121.83 145.66 120.96 148.13 120.38 C 150.6 119.8 153.12 119.5 155.68 119.5 C 156.46 119.5 157.26 119.53 158.07 119.59 C 158.89 119.65 159.68 119.74 160.46 119.85 Z"
+        android:fillColor="#e1e3e1"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_4"
+        android:pathData="M 63.481 27.427 L 102.118 17.075 C 102.457 16.984 102.815 16.984 103.154 17.075 C 103.493 17.166 103.802 17.344 104.05 17.592 C 104.298 17.841 104.477 18.15 104.568 18.489 L 110.779 41.671 C 110.87 42.01 110.87 42.367 110.779 42.706 C 110.689 43.045 110.51 43.355 110.262 43.603 C 110.014 43.851 109.704 44.03 109.365 44.121 L 70.728 54.473 C 70.216 54.611 69.67 54.539 69.211 54.274 C 68.751 54.008 68.416 53.571 68.279 53.059 L 62.067 29.877 C 61.976 29.538 61.976 29.181 62.067 28.842 C 62.158 28.503 62.336 28.193 62.585 27.945 C 62.833 27.697 63.142 27.518 63.481 27.427"
+        android:strokeColor="#444746"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"
+        android:strokeMiterLimit="10"/>
+    <path
+        android:name="path_5"
+        android:pathData="M 86.189 50.327 L 93.917 48.256 L 94.952 52.12 L 87.225 54.191 Z"
+        android:fillColor="#444746"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_6"
+        android:pathData="M 83.36 55.23 L 98.81 51.08"
+        android:strokeColor="#444746"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"
+        android:strokeMiterLimit="10"/>
+    <path
+        android:name="path_7"
+        android:pathData="M 90.9 37.2 L 88.3 35.7 L 89.4 33.8 C 89.59 33.48 89.37 33.07 89 33.05 L 81.88 32.62 C 81.47 32.6 81.21 33.05 81.43 33.39 L 85.36 39.34 C 85.56 39.65 86.02 39.64 86.21 39.31 L 87.31 37.41 L 89.91 38.91 C 90.39 39.19 91 39.02 91.28 38.54 C 91.56 38.06 91.39 37.45 90.91 37.17 Z"
+        android:fillColor="#444746"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_8"
+        android:pathData="M 86 160 L 222 160"
+        android:strokeColor="#e1e3e1"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <group android:name="group">
+        <path
+            android:name="path_9"
+            android:pathData="M 98 80 C 92.088 80 86.325 81.872 81.542 85.348 C 76.759 88.823 73.197 93.725 71.37 99.348 C 69.543 104.97 69.543 111.03 71.37 116.652 C 73.197 122.275 76.759 127.177 81.542 130.652 C 86.325 134.128 92.088 136 98 136 C 103.912 136 109.675 134.128 114.458 130.652 C 119.241 127.177 122.803 122.275 124.63 116.652 C 126.457 111.03 126.457 104.97 124.63 99.348 C 122.803 93.725 119.241 88.823 114.458 85.348 C 109.675 81.872 103.912 80 98 80 Z"
+            android:fillColor="#37be5f"
+            android:strokeWidth="1"/>
+        <path
+            android:name="path_10"
+            android:pathData="M 98 90.5 C 93.36 90.5 88.906 92.345 85.626 95.626 C 82.345 98.906 80.5 103.36 80.5 108 C 80.5 112.64 82.345 117.094 85.626 120.374 C 88.906 123.655 93.36 125.5 98 125.5 C 102.64 125.5 107.094 123.655 110.374 120.374 C 113.655 117.094 115.5 112.64 115.5 108 C 115.5 103.36 113.655 98.906 110.374 95.626 C 107.094 92.345 102.64 90.5 98 90.5 Z"
+            android:strokeColor="#1f1f1f"
+            android:strokeWidth="1"
+            android:strokeMiterLimit="10"/>
+        <path
+            android:name="path_11"
+            android:pathData="M 92.31 104.5 C 92.31 105.22 91.72 105.81 91 105.81 C 90.28 105.81 89.69 105.22 89.69 104.5 C 89.69 103.78 90.28 103.19 91 103.19 C 91.72 103.19 92.31 103.78 92.31 104.5 Z M 106.31 104.5 C 106.31 105.22 105.72 105.81 105 105.81 C 104.28 105.81 103.69 105.22 103.69 104.5 C 103.69 103.78 104.28 103.19 105 103.19 C 105.72 103.19 106.31 103.78 106.31 104.5 Z"
+            android:fillColor="#1f1f1f"
+            android:strokeWidth="1"/>
+        <path
+            android:name="path_12"
+            android:pathData="M 98.88 107.12 L 98.88 110.62 L 96.61 110.62 M 100.11 116.16 C 98.94 117.33 97.06 117.33 95.89 116.16 C 95.29 115.56 95 114.76 95.02 113.97"
+            android:strokeColor="#1f1f1f"
+            android:strokeWidth="1"
+            android:strokeMiterLimit="10"/>
+    </group>
+    <group android:name="group_2">
+        <path
+            android:name="path_13"
+            android:pathData="M 264.23 104 L 296.23 104"
+            android:strokeColor="#444746"
+            android:strokeWidth="1"
+            android:strokeLineCap="round"
+            android:strokeMiterLimit="10"/>
+        <path
+            android:name="path_14"
+            android:pathData="M 273.16 81.21 C 272.82 80.19 271.87 79.5 270.79 79.5 L 243.01 79.5 C 242.2 79.5 241.46 79.88 240.98 80.54 C 240.5 81.2 240.38 82.02 240.64 82.79 L 247.31 102.79 C 247.65 103.81 248.6 104.5 249.68 104.5 L 280.93 104.5 L 273.17 81.21 Z M 248.25 87.93 C 247.45 88.14 246.57 87.45 246.28 86.39 C 245.99 85.33 246.41 84.28 247.21 84.07 C 248.01 83.86 248.89 84.55 249.18 85.61 C 249.47 86.68 249.05 87.72 248.25 87.93 Z"
+            android:fillColor="#444746"
+            android:strokeWidth="1"/>
+        <group android:name="group_1">
+            <path
+                android:name="path_15"
+                android:pathData="M 279 83 L 285 83 M 277 77 L 285 69 M 271 75 L 271 69"
+                android:strokeColor="#444746"
+                android:strokeWidth="1"
+                android:strokeLineCap="round"/>
+        </group>
+    </group>
+    <path
+        android:name="path_16"
+        android:pathData="M 58.16 105.86 C 57.83 105.28 57.29 104.87 56.64 104.69 L 45.05 101.58 C 43.72 101.22 42.34 102.02 41.99 103.35 L 35.78 126.53 C 35.61 127.17 35.7 127.85 36.03 128.43 C 36.36 129.01 36.9 129.42 37.55 129.6 L 49.14 132.71 C 49.36 132.77 49.57 132.79 49.79 132.79 C 50.89 132.79 51.91 132.05 52.21 130.94 L 58.42 107.76 C 58.59 107.11 58.5 106.44 58.17 105.86 Z M 50.65 107.75 C 50.51 108.28 49.96 108.6 49.43 108.46 C 48.9 108.32 48.58 107.77 48.72 107.24 C 48.86 106.71 49.41 106.39 49.94 106.53 C 50.47 106.67 50.79 107.22 50.65 107.75 Z"
+        android:fillColor="#444746"
+        android:strokeWidth="1"/>
+    <path
+        android:name="path_17"
+        android:pathData="M 230 160 L 246 160"
+        android:strokeColor="#e1e3e1"
+        android:strokeWidth="1"
+        android:strokeLineCap="round"/>
+    <path
+        android:name="path_18"
+        android:pathData="M 204.63 80 C 202.38 75.27 197.58 72 192 72 L 190 72 L 190 74 C 190 77.31 192.69 80 196 80 L 204.63 80 Z"
+        android:fillColor="#e1e3e1"
+        android:strokeWidth="1"/>
+    <group android:name="group_4">
+        <path
+            android:name="path_19"
+            android:pathData="M 211.37 102.29 C 210.53 102.82 209.46 102.82 208.63 102.29 C 202.93 98.67 195.29 99.35 190.31 104.32 C 185.34 109.29 184.67 116.93 188.28 122.63 C 188.81 123.46 188.81 124.54 188.28 125.38 C 184.66 131.08 185.34 138.72 190.31 143.69 C 195.29 148.66 202.93 149.34 208.63 145.72 C 209.46 145.19 210.54 145.19 211.37 145.72 C 217.07 149.34 224.71 148.66 229.68 143.69 C 234.66 138.72 235.33 131.08 231.71 125.38 C 231.18 124.55 231.18 123.47 231.71 122.63 C 235.33 116.93 234.65 109.29 229.68 104.32 C 224.71 99.35 217.07 98.67 211.37 102.29 Z"
+            android:fillColor="#7cacf8"
+            android:strokeWidth="1"
+            android:fillType="evenOdd"/>
+        <group android:name="group_3">
+            <path
+                android:name="path_20"
+                android:pathData="M 198 110 C 197.47 110 196.961 110.211 196.586 110.586 C 196.211 110.961 196 111.47 196 112 C 196 112.53 196.211 113.039 196.586 113.414 C 196.961 113.789 197.47 114 198 114 C 198.53 114 199.039 113.789 199.414 113.414 C 199.789 113.039 200 112.53 200 112 C 200 111.47 199.789 110.961 199.414 110.586 C 199.039 110.211 198.53 110 198 110 Z M 210 110 C 209.47 110 208.961 110.211 208.586 110.586 C 208.211 110.961 208 111.47 208 112 C 208 112.53 208.211 113.039 208.586 113.414 C 208.961 113.789 209.47 114 210 114 C 210.53 114 211.039 113.789 211.414 113.414 C 211.789 113.039 212 112.53 212 112 C 212 111.47 211.789 110.961 211.414 110.586 C 211.039 110.211 210.53 110 210 110 Z M 222 110 C 221.47 110 220.961 110.211 220.586 110.586 C 220.211 110.961 220 111.47 220 112 C 220 112.53 220.211 113.039 220.586 113.414 C 220.961 113.789 221.47 114 222 114 C 222.53 114 223.039 113.789 223.414 113.414 C 223.789 113.039 224 112.53 224 112 C 224 111.47 223.789 110.961 223.414 110.586 C 223.039 110.211 222.53 110 222 110 Z M 198 122 C 197.47 122 196.961 122.211 196.586 122.586 C 196.211 122.961 196 123.47 196 124 C 196 124.53 196.211 125.039 196.586 125.414 C 196.961 125.789 197.47 126 198 126 C 198.53 126 199.039 125.789 199.414 125.414 C 199.789 125.039 200 124.53 200 124 C 200 123.47 199.789 122.961 199.414 122.586 C 199.039 122.211 198.53 122 198 122 Z M 210 122 C 209.47 122 208.961 122.211 208.586 122.586 C 208.211 122.961 208 123.47 208 124 C 208 124.53 208.211 125.039 208.586 125.414 C 208.961 125.789 209.47 126 210 126 C 210.53 126 211.039 125.789 211.414 125.414 C 211.789 125.039 212 124.53 212 124 C 212 123.47 211.789 122.961 211.414 122.586 C 211.039 122.211 210.53 122 210 122 Z M 222 122 C 221.47 122 220.961 122.211 220.586 122.586 C 220.211 122.961 220 123.47 220 124 C 220 124.53 220.211 125.039 220.586 125.414 C 220.961 125.789 221.47 126 222 126 C 222.53 126 223.039 125.789 223.414 125.414 C 223.789 125.039 224 124.53 224 124 C 224 123.47 223.789 122.961 223.414 122.586 C 223.039 122.211 222.53 122 222 122 Z M 198 134 C 197.47 134 196.961 134.211 196.586 134.586 C 196.211 134.961 196 135.47 196 136 C 196 136.53 196.211 137.039 196.586 137.414 C 196.961 137.789 197.47 138 198 138 C 198.53 138 199.039 137.789 199.414 137.414 C 199.789 137.039 200 136.53 200 136 C 200 135.47 199.789 134.961 199.414 134.586 C 199.039 134.211 198.53 134 198 134 Z M 210 134 C 209.47 134 208.961 134.211 208.586 134.586 C 208.211 134.961 208 135.47 208 136 C 208 136.53 208.211 137.039 208.586 137.414 C 208.961 137.789 209.47 138 210 138 C 210.53 138 211.039 137.789 211.414 137.414 C 211.789 137.039 212 136.53 212 136 C 212 135.47 211.789 134.961 211.414 134.586 C 211.039 134.211 210.53 134 210 134 Z M 222 134 C 221.47 134 220.961 134.211 220.586 134.586 C 220.211 134.961 220 135.47 220 136 C 220 136.53 220.211 137.039 220.586 137.414 C 220.961 137.789 221.47 138 222 138 C 222.53 138 223.039 137.789 223.414 137.414 C 223.789 137.039 224 136.53 224 136 C 224 135.47 223.789 134.961 223.414 134.586 C 223.039 134.211 222.53 134 222 134 Z"
+                android:fillColor="#1f1f1f"
+                android:strokeWidth="1"/>
+        </group>
+        <path
+            android:name="path_21"
+            android:pathData="M 222 112 L 198 136 L 222 136"
+            android:strokeColor="#1f1f1f"
+            android:strokeWidth="1"
+            android:strokeLineCap="round"/>
+    </group>
+</vector>
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index 8c50271..51dc233 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -49,7 +49,12 @@
 
 import java.time.Instant
 
-// Consider repo per screen, similar to view model?
+/**
+ * Client for interacting with Credential Manager. Also holds data inputs from it.
+ *
+ * IMPORTANT: instantiation of the object can fail if the data inputs aren't valid. Callers need
+ * to be equipped to handle this gracefully.
+ */
 class CredentialManagerRepo(
     private val context: Context,
     intent: Intent,
@@ -81,7 +86,6 @@
                     GetCredentialProviderData::class.java
                 ) ?: testGetCredentialProviderList()
             else -> {
-                // TODO: fail gracefully
                 throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
             }
         }
@@ -167,9 +171,9 @@
         )
     }
 
+    // IMPORTANT: new invocation should be mindful that this method can throw.
     private fun getCredentialInitialUiState(): GetCredentialUiState? {
         val providerEnabledList = GetFlowUtils.toProviderList(
-            // TODO: handle runtime cast error
             providerEnabledList as List<GetCredentialProviderData>, context
         )
         val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context)
@@ -179,9 +183,9 @@
         )
     }
 
+    // IMPORTANT: new invocation should be mindful that this method can throw.
     private fun getCreateProviderEnableListInitialUiState(): List<EnabledProviderInfo> {
         val providerEnabledList = CreateFlowUtils.toEnabledProviderList(
-            // Handle runtime cast error
             providerEnabledList as List<CreateCredentialProviderData>, context
         )
         return providerEnabledList
@@ -266,7 +270,7 @@
         return listOf(
             GetCredentialProviderData.Builder("io.enpass.app")
                 .setCredentialEntries(
-                    listOf<Entry>(
+                    listOf(
                         GetTestUtils.newPasswordEntry(
                             context, "key1", "subkey-1", "elisa.family@outlook.com", null,
                             Instant.ofEpochSecond(8000L)
@@ -285,9 +289,12 @@
                         ),
                     )
                 ).setAuthenticationEntries(
-            listOf<Entry>(
-                    GetTestUtils.newAuthenticationEntry(context, "key2", "subkey-1"),
-            )
+                    listOf(
+                        GetTestUtils.newAuthenticationEntry(
+                            context, "key2", "subkey-1", "locked-user1@gmail.com"),
+                        GetTestUtils.newAuthenticationEntry(
+                            context, "key2", "subkey-2", "locked-user2@gmail.com"),
+                    )
                 ).setActionChips(
                     listOf(
                         GetTestUtils.newActionEntry(
@@ -315,9 +322,8 @@
                         ),
                     )
                 ).setAuthenticationEntries(
-                     listOf<Entry>(
-                    GetTestUtils.newAuthenticationEntry(context, "key2", "subkey-1"),
-                     )
+                     listOf(GetTestUtils.newAuthenticationEntry(
+                         context, "key2", "subkey-1", "foo@email.com"))
                 ).setActionChips(
                     listOf(
                         GetTestUtils.newActionEntry(
@@ -388,7 +394,6 @@
             CreateCredentialRequest(
                 "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL",
                 credentialData,
-                // TODO: populate with actual data
                 /*candidateQueryData=*/ Bundle(),
                 /*isSystemProviderRequired=*/ false
             ),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 5136f04..bf69ef4 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -92,13 +92,20 @@
             handleDialogState(viewModel.uiState.dialogState)
         }
 
-        if (viewModel.uiState.createCredentialUiState != null) {
+        val createCredentialUiState = viewModel.uiState.createCredentialUiState
+        val getCredentialUiState = viewModel.uiState.getCredentialUiState
+        if (createCredentialUiState != null) {
             CreateCredentialScreen(
                 viewModel = viewModel,
+                createCredentialUiState = createCredentialUiState,
                 providerActivityLauncher = launcher
             )
-        } else if (viewModel.uiState.getCredentialUiState != null) {
-            GetCredentialScreen(viewModel = viewModel, providerActivityLauncher = launcher)
+        } else if (getCredentialUiState != null) {
+            GetCredentialScreen(
+                viewModel = viewModel,
+                getCredentialUiState = getCredentialUiState,
+                providerActivityLauncher = launcher
+            )
         } else {
             Log.d(Constants.LOG_TAG, "UI wasn't able to render neither get nor create flow")
             reportInstantiationErrorAndFinishActivity(credManRepo)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 6bf1513..30b4b86 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -255,14 +255,14 @@
     }
 
     fun createFlowOnEntrySelectedFromFirstUseScreen(activeEntry: ActiveEntry) {
+        val providerId = activeEntry.activeProvider.id
+        createFlowOnDefaultChanged(providerId)
         uiState = uiState.copy(
             createCredentialUiState = uiState.createCredentialUiState?.copy(
                 currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
                 activeEntry = activeEntry
             )
         )
-        val providerId = uiState.createCredentialUiState?.activeEntry?.activeProvider?.id
-        createFlowOnDefaultChanged(providerId)
     }
 
     fun createFlowOnDisabledProvidersSelected() {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index 167b956..ae87d95 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -17,6 +17,7 @@
 package com.android.credentialmanager
 
 import android.app.slice.Slice
+import android.app.slice.SliceItem
 import android.content.ComponentName
 import android.content.Context
 import android.content.pm.PackageManager
@@ -176,7 +177,9 @@
         }
 
 
-        /* From service data structure to UI credential entry list representation. */
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         private fun getCredentialOptionInfoList(
             providerId: String,
             credentialEntries: List<Entry>,
@@ -255,6 +258,9 @@
             }
         }
 
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         private fun getAuthenticationEntryList(
             providerId: String,
             providerDisplayName: String,
@@ -262,16 +268,24 @@
             authEntryList: List<Entry>,
         ): List<AuthenticationEntryInfo> {
             val result: MutableList<AuthenticationEntryInfo> = mutableListOf()
-            authEntryList.forEach {
+            authEntryList.forEach { entry ->
                 val structuredAuthEntry =
-                    AuthenticationAction.fromSlice(it.slice) ?: return@forEach
+                    AuthenticationAction.fromSlice(entry.slice) ?: return@forEach
+
+                // TODO: replace with official jetpack code.
+                val titleItem: SliceItem? = entry.slice.items.firstOrNull {
+                    it.hasHint(
+                        "androidx.credentials.provider.authenticationAction.SLICE_HINT_TITLE")
+                }
+                val title: String = titleItem?.text?.toString() ?: providerDisplayName
+
                 result.add(AuthenticationEntryInfo(
                     providerId = providerId,
-                    entryKey = it.key,
-                    entrySubkey = it.subkey,
+                    entryKey = entry.key,
+                    entrySubkey = entry.subkey,
                     pendingIntent = structuredAuthEntry.pendingIntent,
-                    fillInIntent = it.frameworkExtrasIntent,
-                    title = providerDisplayName,
+                    fillInIntent = entry.frameworkExtrasIntent,
+                    title = title,
                     icon = providerIcon,
                 ))
             }
@@ -279,7 +293,6 @@
         }
 
         private fun getRemoteEntry(providerId: String, remoteEntry: Entry?): RemoteEntryInfo? {
-            // TODO: should also call fromSlice after getting the official jetpack code.
             if (remoteEntry == null) {
                 return null
             }
@@ -294,6 +307,9 @@
             )
         }
 
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         private fun getActionEntryList(
             providerId: String,
             actionEntries: List<Entry>,
@@ -321,7 +337,9 @@
 
 class CreateFlowUtils {
     companion object {
-        // Returns the list (potentially empty) of enabled provider.
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         fun toEnabledProviderList(
             providerDataList: List<CreateCredentialProviderData>,
             context: Context,
@@ -346,7 +364,9 @@
             return providerList
         }
 
-        // Returns the list (potentially empty) of disabled provider.
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         fun toDisabledProviderList(
             providerDataList: List<DisabledProviderData>?,
             context: Context,
@@ -532,6 +552,9 @@
             } else null
         }
 
+        /**
+         * Note: caller required handle empty list due to parsing error.
+         */
         private fun toCreationOptionInfoList(
             providerId: String,
             creationEntries: List<Entry>,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
index e3bbaeb..a580c6c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/TestUtils.kt
@@ -23,10 +23,11 @@
 import android.content.Intent
 import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL
 import android.credentials.ui.Entry
-import android.graphics.drawable.Icon
 import android.net.Uri
 import android.provider.Settings
 import androidx.credentials.provider.CreateEntry
+import androidx.credentials.provider.PasswordCredentialEntry
+import androidx.credentials.provider.PublicKeyCredentialEntry
 
 import java.time.Instant
 
@@ -37,6 +38,7 @@
             context: Context,
             key: String,
             subkey: String,
+            title: String,
         ): Entry {
             val slice = Slice.Builder(
                 Uri.EMPTY, SliceSpec("AuthenticationAction", 0)
@@ -52,6 +54,11 @@
                     .build(),
                 /*subType=*/null
             )
+            slice.addText(
+                title,
+                null,
+                listOf("androidx.credentials.provider.authenticationAction.SLICE_HINT_TITLE")
+            )
             return Entry(
                 key,
                 subkey,
@@ -94,23 +101,6 @@
             )
         }
 
-        private const val SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-        private const val SLICE_HINT_TITLE =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_USER_NAME"
-        private const val SLICE_HINT_SUBTITLE =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-        private const val SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-        private const val SLICE_HINT_ICON =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_PROFILE_ICON"
-        private const val SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_PENDING_INTENT"
-        private const val SLICE_HINT_AUTO_ALLOWED =
-            "androidx.credentials.provider.passwordCredentialEntry.SLICE_HINT_AUTO_ALLOWED"
-        private const val AUTO_SELECT_TRUE_STRING = "true"
-        private const val AUTO_SELECT_FALSE_STRING = "false"
-
         internal fun newPasswordEntry(
             context: Context,
             key: String,
@@ -125,90 +115,13 @@
             val pendingIntent = PendingIntent.getActivity(
                 context, 1,
                 intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-                    or PendingIntent.FLAG_ONE_SHOT)
+                or PendingIntent.FLAG_ONE_SHOT)
             )
-            return Entry(
-                key,
-                subkey,
-                toPasswordSlice(userName, userDisplayName, pendingIntent, lastUsedTime),
-                Intent()
-            )
+            val passwordEntry = PasswordCredentialEntry.Builder(context, userName, pendingIntent)
+                .setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
+            return Entry(key, subkey, passwordEntry.slice, Intent())
         }
 
-        private fun toPasswordSlice(
-            title: CharSequence,
-            subTitle: CharSequence?,
-            pendingIntent: PendingIntent,
-            lastUsedTime: Instant?,
-            icon: Icon? = null,
-            isAutoSelectAllowed: Boolean = true
-        ): Slice {
-            val type = TYPE_PASSWORD_CREDENTIAL
-            val autoSelectAllowed = if (isAutoSelectAllowed) {
-                AUTO_SELECT_TRUE_STRING
-            } else {
-                AUTO_SELECT_FALSE_STRING
-            }
-            val sliceBuilder = Slice.Builder(
-                Uri.EMPTY, SliceSpec(
-                    type, 1
-                )
-            )
-                .addText(
-                    "Password", /*subType=*/null,
-                    listOf(SLICE_HINT_TYPE_DISPLAY_NAME)
-                )
-                .addText(
-                    title, /*subType=*/null,
-                    listOf(SLICE_HINT_TITLE)
-                )
-                .addText(
-                    subTitle, /*subType=*/null,
-                    listOf(SLICE_HINT_SUBTITLE)
-                )
-                .addText(
-                    autoSelectAllowed, /*subType=*/null,
-                    listOf(SLICE_HINT_AUTO_ALLOWED)
-                )
-            if (lastUsedTime != null) {
-                sliceBuilder.addLong(
-                    lastUsedTime.toEpochMilli(),
-                    /*subType=*/null,
-                    listOf(SLICE_HINT_LAST_USED_TIME_MILLIS)
-                )
-            }
-            if (icon != null) {
-                sliceBuilder.addIcon(
-                    icon, /*subType=*/null,
-                    listOf(SLICE_HINT_ICON)
-                )
-            }
-            sliceBuilder.addAction(
-                pendingIntent,
-                Slice.Builder(sliceBuilder)
-                    .addHints(listOf(SLICE_HINT_PENDING_INTENT))
-                    .build(),
-                /*subType=*/null
-            )
-            return sliceBuilder.build()
-        }
-
-
-        private const val PASSKEY_SLICE_HINT_TYPE_DISPLAY_NAME =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-        private const val PASSKEY_SLICE_HINT_TITLE =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_USER_NAME"
-        private const val PASSKEY_SLICE_HINT_SUBTITLE =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_TYPE_DISPLAY_NAME"
-        private const val PASSKEY_SLICE_HINT_LAST_USED_TIME_MILLIS =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_LAST_USED_TIME_MILLIS"
-        private const val PASSKEY_SLICE_HINT_ICON =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_PROFILE_ICON"
-        private const val PASSKEY_SLICE_HINT_PENDING_INTENT =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_PENDING_INTENT"
-        private const val PASSKEY_SLICE_HINT_AUTO_ALLOWED =
-            "androidx.credentials.provider.publicKeyCredEntry.SLICE_HINT_AUTO_ALLOWED"
-
         internal fun newPasskeyEntry(
             context: Context,
             key: String,
@@ -223,72 +136,11 @@
             val pendingIntent = PendingIntent.getActivity(
                 context, 1,
                 intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-                    or PendingIntent.FLAG_ONE_SHOT)
+                or PendingIntent.FLAG_ONE_SHOT)
             )
-            return Entry(
-                key, subkey, toPasskeySlice(
-                    userName, userDisplayName, pendingIntent, lastUsedTime
-                ),
-                Intent()
-            )
-        }
-
-        private fun toPasskeySlice(
-            title: CharSequence,
-            subTitle: CharSequence?,
-            pendingIntent: PendingIntent,
-            lastUsedTime: Instant?,
-            icon: Icon? = null,
-            isAutoSelectAllowed: Boolean = true
-        ): Slice {
-            val type = "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL"
-            val autoSelectAllowed = if (isAutoSelectAllowed) {
-                AUTO_SELECT_TRUE_STRING
-            } else {
-                AUTO_SELECT_FALSE_STRING
-            }
-            val sliceBuilder = Slice.Builder(
-                Uri.EMPTY, SliceSpec(
-                    type, 1
-                )
-            )
-                .addText(
-                    "Passkey", /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_TYPE_DISPLAY_NAME)
-                )
-                .addText(
-                    title, /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_TITLE)
-                )
-                .addText(
-                    subTitle, /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_SUBTITLE)
-                )
-                .addText(
-                    autoSelectAllowed, /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_AUTO_ALLOWED)
-                )
-            if (lastUsedTime != null) {
-                sliceBuilder.addLong(
-                    lastUsedTime.toEpochMilli(),
-                    /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_LAST_USED_TIME_MILLIS)
-                )
-            }
-            if (icon != null) {
-                sliceBuilder.addIcon(
-                    icon, /*subType=*/null,
-                    listOf(PASSKEY_SLICE_HINT_ICON)
-                )
-            }
-            sliceBuilder.addAction(
-                pendingIntent,
-                Slice.Builder(sliceBuilder)
-                    .addHints(listOf(PASSKEY_SLICE_HINT_PENDING_INTENT))
-                    .build(),
-                /*subType=*/null
-            )
-            return sliceBuilder.build()
+            val passkeyEntry = PublicKeyCredentialEntry.Builder(context, userName, pendingIntent)
+                .setDisplayName(userDisplayName).setLastUsedTime(lastUsedTime).build()
+            return Entry(key, subkey, passkeyEntry.slice, Intent())
         }
     }
 }
@@ -326,7 +178,7 @@
             val pendingIntent = PendingIntent.getActivity(
                 context, 1,
                 intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
-                    or PendingIntent.FLAG_ONE_SHOT)
+                or PendingIntent.FLAG_ONE_SHOT)
             )
             val credCountMap = mutableMapOf<String, Int>()
             passwordCount?.let { credCountMap.put(TYPE_PASSWORD_CREDENTIAL, it) }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index adb5467..823ee5b 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -5,6 +5,7 @@
 import androidx.activity.compose.ManagedActivityResultLauncher
 import androidx.activity.result.ActivityResult
 import androidx.activity.result.IntentSenderRequest
+import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -62,9 +63,9 @@
 @Composable
 fun CreateCredentialScreen(
     viewModel: CredentialSelectorViewModel,
+    createCredentialUiState: CreateCredentialUiState,
     providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
 ) {
-    val createCredentialUiState = viewModel.uiState.createCredentialUiState ?: return
     ModalBottomSheet(
         sheetContent = {
             // Hide the sheet content as opposed to the whole bottom sheet to maintain the scrim
@@ -94,6 +95,7 @@
                             requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
                             enabledProviderList = createCredentialUiState.enabledProviders,
                             providerInfo = createCredentialUiState.activeEntry?.activeProvider!!,
+                            hasDefaultProvider = createCredentialUiState.hasDefaultProvider,
                             createOptionInfo =
                             createCredentialUiState.activeEntry.activeEntryInfo
                                 as CreateOptionInfo,
@@ -164,8 +166,16 @@
 ) {
     ContainerCard() {
         Column() {
+            val onboardingImageResource = remember {
+                mutableStateOf(R.drawable.ic_passkeys_onboarding)
+            }
+            if (isSystemInDarkTheme()) {
+                onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark
+            } else {
+                onboardingImageResource.value = R.drawable.ic_passkeys_onboarding
+            }
             Image(
-                painter = painterResource(R.drawable.ic_passkeys_onboarding),
+                painter = painterResource(onboardingImageResource.value),
                 contentDescription = null,
                 modifier = Modifier.align(alignment = Alignment.CenterHorizontally)
                     .padding(top = 24.dp, bottom = 12.dp).size(316.dp, 168.dp)
@@ -264,7 +274,6 @@
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ProviderSelectionCard(
     requestDisplayInfo: RequestDisplayInfo,
@@ -351,7 +360,6 @@
                 thickness = 24.dp,
                 color = Color.Transparent
             )
-            // TODO: handle the error situation that if multiple remoteInfos exists
             enabledProviderList.forEach { enabledProvider ->
                 if (enabledProvider.remoteEntry != null) {
                     Row(
@@ -363,6 +371,7 @@
                             onMoreOptionsSelected
                         )
                     }
+                    return@forEach
                 }
             }
             Divider(
@@ -463,7 +472,6 @@
                             )
                         }
                     }
-                    // TODO: handle the error situation that if multiple remoteInfos exists
                     enabledProviderList.forEach {
                         if (it.remoteEntry != null) {
                             item {
@@ -472,6 +480,7 @@
                                     onRemoteEntrySelected = onRemoteEntrySelected,
                                 )
                             }
+                            return@forEach
                         }
                     }
                 }
@@ -549,6 +558,7 @@
     onOptionSelected: (BaseEntry) -> Unit,
     onConfirm: () -> Unit,
     onMoreOptionsSelected: () -> Unit,
+    hasDefaultProvider: Boolean,
 ) {
     ContainerCard() {
         Column() {
@@ -601,7 +611,6 @@
                     onOptionSelected = onOptionSelected
                 )
             }
-            var shouldShowMoreOptionsButton = false
             var createOptionsSize = 0
             var remoteEntry: RemoteInfo? = null
             enabledProviderList.forEach { enabledProvider ->
@@ -610,8 +619,13 @@
                 }
                 createOptionsSize += enabledProvider.createOptions.size
             }
-            if (createOptionsSize > 1 || remoteEntry != null) {
-                shouldShowMoreOptionsButton = true
+            val shouldShowMoreOptionsButton = if (!hasDefaultProvider) {
+                // User has already been presented with all options on the default provider
+                // selection screen. Don't show them again. Therefore, only show the more option
+                // button if remote option is present.
+                remoteEntry != null
+            } else {
+                createOptionsSize > 1 || remoteEntry != null
             }
             Row(
                 horizontalArrangement =
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 8b311fe..48ee287 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -76,9 +76,9 @@
 @Composable
 fun GetCredentialScreen(
     viewModel: CredentialSelectorViewModel,
+    getCredentialUiState: GetCredentialUiState,
     providerActivityLauncher: ManagedActivityResultLauncher<IntentSenderRequest, ActivityResult>
 ) {
-    val getCredentialUiState = viewModel.uiState.getCredentialUiState ?: return
     if (getCredentialUiState.currentScreenState != GetScreenState.REMOTE_ONLY) {
         ModalBottomSheet(
             sheetContent = {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
index e8b5b19..f6bb3cc 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/CustomizedAppBar.kt
@@ -119,7 +119,6 @@
         actions = actions,
         colors = topAppBarColors(),
         windowInsets = TopAppBarDefaults.windowInsets,
-        maxHeightWithoutTitle = 120.dp,
         pinnedHeight = ContainerHeight,
         scrollBehavior = scrollBehavior,
     )
@@ -261,7 +260,7 @@
  * A two-rows top app bar that is designed to be called by the Large and Medium top app bar
  * composables.
  *
- * @throws [IllegalArgumentException] if the given [maxHeightWithoutTitle] is equal or smaller than
+ * @throws [IllegalArgumentException] if the given [MaxHeightWithoutTitle] is equal or smaller than
  * the [pinnedHeight]
  */
 @OptIn(ExperimentalMaterial3Api::class)
@@ -277,11 +276,10 @@
     actions: @Composable RowScope.() -> Unit,
     windowInsets: WindowInsets,
     colors: TopAppBarColors,
-    maxHeightWithoutTitle: Dp,
     pinnedHeight: Dp,
     scrollBehavior: TopAppBarScrollBehavior?
 ) {
-    if (maxHeightWithoutTitle <= pinnedHeight) {
+    if (MaxHeightWithoutTitle <= pinnedHeight) {
         throw IllegalArgumentException(
             "A TwoRowsTopAppBar max height should be greater than its pinned height"
         )
@@ -289,7 +287,7 @@
     val pinnedHeightPx: Float
     val density = LocalDensity.current
     val maxHeightPx = density.run {
-        remember { mutableStateOf((maxHeightWithoutTitle + pinnedHeight).toPx()) }
+        remember { mutableStateOf((MaxHeightWithoutTitle + DefaultTitleHeight).toPx()) }
     }
     val titleBottomPaddingPx: Int
     density.run {
@@ -380,7 +378,7 @@
                     Box(modifier = Modifier.onGloballyPositioned { coordinates ->
                         density.run {
                             maxHeightPx.value =
-                                maxHeightWithoutTitle.toPx() + coordinates.size.height.toFloat()
+                                MaxHeightWithoutTitle.toPx() + coordinates.size.height.toFloat()
                         }
                     }) { title() }
                 },
@@ -610,6 +608,8 @@
 // Medium or Large app bar.
 private val TopTitleAlphaEasing = CubicBezierEasing(.8f, 0f, .8f, .15f)
 
+private val MaxHeightWithoutTitle = 124.dp
+private val DefaultTitleHeight = 52.dp
 private val ContainerHeight = 56.dp
 private val LargeTitleBottomPadding = 28.dp
 private val TopAppBarHorizontalPadding = 4.dp
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
index 037b737..1a3c0ab 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppStorageSize.kt
@@ -43,5 +43,5 @@
 private fun ApplicationInfo.calculateSizeBytes(context: Context): Long {
     val storageStatsManager = context.storageStatsManager
     val stats = storageStatsManager.queryStatsForPackage(storageUuid, packageName, userHandle)
-    return stats.codeBytes + stats.dataBytes + stats.cacheBytes
+    return stats.codeBytes + stats.dataBytes
 }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
index 066e28a..fcacc34 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppStorageSizeTest.kt
@@ -75,7 +75,7 @@
             }
         }
 
-        composeTestRule.waitUntil { storageSize.value == "123 B" }
+        composeTestRule.waitUntil { storageSize.value == "120 B" }
     }
 
     companion object {
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 333aeba..bdd941d 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -49,13 +49,16 @@
     },
     {
       // Permission indicators
-      "name": "CtsPermission4TestCases",
+      "name": "CtsPermission3TestCases",
       "options": [
         {
           "exclude-annotation": "org.junit.Ignore"
         },
         {
           "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "include-filter": "android.permission3.cts.CameraMicIndicatorsPermissionTest"
         }
       ]
     },
diff --git a/packages/SystemUI/res/drawable/ic_sound_bars_anim.xml b/packages/SystemUI/res/drawable/ic_sound_bars_anim.xml
new file mode 100644
index 0000000..43fdec5
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sound_bars_anim.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector android:height="30dp" android:width="36dp" android:viewportHeight="30"
+                android:viewportWidth="36">
+            <group android:name="_R_G">
+                <group android:name="_R_G_L_2_G" android:translateX="5.939"
+                       android:translateY="26.583" android:pivotY="0.379" android:scaleX="31.96611"
+                       android:scaleY="31.96611">
+                    <path android:name="_R_G_L_2_G_D_0_P_0" android:fillColor="#ffffff"
+                          android:fillAlpha="1" android:fillType="nonZero"
+                          android:pathData=" M0.09 0.27 C0.09,0.27 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.27 -0.09,0.27 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.27c "/>
+                </group>
+                <group android:name="_R_G_L_1_G_N_3_T_0" android:translateX="5.939"
+                       android:translateY="26.583" android:pivotY="0.379" android:scaleX="31.96611"
+                       android:scaleY="31.96611">
+                    <group android:name="_R_G_L_1_G" android:translateX="0.379"
+                           android:translateY="0.19">
+                        <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#ffffff"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M0.09 0.09 C0.09,0.09 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,0.09 -0.1,0.09 C-0.1,0.04 -0.05,-0.01 0,-0.01 C0.05,-0.01 0.09,0.04 0.09,0.09c "/>
+                    </group>
+                </group>
+                <group android:name="_R_G_L_0_G_N_3_T_0" android:translateX="5.939"
+                       android:translateY="26.583" android:pivotY="0.379" android:scaleX="31.96611"
+                       android:scaleY="31.96611">
+                    <group android:name="_R_G_L_0_G" android:translateX="0.758">
+                        <path android:name="_R_G_L_0_G_D_0_P_0" android:fillColor="#ffffff"
+                              android:fillAlpha="1" android:fillType="nonZero"
+                              android:pathData=" M0.09 0.28 C0.09,0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.28 -0.09,0.28 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.28c "/>
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_2_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="333"
+                        android:startOffset="0"
+                        android:valueFrom="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueTo="M0.09 0.27 C0.09,0.27 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.27 -0.09,0.27 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.27c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.595,0 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="333"
+                        android:startOffset="333"
+                        android:valueFrom="M0.09 0.27 C0.09,0.27 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.27 -0.09,0.27 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.27c "
+                        android:valueTo="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.595,0 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="333"
+                        android:startOffset="667"
+                        android:valueFrom="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueTo="M0.09 0.27 C0.09,0.27 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.27 -0.09,0.27 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.27c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.595,0 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="83"
+                        android:startOffset="0"
+                        android:valueFrom="M0.09 -0.44 C0.09,-0.44 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.44 -0.1,-0.44 C-0.1,-0.5 -0.05,-0.54 0,-0.54 C0.05,-0.54 0.09,-0.5 0.09,-0.44c "
+                        android:valueTo="M0.09 -0.49 C0.09,-0.49 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.49 -0.1,-0.49 C-0.1,-0.54 -0.05,-0.58 0,-0.58 C0.05,-0.58 0.09,-0.54 0.09,-0.49c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="333"
+                        android:startOffset="83"
+                        android:valueFrom="M0.09 -0.49 C0.09,-0.49 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.49 -0.1,-0.49 C-0.1,-0.54 -0.05,-0.58 0,-0.58 C0.05,-0.58 0.09,-0.54 0.09,-0.49c "
+                        android:valueTo="M0.09 0.09 C0.09,0.09 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,0.09 -0.1,0.09 C-0.1,0.04 -0.05,-0.01 0,-0.01 C0.05,-0.01 0.09,0.04 0.09,0.09c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.595,0 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="250"
+                        android:startOffset="417"
+                        android:valueFrom="M0.09 0.09 C0.09,0.09 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,0.09 -0.1,0.09 C-0.1,0.04 -0.05,-0.01 0,-0.01 C0.05,-0.01 0.09,0.04 0.09,0.09c "
+                        android:valueTo="M0.09 -0.44 C0.09,-0.44 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.44 -0.1,-0.44 C-0.1,-0.5 -0.05,-0.54 0,-0.54 C0.05,-0.54 0.09,-0.5 0.09,-0.44c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.595,0 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="83"
+                        android:startOffset="667"
+                        android:valueFrom="M0.09 -0.44 C0.09,-0.44 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.44 -0.1,-0.44 C-0.1,-0.5 -0.05,-0.54 0,-0.54 C0.05,-0.54 0.09,-0.5 0.09,-0.44c "
+                        android:valueTo="M0.09 -0.49 C0.09,-0.49 0.09,0.09 0.09,0.09 C0.09,0.15 0.05,0.19 0,0.19 C-0.05,0.19 -0.09,0.15 -0.09,0.09 C-0.09,0.09 -0.1,-0.49 -0.1,-0.49 C-0.1,-0.54 -0.05,-0.58 0,-0.58 C0.05,-0.58 0.09,-0.54 0.09,-0.49c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="167"
+                        android:startOffset="0"
+                        android:valueFrom="M0.09 -0.02 C0.09,-0.02 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.02 -0.09,-0.02 C-0.09,-0.07 -0.05,-0.12 0,-0.12 C0.05,-0.12 0.09,-0.07 0.09,-0.02c "
+                        android:valueTo="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="333"
+                        android:startOffset="167"
+                        android:valueFrom="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueTo="M0.09 0.28 C0.09,0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.28 -0.09,0.28 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.28c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.595,0 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="167"
+                        android:startOffset="500"
+                        android:valueFrom="M0.09 0.28 C0.09,0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,0.28 -0.09,0.28 C-0.09,0.22 -0.05,0.18 0,0.18 C0.05,0.18 0.09,0.22 0.09,0.28c "
+                        android:valueTo="M0.09 -0.02 C0.09,-0.02 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.02 -0.09,-0.02 C-0.09,-0.07 -0.05,-0.12 0,-0.12 C0.05,-0.12 0.09,-0.07 0.09,-0.02c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.595,0 0.833,0.833 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                        android:repeatCount="infinite"
+                        android:repeatMode="reverse"
+                        android:propertyName="pathData" android:duration="167"
+                        android:startOffset="667"
+                        android:valueFrom="M0.09 -0.02 C0.09,-0.02 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.02 -0.09,-0.02 C-0.09,-0.07 -0.05,-0.12 0,-0.12 C0.05,-0.12 0.09,-0.07 0.09,-0.02c "
+                        android:valueTo="M0.09 -0.28 C0.09,-0.28 0.09,0.28 0.09,0.28 C0.09,0.34 0.05,0.38 0,0.38 C-0.05,0.38 -0.09,0.34 -0.09,0.28 C-0.09,0.28 -0.09,-0.28 -0.09,-0.28 C-0.09,-0.34 -0.05,-0.38 0,-0.38 C0.05,-0.38 0.09,-0.34 0.09,-0.28c "
+                        android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator
+                            android:pathData="M 0.0,0.0 c0.167,0.167 0.372,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:repeatCount="infinite"
+                    android:repeatMode="reverse"
+                    android:propertyName="translateX"
+                    android:duration="683"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
index 102f208..055cd52 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamMonitor.java
@@ -16,19 +16,23 @@
 
 package com.android.systemui.dreams;
 
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
 import android.util.Log;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dreams.callbacks.DreamStatusBarStateCallback;
 import com.android.systemui.dreams.conditions.DreamCondition;
 import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * A {@link CoreStartable} to retain a monitor for tracking dreaming.
  */
-public class DreamMonitor implements CoreStartable {
+public class DreamMonitor extends ConditionalCoreStartable {
     private static final String TAG = "DreamMonitor";
 
     // We retain a reference to the monitor so it is not garbage-collected.
@@ -39,14 +43,17 @@
 
     @Inject
     public DreamMonitor(Monitor monitor, DreamCondition dreamCondition,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor pretextMonitor,
             DreamStatusBarStateCallback callback) {
+        super(pretextMonitor);
         mConditionMonitor = monitor;
         mDreamCondition = dreamCondition;
         mCallback = callback;
 
     }
+
     @Override
-    public void start() {
+    protected void onStart() {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "started");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
index 87c5f51..a2dcdf5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayRegistrant.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams;
 
 import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -33,8 +34,9 @@
 import android.service.dreams.IDreamManager;
 import android.util.Log;
 
-import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -43,7 +45,7 @@
  * {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
  * the designated dream overlay component.
  */
-public class DreamOverlayRegistrant implements CoreStartable {
+public class DreamOverlayRegistrant extends ConditionalCoreStartable {
     private static final String TAG = "DreamOverlayRegistrant";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private final IDreamManager mDreamManager;
@@ -102,7 +104,9 @@
 
     @Inject
     public DreamOverlayRegistrant(Context context, @Main Resources resources,
-            @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent) {
+            @Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+        super(monitor);
         mContext = context;
         mResources = resources;
         mDreamManager = IDreamManager.Stub.asInterface(
@@ -111,7 +115,7 @@
     }
 
     @Override
-    public void start() {
+    protected void onStart() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         filter.addDataSchemeSpecificPart(mOverlayServiceComponent.getPackageName(),
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
index ee2f1af..244212b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java
@@ -16,27 +16,31 @@
 
 package com.android.systemui.dreams.complication;
 
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
+
 import android.database.ContentObserver;
 import android.os.UserHandle;
 import android.provider.Settings;
 
 import com.android.settingslib.dream.DreamBackend;
-import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 
 /**
  * {@link ComplicationTypesUpdater} observes the state of available complication types set by the
  * user, and pushes updates to {@link DreamOverlayStateController}.
  */
 @SysUISingleton
-public class ComplicationTypesUpdater implements CoreStartable {
+public class ComplicationTypesUpdater extends ConditionalCoreStartable {
     private final DreamBackend mDreamBackend;
     private final Executor mExecutor;
     private final SecureSettings mSecureSettings;
@@ -48,7 +52,9 @@
             DreamBackend dreamBackend,
             @Main Executor executor,
             SecureSettings secureSettings,
-            DreamOverlayStateController dreamOverlayStateController) {
+            DreamOverlayStateController dreamOverlayStateController,
+            @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+        super(monitor);
         mDreamBackend = dreamBackend;
         mExecutor = executor;
         mSecureSettings = secureSettings;
@@ -56,7 +62,7 @@
     }
 
     @Override
-    public void start() {
+    public void onStart() {
         final ContentObserver settingsObserver = new ContentObserver(null /*handler*/) {
             @Override
             public void onChange(boolean selfChange) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
index 77e1fc9..bb1e6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -18,11 +18,14 @@
 
 import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.view.View;
 
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -60,7 +63,7 @@
      * {@link CoreStartable} responsible for registering {@link DreamClockTimeComplication} with
      * SystemUI.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final DreamClockTimeComplication mComplication;
 
@@ -70,13 +73,15 @@
         @Inject
         public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
-                DreamClockTimeComplication dreamClockTimeComplication) {
+                DreamClockTimeComplication dreamClockTimeComplication,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = dreamClockTimeComplication;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mDreamOverlayStateController.addComplication(mComplication);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
index 1065b94..7f395d8 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamHomeControlsComplication.java
@@ -21,6 +21,7 @@
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
 import static com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.Context;
 import android.content.Intent;
@@ -42,7 +43,9 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import java.util.List;
 
@@ -75,7 +78,7 @@
     /**
      * {@link CoreStartable} for registering the complication with SystemUI on startup.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamHomeControlsComplication mComplication;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final ControlsComponent mControlsComponent;
@@ -105,14 +108,16 @@
         @Inject
         public Registrant(DreamHomeControlsComplication complication,
                 DreamOverlayStateController dreamOverlayStateController,
-                ControlsComponent controlsComponent) {
+                ControlsComponent controlsComponent,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mComplication = complication;
             mControlsComponent = controlsComponent;
             mDreamOverlayStateController = dreamOverlayStateController;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mControlsComponent.getControlsListingController().ifPresent(
                     c -> c.addCallback(mControlsCallback));
             mDreamOverlayStateController.addCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index c3aaf0c..e39073b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.complication;
 
 import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_SMARTSPACE_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.dagger.DreamModule.DREAM_PRETEXT_MONITOR;
 
 import android.content.Context;
 import android.os.Parcelable;
@@ -28,6 +29,8 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
 
 import java.util.List;
 
@@ -61,7 +64,7 @@
      * {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
      * SystemUI.
      */
-    public static class Registrant implements CoreStartable {
+    public static class Registrant extends ConditionalCoreStartable {
         private final DreamSmartspaceController mSmartSpaceController;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final SmartSpaceComplication mComplication;
@@ -81,14 +84,16 @@
         public Registrant(
                 DreamOverlayStateController dreamOverlayStateController,
                 SmartSpaceComplication smartSpaceComplication,
-                DreamSmartspaceController smartSpaceController) {
+                DreamSmartspaceController smartSpaceController,
+                @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) {
+            super(monitor);
             mDreamOverlayStateController = dreamOverlayStateController;
             mComplication = smartSpaceComplication;
             mSmartSpaceController = smartSpaceController;
         }
 
         @Override
-        public void start() {
+        public void onStart() {
             mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() {
                 @Override
                 public void onStateChanged() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 7d8389a..3a37c6f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -30,11 +30,18 @@
 import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
 import com.android.systemui.dreams.dreamcomplication.dagger.ComplicationComponent;
 import com.android.systemui.dreams.touch.scrim.dagger.ScrimModule;
+import com.android.systemui.process.condition.UserProcessCondition;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.IntoSet;
 
 import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
@@ -57,6 +64,8 @@
     String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled";
 
     String DREAM_SUPPORTED = "dream_supported";
+    String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions";
+    String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor";
 
     /**
      * Provides the dream component
@@ -115,4 +124,19 @@
     static boolean providesDreamSupported(@Main Resources resources) {
         return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported);
     }
+
+    /** */
+    @Binds
+    @IntoSet
+    @Named(DREAM_PRETEXT_CONDITIONS)
+    Condition bindsUserProcessCondition(UserProcessCondition condition);
+
+    /** */
+    @Provides
+    @Named(DREAM_PRETEXT_MONITOR)
+    static Monitor providesDockerPretextMonitor(
+            @Main Executor executor,
+            @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) {
+        return new Monitor(executor, pretextConditions);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5155a82..d594fce 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -367,9 +367,6 @@
     // TODO(b/265045965): Tracking Bug
     val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot")
 
-    @JvmField
-    val ENABLE_LOW_LIGHT_CLOCK_UNDOCKED = unreleasedFlag(1004, "enable_low_light_clock_undocked")
-
     // 1100 - windowing
     @Keep
     @JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
index 348d941..ccd4060 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/Diffable.kt
@@ -79,10 +79,10 @@
     }
 }
 
-/**
- * Each time the boolean flow is updated with a new value that's different from the previous value,
- * logs the new value to the given [tableLogBuffer].
- */
+// Here and below: Various Flow<SomeType> extension functions that are effectively equivalent to the
+// above [logDiffsForTable] method.
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Boolean>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -100,10 +100,8 @@
         newVal
     }
 }
-/**
- * Each time the Int flow is updated with a new value that's different from the previous value, logs
- * the new value to the given [tableLogBuffer].
- */
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<Int>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -122,10 +120,26 @@
     }
 }
 
-/**
- * Each time the String? flow is updated with a new value that's different from the previous value,
- * logs the new value to the given [tableLogBuffer].
- */
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
+fun Flow<Int?>.logDiffsForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: Int?,
+): Flow<Int?> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue)
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal, newVal: Int? ->
+        if (prevVal != newVal) {
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal)
+        }
+        newVal
+    }
+}
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
 fun Flow<String?>.logDiffsForTable(
     tableLogBuffer: TableLogBuffer,
     columnPrefix: String,
@@ -143,3 +157,23 @@
         newVal
     }
 }
+
+/** See [logDiffsForTable(TableLogBuffer, String, T)]. */
+fun <T> Flow<List<T>>.logDiffsForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: List<T>,
+): Flow<List<T>> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue.toString())
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal, newVal: List<T> ->
+        if (prevVal != newVal) {
+            // TODO(b/267761156): Can we log list changes without using toString?
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal.toString())
+        }
+        newVal
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
index 68c297f..4880f80 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableChange.kt
@@ -27,7 +27,7 @@
     var columnName: String = "",
     var type: DataType = DataType.EMPTY,
     var bool: Boolean = false,
-    var int: Int = 0,
+    var int: Int? = null,
     var str: String? = null,
 ) {
     /** Resets to default values so that the object can be recycled. */
@@ -54,7 +54,7 @@
     }
 
     /** Sets this to store an int change. */
-    fun set(value: Int) {
+    fun set(value: Int?) {
         type = DataType.INT
         int = value
     }
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 2c299d6..1712dab 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -138,7 +138,7 @@
     }
 
     /** Logs a Int change. */
-    fun logChange(prefix: String, columnName: String, value: Int) {
+    fun logChange(prefix: String, columnName: String, value: Int?) {
         logChange(systemClock.currentTimeMillis(), prefix, columnName, value)
     }
 
@@ -155,7 +155,7 @@
         change.set(value)
     }
 
-    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int) {
+    private fun logChange(timestamp: Long, prefix: String, columnName: String, value: Int?) {
         val change = obtain(timestamp, prefix, columnName)
         change.set(value)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 99e5740..00e9a79 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -27,6 +27,7 @@
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.Log;
@@ -235,7 +236,7 @@
                         mSubTitleText.setText(device.getSubtextString());
                         Drawable deviceStatusIcon =
                                 device.hasOngoingSession() ? mContext.getDrawable(
-                                        R.drawable.media_output_status_session)
+                                        R.drawable.ic_sound_bars_anim)
                                         : Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(
                                                 device,
                                                 mContext);
@@ -334,7 +335,7 @@
                             && mController.isSubStatusSupported()) {
                         Drawable deviceStatusIcon =
                                 device.hasOngoingSession() ? mContext.getDrawable(
-                                        R.drawable.media_output_status_session)
+                                        R.drawable.ic_sound_bars_anim)
                                         : Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(
                                                 device,
                                                 mContext);
@@ -399,6 +400,9 @@
         private void updateDeviceStatusIcon(Drawable drawable) {
             mStatusIcon.setImageDrawable(drawable);
             mStatusIcon.setColorFilter(mController.getColorItemContent());
+            if (drawable instanceof AnimatedVectorDrawable) {
+                ((AnimatedVectorDrawable) drawable).start();
+            }
         }
 
         private void updateProgressBarColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
index 7db293d..245cf89 100644
--- a/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/process/ProcessWrapper.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.process;
 
+import javax.inject.Inject;
+
 /**
  * A simple wrapper that provides access to process-related details. This facilitates testing by
  * providing a mockable target around these details.
  */
 public class ProcessWrapper {
+    @Inject
+    public ProcessWrapper() {}
+
     public int getUserHandleIdentifier() {
         return android.os.Process.myUserHandle().getIdentifier();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index f53f824..9286d29 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2144,6 +2144,7 @@
     void flingToHeight(float vel, boolean expand, float target,
             float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
         mLastFlingWasExpanding = expand;
+        mShadeLog.logLastFlingWasExpanding(expand);
         mHeadsUpTouchHelper.notifyFling(!expand);
         mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
         setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
@@ -4624,6 +4625,7 @@
         ipw.println(mBlockingExpansionForCurrentTouch);
         ipw.print("mExpectingSynthesizedDown="); ipw.println(mExpectingSynthesizedDown);
         ipw.print("mLastEventSynthesizedDown="); ipw.println(mLastEventSynthesizedDown);
+        ipw.print("mLastFlingWasExpanding="); ipw.println(mLastFlingWasExpanding);
         ipw.print("mInterpolatedDarkAmount="); ipw.println(mInterpolatedDarkAmount);
         ipw.print("mLinearDarkAmount="); ipw.println(mLinearDarkAmount);
         ipw.print("mPulsing="); ipw.println(mPulsing);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 11617be..26c839de 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.log.dagger.ShadeLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel
-import com.android.systemui.plugins.log.LogMessage
 import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
@@ -234,4 +233,19 @@
             }
         )
     }
+
+    fun logLastFlingWasExpanding(
+            expand: Boolean
+    ) {
+        buffer.log(
+                TAG,
+                LogLevel.VERBOSE,
+                {
+                    bool1 = expand
+                },
+                {
+                    "NPVC mLastFlingWasExpanding set to: $bool1"
+                }
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
index 058042c..0c95eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -71,11 +71,9 @@
      * marking them as relevant for setup are allowed to show when device is unprovisioned
      */
     private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        final boolean hasPermission = checkUidPermission(
-                Manifest.permission.NOTIFICATION_DURING_SETUP,
-                sbn.getUid()) == PackageManager.PERMISSION_GRANTED;
-        return hasPermission
-                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+        // system_server checks the permission so systemui can just check whether the
+        // extra exists
+        return sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
     }
 
     private int checkUidPermission(String permission, int uid) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 416bc71..5408afb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -163,7 +163,8 @@
         for (int i = currentSlots.size() - 1; i >= 0; i--) {
             Slot s = currentSlots.get(i);
             slotsToReAdd.put(s, s.getHolderList());
-            removeAllIconsForSlot(s.getName());
+            // Don't force here because the new pipeline properly handles the tuner settings
+            removeAllIconsForSlot(s.getName(), /* force */ false);
         }
 
         // Add them all back
@@ -285,7 +286,7 @@
         // Because of the way we cache the icon holders, we need to remove everything any time
         // we get a new set of subscriptions. This might change in the future, but is required
         // to support demo mode for now
-        removeAllIconsForSlot(slotName);
+        removeAllIconsForSlot(slotName, /* force */ true);
 
         Collections.reverse(subIds);
 
@@ -428,6 +429,14 @@
     /** */
     @Override
     public void removeIcon(String slot, int tag) {
+        // If the new pipeline is on for this icon, don't allow removal, since the new pipeline
+        // will never call this method
+        if (mStatusBarPipelineFlags.isIconControlledByFlags(slot)) {
+            Log.i(TAG, "Ignoring removal of (" + slot + "). "
+                    + "It should be controlled elsewhere");
+            return;
+        }
+
         if (mStatusBarIconList.getIconHolder(slot, tag) == null) {
             return;
         }
@@ -444,6 +453,18 @@
     /** */
     @Override
     public void removeAllIconsForSlot(String slotName) {
+        removeAllIconsForSlot(slotName, /* force */ false);
+    }
+
+    private void removeAllIconsForSlot(String slotName, Boolean force) {
+        // If the new pipeline is on for this icon, don't allow removal, since the new pipeline
+        // will never call this method
+        if (!force && mStatusBarPipelineFlags.isIconControlledByFlags(slotName)) {
+            Log.i(TAG, "Ignoring removal of (" + slotName + "). "
+                    + "It should be controlled elsewhere");
+            return;
+        }
+
         Slot slot = mStatusBarIconList.getSlot(slotName);
         if (!slot.hasIconsInSlot()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 15fed32..4a684d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline
 
+import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -23,7 +24,15 @@
 
 /** All flagging methods related to the new status bar pipeline (see b/238425913). */
 @SysUISingleton
-class StatusBarPipelineFlags @Inject constructor(private val featureFlags: FeatureFlags) {
+class StatusBarPipelineFlags
+@Inject
+constructor(
+    context: Context,
+    private val featureFlags: FeatureFlags,
+) {
+    private val mobileSlot = context.getString(com.android.internal.R.string.status_bar_mobile)
+    private val wifiSlot = context.getString(com.android.internal.R.string.status_bar_wifi)
+
     /** True if we should display the mobile icons using the new status bar data pipeline. */
     fun useNewMobileIcons(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS)
 
@@ -54,4 +63,13 @@
      */
     fun useDebugColoring(): Boolean =
         featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
+
+    /**
+     * For convenience in the StatusBarIconController, we want to gate some actions based on slot
+     * name and the flag together.
+     *
+     * @return true if this icon is controlled by any of the status bar pipeline flags
+     */
+    fun isIconControlledByFlags(slotName: String): Boolean =
+        slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.kt
new file mode 100644
index 0000000..2ac9ab3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/MobileSummaryLog.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.statusbar.pipeline.dagger
+
+import javax.inject.Qualifier
+
+/**
+ * Logs for mobile data that's **the same across all connections**.
+ *
+ * This buffer should only be used for the mobile parent classes like [MobileConnectionsRepository]
+ * and [MobileIconsInteractor]. It should *not* be used for classes that represent an individual
+ * connection, like [MobileConnectionRepository] or [MobileIconInteractor].
+ */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class MobileSummaryLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 5f3b0dc..60de1a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -118,5 +118,12 @@
         fun provideAirplaneTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
             return factory.create("AirplaneTableLog", 30)
         }
+
+        @Provides
+        @SysUISingleton
+        @MobileSummaryLog
+        fun provideMobileSummaryLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("MobileSummaryLog", 100)
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
index e618905..97a537a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileConnectivityModel.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.pipeline.mobile.data.model
 
 import android.net.NetworkCapabilities
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
 
 /** Provides information about a mobile network connection */
 data class MobileConnectivityModel(
@@ -24,4 +26,24 @@
     val isConnected: Boolean = false,
     /** Whether the mobile transport is validated [NetworkCapabilities.NET_CAPABILITY_VALIDATED] */
     val isValidated: Boolean = false,
-)
+) : Diffable<MobileConnectivityModel> {
+    // TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes?
+    override fun logDiffs(prevVal: MobileConnectivityModel, row: TableRowLogger) {
+        if (prevVal.isConnected != isConnected) {
+            row.logChange(COL_IS_CONNECTED, isConnected)
+        }
+        if (prevVal.isValidated != isValidated) {
+            row.logChange(COL_IS_VALIDATED, isValidated)
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        row.logChange(COL_IS_CONNECTED, isConnected)
+        row.logChange(COL_IS_VALIDATED, isValidated)
+    }
+
+    companion object {
+        private const val COL_IS_CONNECTED = "isConnected"
+        private const val COL_IS_VALIDATED = "isValidated"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
index c783b12..f5041d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepository.kt
@@ -91,9 +91,6 @@
             .map { it.toMobileConnectionModel() }
             .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectionModel())
 
-    // TODO(b/238425913): Add logging to this class.
-    // TODO(b/238425913): Make sure SignalStrength.getEmptyState is used when appropriate.
-
     // Carrier merged is never roaming.
     override val cdmaRoaming: StateFlow<Boolean> = MutableStateFlow(false).asStateFlow()
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
index dd2cc92..f17791b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepository.kt
@@ -117,11 +117,22 @@
     override val connectionInfo =
         activeRepo
             .flatMapLatest { it.connectionInfo }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                initialValue = activeRepo.value.connectionInfo.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.connectionInfo.value)
 
     override val dataEnabled =
         activeRepo
             .flatMapLatest { it.dataEnabled }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                columnName = "dataEnabled",
+                initialValue = activeRepo.value.dataEnabled.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.dataEnabled.value)
 
     override val numberOfLevels =
@@ -132,6 +143,11 @@
     override val networkName =
         activeRepo
             .flatMapLatest { it.networkName }
+            .logDiffsForTable(
+                tableLogBuffer,
+                columnPrefix = "",
+                initialValue = activeRepo.value.networkName.value,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), activeRepo.value.networkName.value)
 
     class Factory
@@ -168,7 +184,7 @@
             const val MOBILE_CONNECTION_BUFFER_SIZE = 100
 
             /** Returns a log buffer name for a mobile connection with the given [subId]. */
-            fun tableBufferLogName(subId: Int): String = "MobileConnectionLog [$subId]"
+            fun tableBufferLogName(subId: Int): String = "MobileConnectionLog[$subId]"
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 76fef35..cfc4cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.table.TableLogBuffer
-import com.android.systemui.log.table.logDiffsForTable
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
@@ -62,7 +61,6 @@
 import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.mapNotNull
@@ -87,7 +85,7 @@
     private val mobileMappingsProxy: MobileMappingsProxy,
     bgDispatcher: CoroutineDispatcher,
     logger: ConnectivityPipelineLogger,
-    mobileLogger: TableLogBuffer,
+    override val tableLogBuffer: TableLogBuffer,
     scope: CoroutineScope,
 ) : MobileConnectionRepository {
     init {
@@ -101,8 +99,6 @@
 
     private val telephonyCallbackEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
 
-    override val tableLogBuffer: TableLogBuffer = mobileLogger
-
     /**
      * This flow defines the single shared connection to system_server via TelephonyCallback. Any
      * new callback should be added to this listener and funneled through callbackEvents via a data
@@ -243,11 +239,6 @@
         val initial = MobileConnectionModel()
         callbackEvents
             .scan(initial, ::updateConnectionState)
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "MobileConnection ($subId)",
-                initialValue = initial,
-            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 
@@ -285,24 +276,12 @@
                     intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName
                 }
             }
-            .distinctUntilChanged()
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "",
-                initialValue = defaultNetworkName,
-            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
 
     override val dataEnabled = run {
         val initial = telephonyManager.isDataConnectionAllowed
         callbackEvents
             .mapNotNull { (it as? CallbackEvent.OnDataEnabledChanged)?.enabled }
-            .logDiffsForTable(
-                mobileLogger,
-                columnPrefix = "",
-                columnName = "dataEnabled",
-                initialValue = initial
-            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 10f48a3..c660d31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -42,6 +42,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -82,6 +85,7 @@
     private val subscriptionManager: SubscriptionManager,
     private val telephonyManager: TelephonyManager,
     private val logger: ConnectivityPipelineLogger,
+    @MobileSummaryLog private val tableLogger: TableLogBuffer,
     mobileMappingsProxy: MobileMappingsProxy,
     broadcastDispatcher: BroadcastDispatcher,
     private val context: Context,
@@ -114,6 +118,12 @@
                 }
             }
             .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "carrierMergedSubId",
+                initialValue = null,
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), null)
 
     private val mobileSubscriptionsChangeEvent: Flow<Unit> = conflatedCallbackFlow {
@@ -139,8 +149,14 @@
     override val subscriptions: StateFlow<List<SubscriptionModel>> =
         merge(mobileSubscriptionsChangeEvent, carrierMergedSubId)
             .mapLatest { fetchSubscriptionsList().map { it.toSubscriptionModel() } }
-            .logInputChange(logger, "onSubscriptionsChanged")
             .onEach { infos -> updateRepos(infos) }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "subscriptions",
+                initialValue = listOf(),
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), listOf())
 
     /** StateFlow that keeps track of the current active mobile data subscription */
@@ -157,7 +173,12 @@
                 awaitClose { telephonyManager.unregisterTelephonyCallback(callback) }
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "onActiveDataSubscriptionIdChanged")
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "activeSubId",
+                initialValue = INVALID_SUBSCRIPTION_ID,
+            )
             .stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
 
     private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
@@ -171,7 +192,12 @@
                 intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "defaultSubId",
+                initialValue = SubscriptionManager.getDefaultDataSubscriptionId(),
+            )
             .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
             .stateIn(
                 scope,
@@ -247,7 +273,11 @@
                 awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
             }
             .distinctUntilChanged()
-            .logInputChange(logger, "defaultMobileNetworkConnectivity")
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = "$LOGGING_PREFIX.defaultConnection",
+                initialValue = MobileConnectivityModel(),
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
 
     /**
@@ -321,4 +351,8 @@
             subscriptionId = subscriptionId,
             isOpportunistic = isOpportunistic,
         )
+
+    companion object {
+        private const val LOGGING_PREFIX = "Repo"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 9cdff96..9b7614c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -33,7 +33,9 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 interface MobileIconInteractor {
@@ -109,6 +111,9 @@
 
     /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */
     val numberOfLevels: StateFlow<Int>
+
+    /** See [MobileIconsInteractor.isForceHidden]. */
+    val isForceHidden: Flow<Boolean>
 }
 
 /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
@@ -124,6 +129,7 @@
     defaultMobileIconGroup: StateFlow<MobileIconGroup>,
     defaultDataSubId: StateFlow<Int>,
     override val isDefaultConnectionFailed: StateFlow<Boolean>,
+    override val isForceHidden: Flow<Boolean>,
     connectionRepository: MobileConnectionRepository,
 ) : MobileIconInteractor {
     private val connectionInfo = connectionRepository.connectionInfo
@@ -181,6 +187,16 @@
                     else -> mapping[info.resolvedNetworkType.lookupKey] ?: defaultGroup
                 }
             }
+            .distinctUntilChanged()
+            .onEach {
+                // Doesn't use [logDiffsForTable] because [MobileIconGroup] can't implement the
+                // [Diffable] interface.
+                tableLogBuffer.logChange(
+                    prefix = "",
+                    columnName = "networkTypeIcon",
+                    value = it.name
+                )
+            }
             .stateIn(scope, SharingStarted.WhileSubscribed(), defaultMobileIconGroup.value)
 
     override val isEmergencyOnly: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index 0e4a432..94f7af4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -23,12 +23,17 @@
 import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.dagger.MobileSummaryLog
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
 import com.android.systemui.util.CarrierConfigTracker
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -42,8 +47,8 @@
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.transformLatest
 
@@ -88,6 +93,10 @@
     val isDefaultConnectionFailed: StateFlow<Boolean>
     /** True once the user has been set up */
     val isUserSetup: StateFlow<Boolean>
+
+    /** True if we're configured to force-hide the mobile icons and false otherwise. */
+    val isForceHidden: Flow<Boolean>
+
     /**
      * Vends out a [MobileIconInteractor] tracking the [MobileConnectionRepository] for the given
      * subId. Will throw if the ID is invalid
@@ -104,6 +113,8 @@
     private val mobileConnectionsRepo: MobileConnectionsRepository,
     private val carrierConfigTracker: CarrierConfigTracker,
     private val logger: ConnectivityPipelineLogger,
+    @MobileSummaryLog private val tableLogger: TableLogBuffer,
+    connectivityRepository: ConnectivityRepository,
     userSetupRepo: UserSetupRepository,
     @Application private val scope: CoroutineScope,
 ) : MobileIconsInteractor {
@@ -173,7 +184,13 @@
             }
         }
             .distinctUntilChanged()
-            .onEach { logger.logFilteredSubscriptionsChanged(it) }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "filteredSubscriptions",
+                initialValue = listOf(),
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
 
     override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
 
@@ -195,6 +212,12 @@
                 delay(2000)
                 emit(false)
             }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "forcingValidation",
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
@@ -211,6 +234,12 @@
                     networkConnectivity
                 }
             }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                tableLogger,
+                columnPrefix = "$LOGGING_PREFIX.defaultConnection",
+                initialValue = mobileConnectionsRepo.defaultMobileNetworkConnectivity.value,
+            )
             .stateIn(
                 scope,
                 SharingStarted.WhileSubscribed(),
@@ -259,10 +288,21 @@
                     !connectivityModel.isValidated
                 }
             }
+            .logDiffsForTable(
+                tableLogger,
+                LOGGING_PREFIX,
+                columnName = "isDefaultConnectionFailed",
+                initialValue = false,
+            )
             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val isUserSetup: StateFlow<Boolean> = userSetupRepo.isUserSetupFlow
 
+    override val isForceHidden: Flow<Boolean> =
+        connectivityRepository.forceHiddenSlots
+            .map { it.contains(ConnectivitySlot.MOBILE) }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     /** Vends out new [MobileIconInteractor] for a particular subId */
     override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
         MobileIconInteractorImpl(
@@ -275,6 +315,11 @@
             defaultMobileIconGroup,
             defaultDataSubId,
             isDefaultConnectionFailed,
+            isForceHidden,
             mobileConnectionsRepo.getRepoForSubId(subId),
         )
+
+    companion object {
+        private const val LOGGING_PREFIX = "Intr"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index a4b2abc..db585e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -91,10 +91,17 @@
                     }
                 }
 
+                launch { viewModel.isVisible.collect { isVisible -> view.isVisible = isVisible } }
+
                 // Set the icon for the triangle
                 launch {
-                    viewModel.iconId.distinctUntilChanged().collect { iconId ->
-                        mobileDrawable.level = iconId
+                    viewModel.icon.distinctUntilChanged().collect { icon ->
+                        mobileDrawable.level =
+                            SignalDrawable.getState(
+                                icon.level,
+                                icon.numberOfLevels,
+                                icon.showExclamationMark,
+                            )
                     }
                 }
 
@@ -148,8 +155,7 @@
 
         return object : ModernStatusBarViewBinding {
             override fun getShouldIconBeVisible(): Boolean {
-                // If this view model exists, then the icon should be visible.
-                return true
+                return viewModel.isVisible.value
             }
 
             override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
new file mode 100644
index 0000000..16e1766
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/model/SignalIconModel.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.statusbar.pipeline.mobile.ui.model
+
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
+/** A model that will be consumed by [SignalDrawable] to show the mobile triangle icon. */
+data class SignalIconModel(
+    val level: Int,
+    val numberOfLevels: Int,
+    val showExclamationMark: Boolean,
+) : Diffable<SignalIconModel> {
+    // TODO(b/267767715): Can we implement [logDiffs] and [logFull] generically for data classes?
+    override fun logDiffs(prevVal: SignalIconModel, row: TableRowLogger) {
+        if (prevVal.level != level) {
+            row.logChange(COL_LEVEL, level)
+        }
+        if (prevVal.numberOfLevels != numberOfLevels) {
+            row.logChange(COL_NUM_LEVELS, numberOfLevels)
+        }
+        if (prevVal.showExclamationMark != showExclamationMark) {
+            row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
+        }
+    }
+
+    override fun logFull(row: TableRowLogger) {
+        row.logChange(COL_LEVEL, level)
+        row.logChange(COL_NUM_LEVELS, numberOfLevels)
+        row.logChange(COL_SHOW_EXCLAMATION, showExclamationMark)
+    }
+
+    companion object {
+        /** Creates a [SignalIconModel] representing an empty and invalidated state. */
+        fun createEmptyState(numberOfLevels: Int) =
+            SignalIconModel(level = 0, numberOfLevels, showExclamationMark = true)
+
+        private const val COL_LEVEL = "level"
+        private const val COL_NUM_LEVELS = "numLevels"
+        private const val COL_SHOW_EXCLAMATION = "showExclamation"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
index 9e2024a..0496278 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt
@@ -22,10 +22,11 @@
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -37,14 +38,14 @@
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 
 /** Common interface for all of the location-based mobile icon view models. */
 interface MobileIconViewModelCommon {
     val subscriptionId: Int
-    /** An int consumable by [SignalDrawable] for display */
-    val iconId: Flow<Int>
+    /** True if this view should be visible at all. */
+    val isVisible: StateFlow<Boolean>
+    val icon: Flow<SignalIconModel>
     val contentDescription: Flow<ContentDescription>
     val roaming: Flow<Boolean>
     /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
@@ -73,7 +74,7 @@
 constructor(
     override val subscriptionId: Int,
     iconInteractor: MobileIconInteractor,
-    logger: ConnectivityPipelineLogger,
+    airplaneModeInteractor: AirplaneModeInteractor,
     constants: ConnectivityConstants,
     scope: CoroutineScope,
 ) : MobileIconViewModelCommon {
@@ -81,8 +82,28 @@
     private val showExclamationMark: Flow<Boolean> =
         iconInteractor.isDefaultDataEnabled.mapLatest { !it }
 
-    override val iconId: Flow<Int> = run {
-        val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
+    override val isVisible: StateFlow<Boolean> =
+        if (!constants.hasDataCapabilities) {
+                flowOf(false)
+            } else {
+                combine(
+                    airplaneModeInteractor.isAirplaneMode,
+                    iconInteractor.isForceHidden,
+                ) { isAirplaneMode, isForceHidden ->
+                    !isAirplaneMode && !isForceHidden
+                }
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnPrefix = "",
+                columnName = "visible",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
+    override val icon: Flow<SignalIconModel> = run {
+        val initial = SignalIconModel.createEmptyState(iconInteractor.numberOfLevels.value)
         combine(
                 iconInteractor.level,
                 iconInteractor.numberOfLevels,
@@ -90,16 +111,15 @@
                 iconInteractor.isInService,
             ) { level, numberOfLevels, showExclamationMark, isInService ->
                 if (!isInService) {
-                    SignalDrawable.getEmptyState(numberOfLevels)
+                    SignalIconModel.createEmptyState(numberOfLevels)
                 } else {
-                    SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
+                    SignalIconModel(level, numberOfLevels, showExclamationMark)
                 }
             }
             .distinctUntilChanged()
             .logDiffsForTable(
                 iconInteractor.tableLogBuffer,
-                columnPrefix = "",
-                columnName = "iconId",
+                columnPrefix = "icon",
                 initialValue = initial,
             )
             .stateIn(scope, SharingStarted.WhileSubscribed(), initial)
@@ -124,14 +144,22 @@
 
     private val showNetworkTypeIcon: Flow<Boolean> =
         combine(
-            iconInteractor.isDataConnected,
-            iconInteractor.isDataEnabled,
-            iconInteractor.isDefaultConnectionFailed,
-            iconInteractor.alwaysShowDataRatIcon,
-            iconInteractor.isConnected,
-        ) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
-            alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
-        }
+                iconInteractor.isDataConnected,
+                iconInteractor.isDataEnabled,
+                iconInteractor.isDefaultConnectionFailed,
+                iconInteractor.alwaysShowDataRatIcon,
+                iconInteractor.isConnected,
+            ) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
+                alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
+            }
+            .distinctUntilChanged()
+            .logDiffsForTable(
+                iconInteractor.tableLogBuffer,
+                columnPrefix = "",
+                columnName = "showNetworkTypeIcon",
+                initialValue = false,
+            )
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     override val networkTypeIcon: Flow<Icon?> =
         combine(
@@ -149,14 +177,6 @@
                 }
             }
             .distinctUntilChanged()
-            .onEach {
-                // This is done as an onEach side effect since Icon is not Diffable (yet)
-                iconInteractor.tableLogBuffer.logChange(
-                    prefix = "",
-                    columnName = "networkTypeIcon",
-                    value = it.toString(),
-                )
-            }
             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
 
     override val roaming: StateFlow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 24370d2..185b668 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -17,9 +17,11 @@
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
@@ -39,6 +41,7 @@
 constructor(
     val subscriptionIdsFlow: StateFlow<List<Int>>,
     private val interactor: MobileIconsInteractor,
+    private val airplaneModeInteractor: AirplaneModeInteractor,
     private val logger: ConnectivityPipelineLogger,
     private val constants: ConnectivityConstants,
     @Application private val scope: CoroutineScope,
@@ -56,7 +59,7 @@
                 ?: MobileIconViewModel(
                         subId,
                         interactor.createMobileConnectionInteractorForSubId(subId),
-                        logger,
+                        airplaneModeInteractor,
                         constants,
                         scope,
                     )
@@ -74,10 +77,12 @@
         subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
     }
 
+    @SysUISingleton
     class Factory
     @Inject
     constructor(
         private val interactor: MobileIconsInteractor,
+        private val airplaneModeInteractor: AirplaneModeInteractor,
         private val logger: ConnectivityPipelineLogger,
         private val constants: ConnectivityConstants,
         @Application private val scope: CoroutineScope,
@@ -87,6 +92,7 @@
             return MobileIconsViewModel(
                 subscriptionIdsFlow,
                 interactor,
+                airplaneModeInteractor,
                 logger,
                 constants,
                 scope,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
index 6796a94..45036969 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
@@ -204,15 +204,6 @@
 
     // TODO(b/238425913): We should split this class into mobile-specific and wifi-specific loggers.
 
-    fun logFilteredSubscriptionsChanged(subs: List<SubscriptionModel>) {
-        buffer.log(
-            SB_LOGGING_TAG,
-            LogLevel.INFO,
-            { str1 = subs.toString() },
-            { "Filtered subscriptions updated: $str1" },
-        )
-    }
-
     fun logUiAdapterSubIdsUpdated(subs: List<Int>) {
         buffer.log(
             SB_LOGGING_TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUiEvent.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUiEvent.kt
new file mode 100644
index 0000000..577292f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUiEvent.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.stylus
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class StylusUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+    @UiEvent(doc = "UiEvent for USI low battery notification shown")
+    STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN(1298),
+    @UiEvent(doc = "UiEvent for USI low battery notification clicked")
+    STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED(1299),
+    @UiEvent(doc = "UiEvent for USI low battery notification dismissed")
+    STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED(1300),
+    @UiEvent(doc = "UIEvent for Toast shown when stylus started charging")
+    STYLUS_STARTED_CHARGING(1302),
+    @UiEvent(doc = "UIEvent for Toast shown when stylus stopped charging")
+    STYLUS_STOPPED_CHARGING(1303);
+
+    override fun getId() = _id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
index 9050dad..89453ad 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.stylus
 
 import android.Manifest
+import android.app.ActivityManager
 import android.app.PendingIntent
 import android.content.ActivityNotFoundException
 import android.content.BroadcastReceiver
@@ -32,6 +33,7 @@
 import androidx.core.app.NotificationCompat
 import androidx.core.app.NotificationManagerCompat
 import com.android.internal.annotations.VisibleForTesting
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
@@ -53,6 +55,7 @@
     private val notificationManager: NotificationManagerCompat,
     private val inputManager: InputManager,
     @Background private val handler: Handler,
+    private val uiEventLogger: UiEventLogger,
 ) {
 
     // These values must only be accessed on the handler.
@@ -79,12 +82,13 @@
 
     fun refresh() {
         handler.post refreshNotification@{
-            if (!suppressed && !hasConnectedBluetoothStylus() && isBatteryBelowThreshold()) {
+            val batteryBelowThreshold = isBatteryBelowThreshold()
+            if (!suppressed && !hasConnectedBluetoothStylus() && batteryBelowThreshold) {
                 showOrUpdateNotification()
                 return@refreshNotification
             }
 
-            if (!isBatteryBelowThreshold()) {
+            if (!batteryBelowThreshold) {
                 // Reset suppression when stylus battery is recharged, so that the next time
                 // it reaches a low battery, the notification will show again.
                 suppressed = false
@@ -143,6 +147,7 @@
                 .setAutoCancel(true)
                 .build()
 
+        logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN)
         notificationManager.notify(USI_NOTIFICATION_ID, notification)
     }
 
@@ -168,8 +173,12 @@
         object : BroadcastReceiver() {
             override fun onReceive(context: Context, intent: Intent) {
                 when (intent.action) {
-                    ACTION_DISMISSED_LOW_BATTERY -> updateSuppression(true)
+                    ACTION_DISMISSED_LOW_BATTERY -> {
+                        logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED)
+                        updateSuppression(true)
+                    }
                     ACTION_CLICKED_LOW_BATTERY -> {
+                        logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED)
                         updateSuppression(true)
                         if (inputDeviceId == null) return
 
@@ -195,6 +204,15 @@
             }
         }
 
+    private fun logUiEvent(metricId: StylusUiEvent) {
+        uiEventLogger.logWithPosition(
+            metricId,
+            ActivityManager.getCurrentUser(),
+            context.packageName,
+            (batteryCapacity * 100.0).toInt()
+        )
+    }
+
     companion object {
         // Low battery threshold matches CrOS, see:
         // https://source.chromium.org/chromium/chromium/src/+/main:ash/system/power/peripheral_battery_notifier.cc;l=41
@@ -203,10 +221,14 @@
         private val USI_NOTIFICATION_ID = R.string.stylus_battery_low_percentage
 
         @VisibleForTesting const val ACTION_DISMISSED_LOW_BATTERY = "StylusUsiPowerUI.dismiss"
+
         @VisibleForTesting const val ACTION_CLICKED_LOW_BATTERY = "StylusUsiPowerUI.click"
+
         @VisibleForTesting
         const val ACTION_STYLUS_USI_DETAILS = "com.android.settings.STYLUS_USI_DETAILS_SETTINGS"
+
         @VisibleForTesting const val KEY_DEVICE_INPUT_ID = "device_input_id"
+
         @VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
new file mode 100644
index 0000000..c1f0c58
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/theme/DynamicColors.kt
@@ -0,0 +1,90 @@
+/*
+ * 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.theme
+
+import android.util.Pair
+import com.android.systemui.monet.dynamiccolor.DynamicColor
+import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors as MDC
+
+class DynamicColors {
+    companion object {
+        @JvmField
+        val ALL_DYNAMIC_COLORS_MAPPED: List<Pair<String, DynamicColor>> =
+            arrayListOf(
+                Pair.create("primary_container", MDC.primaryContainer),
+                Pair.create("on_primary_container", MDC.onPrimaryContainer),
+                Pair.create("primary", MDC.primary),
+                Pair.create("on_primary", MDC.onPrimary),
+                Pair.create("secondary_container", MDC.secondaryContainer),
+                Pair.create("on_secondary_container", MDC.onSecondaryContainer),
+                Pair.create("secondary", MDC.secondary),
+                Pair.create("on_secondary", MDC.onSecondary),
+                Pair.create("tertiary_container", MDC.tertiaryContainer),
+                Pair.create("on_tertiary_container", MDC.onTertiaryContainer),
+                Pair.create("tertiary", MDC.tertiary),
+                Pair.create("on_tertiary", MDC.onTertiary),
+                Pair.create("background", MDC.background),
+                Pair.create("on_background", MDC.onBackground),
+                Pair.create("surface", MDC.surface),
+                Pair.create("on_surface", MDC.onSurface),
+                Pair.create("surface_container_low", MDC.surfaceContainerLow),
+                Pair.create("surface_container_lowest", MDC.surfaceContainerLowest),
+                Pair.create("surface_container", MDC.surfaceContainer),
+                Pair.create("surface_container_high", MDC.surfaceContainerHigh),
+                Pair.create("surface_container_highest", MDC.surfaceContainerHighest),
+                Pair.create("surface_bright", MDC.surfaceBright),
+                Pair.create("surface_dim", MDC.surfaceDim),
+                Pair.create("surface_variant", MDC.surfaceVariant),
+                Pair.create("on_surface_variant", MDC.onSurfaceVariant),
+                Pair.create("outline", MDC.outline),
+                Pair.create("error", MDC.error),
+                Pair.create("on_error", MDC.onError),
+                Pair.create("error_container", MDC.errorContainer),
+                Pair.create("on_error_container", MDC.onErrorContainer),
+                Pair.create("primary_fixed", MDC.primaryFixed),
+                Pair.create("primary_fixed_darker", MDC.primaryFixedDarker),
+                Pair.create("on_primary_fixed", MDC.onPrimaryFixed),
+                Pair.create("on_primary_fixed_variant", MDC.onPrimaryFixedVariant),
+                Pair.create("secondary_fixed", MDC.secondaryFixed),
+                Pair.create("secondary_fixed_darker", MDC.secondaryFixedDarker),
+                Pair.create("on_secondary_fixed", MDC.onSecondaryFixed),
+                Pair.create("on_secondary_fixed_variant", MDC.onSecondaryFixedVariant),
+                Pair.create("tertiary_fixed", MDC.tertiaryFixed),
+                Pair.create("tertiary_fixed_darker", MDC.tertiaryFixedDarker),
+                Pair.create("on_tertiary_fixed", MDC.onTertiaryFixed),
+                Pair.create("on_tertiary_fixed_variant", MDC.onTertiaryFixedVariant),
+                Pair.create("control_activated", MDC.controlActivated),
+                Pair.create("control_normal", MDC.controlNormal),
+                Pair.create("control_highlight", MDC.controlHighlight),
+                Pair.create("text_primary_inverse", MDC.textPrimaryInverse),
+                Pair.create(
+                    "text_secondary_and_tertiary_inverse",
+                    MDC.textSecondaryAndTertiaryInverse
+                ),
+                Pair.create("text_primary_inverse_disable_only", MDC.textPrimaryInverseDisableOnly),
+                Pair.create(
+                    "text_secondary_and_tertiary_inverse_disabled",
+                    MDC.textSecondaryAndTertiaryInverseDisabled
+                ),
+                Pair.create("text_hint_inverse", MDC.textHintInverse),
+                Pair.create("palette_key_color_primary", MDC.primaryPaletteKeyColor),
+                Pair.create("palette_key_color_secondary", MDC.secondaryPaletteKeyColor),
+                Pair.create("palette_key_color_tertiary", MDC.tertiaryPaletteKeyColor),
+                Pair.create("palette_key_color_neutral", MDC.neutralPaletteKeyColor),
+                Pair.create("palette_key_color_neutral_variant", MDC.neutralVariantPaletteKeyColor)
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index ba39367..3376e23 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -65,6 +65,8 @@
     @VisibleForTesting
     static final String SYSUI_PACKAGE = "com.android.systemui";
 
+    static final String OVERLAY_CATEGORY_DYNAMIC_COLOR =
+            "android.theme.customization.dynamic_color";
     static final String OVERLAY_CATEGORY_ACCENT_COLOR =
             "android.theme.customization.accent_color";
     static final String OVERLAY_CATEGORY_SYSTEM_PALETTE =
@@ -117,6 +119,7 @@
             OVERLAY_CATEGORY_SHAPE,
             OVERLAY_CATEGORY_FONT,
             OVERLAY_CATEGORY_ACCENT_COLOR,
+            OVERLAY_CATEGORY_DYNAMIC_COLOR,
             OVERLAY_CATEGORY_ICON_ANDROID,
             OVERLAY_CATEGORY_ICON_SYSUI,
             OVERLAY_CATEGORY_ICON_SETTINGS,
@@ -127,6 +130,7 @@
     static final Set<String> SYSTEM_USER_CATEGORIES = Sets.newHashSet(
             OVERLAY_CATEGORY_SYSTEM_PALETTE,
             OVERLAY_CATEGORY_ACCENT_COLOR,
+            OVERLAY_CATEGORY_DYNAMIC_COLOR,
             OVERLAY_CATEGORY_FONT,
             OVERLAY_CATEGORY_SHAPE,
             OVERLAY_CATEGORY_ICON_ANDROID,
@@ -153,6 +157,7 @@
         mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
                 OVERLAY_CATEGORY_SYSTEM_PALETTE, OVERLAY_CATEGORY_ACCENT_COLOR,
+                OVERLAY_CATEGORY_DYNAMIC_COLOR,
                 OVERLAY_CATEGORY_FONT, OVERLAY_CATEGORY_SHAPE,
                 OVERLAY_CATEGORY_ICON_ANDROID));
         mTargetPackageToCategories.put(SYSUI_PACKAGE,
@@ -164,6 +169,7 @@
         mTargetPackageToCategories.put(mThemePickerPackage,
                 Sets.newHashSet(OVERLAY_CATEGORY_ICON_THEME_PICKER));
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, ANDROID_PACKAGE);
+        mCategoryToTargetPackage.put(OVERLAY_CATEGORY_DYNAMIC_COLOR, ANDROID_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_FONT, ANDROID_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_SHAPE, ANDROID_PACKAGE);
         mCategoryToTargetPackage.put(OVERLAY_CATEGORY_ICON_ANDROID, ANDROID_PACKAGE);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 6b507ba..2ad3558 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -15,11 +15,14 @@
  */
 package com.android.systemui.theme;
 
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
+
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_HOME;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_LOCK;
 import static com.android.systemui.theme.ThemeOverlayApplier.COLOR_SOURCE_PRESET;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_SYSTEM_PALETTE;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_BOTH;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX;
@@ -51,7 +54,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.TypedValue;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -70,6 +73,16 @@
 import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.monet.Style;
 import com.android.systemui.monet.TonalPalette;
+import com.android.systemui.monet.dynamiccolor.MaterialDynamicColors;
+import com.android.systemui.monet.hct.Hct;
+import com.android.systemui.monet.scheme.DynamicScheme;
+import com.android.systemui.monet.scheme.SchemeExpressive;
+import com.android.systemui.monet.scheme.SchemeFruitSalad;
+import com.android.systemui.monet.scheme.SchemeMonochrome;
+import com.android.systemui.monet.scheme.SchemeNeutral;
+import com.android.systemui.monet.scheme.SchemeRainbow;
+import com.android.systemui.monet.scheme.SchemeTonalSpot;
+import com.android.systemui.monet.scheme.SchemeVibrant;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -105,9 +118,6 @@
     protected static final String TAG = "ThemeOverlayController";
     private static final boolean DEBUG = true;
 
-    protected static final int NEUTRAL = 0;
-    protected static final int ACCENT = 1;
-
     private final ThemeOverlayApplier mThemeManager;
     private final UserManager mUserManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -130,12 +140,17 @@
     private boolean mNeedsOverlayCreation;
     // Dominant color extracted from wallpaper, NOT the color used on the overlay
     protected int mMainWallpaperColor = Color.TRANSPARENT;
+    // UI contrast as reported by AccessibilityManager
+    private float mUiContrast = 0;
     // Theme variant: Vibrant, Tonal, Expressive, etc
-    private Style mThemeStyle = Style.TONAL_SPOT;
+    @VisibleForTesting
+    protected Style mThemeStyle = Style.TONAL_SPOT;
     // Accent colors overlay
     private FabricatedOverlay mSecondaryOverlay;
     // Neutral system colors overlay
     private FabricatedOverlay mNeutralOverlay;
+    // Dynamic colors overlay
+    private FabricatedOverlay mDynamicOverlay;
     // If wallpaper color event will be accepted and change the UI colors.
     private boolean mAcceptColorEvents = true;
     // If non-null (per user), colors that were sent to the framework, and processing was deferred
@@ -143,6 +158,9 @@
     private final SparseArray<WallpaperColors> mDeferredWallpaperColors = new SparseArray<>();
     private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray();
     private final WakefulnessLifecycle mWakefulnessLifecycle;
+    private final AccessibilityManager mAccessibilityManager;
+    private DynamicScheme mDynamicSchemeDark;
+    private DynamicScheme mDynamicSchemeLight;
 
     // Defers changing themes until Setup Wizard is done.
     private boolean mDeferredThemeEvaluation;
@@ -304,6 +322,7 @@
                 mSkipSettingChange = true;
                 if (jsonObject.has(OVERLAY_CATEGORY_ACCENT_COLOR) || jsonObject.has(
                         OVERLAY_CATEGORY_SYSTEM_PALETTE)) {
+                    jsonObject.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR);
                     jsonObject.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
                     jsonObject.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
                     jsonObject.remove(OVERLAY_COLOR_INDEX);
@@ -372,7 +391,8 @@
             DumpManager dumpManager,
             FeatureFlags featureFlags,
             @Main Resources resources,
-            WakefulnessLifecycle wakefulnessLifecycle) {
+            WakefulnessLifecycle wakefulnessLifecycle,
+            AccessibilityManager accessibilityManager) {
         mContext = context;
         mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME);
         mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET);
@@ -388,6 +408,7 @@
         mUserTracker = userTracker;
         mResources = resources;
         mWakefulnessLifecycle = wakefulnessLifecycle;
+        mAccessibilityManager = accessibilityManager;
         dumpManager.registerDumpable(TAG, this);
     }
 
@@ -424,6 +445,12 @@
                     }
                 },
                 UserHandle.USER_ALL);
+        mUiContrast = mAccessibilityManager.getUiContrast();
+        mAccessibilityManager.addUiContrastChangeListener(mMainExecutor, uiContrast -> {
+            mUiContrast = uiContrast;
+            // Force reload so that we update even when the main color has not changed
+            reevaluateSystemTheme(true /* forceReload */);
+        });
 
         if (!mIsMonetEnabled) {
             return;
@@ -496,12 +523,11 @@
 
         if (mIsMonetEnabled) {
             mThemeStyle = fetchThemeStyleFromSetting();
-            mSecondaryOverlay = getOverlay(mMainWallpaperColor, ACCENT, mThemeStyle);
-            mNeutralOverlay = getOverlay(mMainWallpaperColor, NEUTRAL, mThemeStyle);
+            createOverlays(mMainWallpaperColor);
             mNeedsOverlayCreation = true;
             if (DEBUG) {
                 Log.d(TAG, "fetched overlays. accent: " + mSecondaryOverlay
-                        + " neutral: " + mNeutralOverlay);
+                        + " neutral: " + mNeutralOverlay + " dynamic: " + mDynamicOverlay);
             }
         }
 
@@ -519,42 +545,95 @@
         return ColorScheme.getSeedColor(wallpaperColors);
     }
 
-    /**
-     * Given a color candidate, return an overlay definition.
-     */
-    protected FabricatedOverlay getOverlay(int color, int type, Style style) {
-        boolean nightMode = (mResources.getConfiguration().uiMode
-                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
-
-        mColorScheme = new ColorScheme(color, nightMode, style);
-        String name = type == ACCENT ? "accent" : "neutral";
-
-        FabricatedOverlay.Builder overlay =
-                new FabricatedOverlay.Builder("com.android.systemui", name, "android");
-
-        if (type == ACCENT) {
-            assignTonalPaletteToOverlay("accent1", overlay, mColorScheme.getAccent1());
-            assignTonalPaletteToOverlay("accent2", overlay, mColorScheme.getAccent2());
-            assignTonalPaletteToOverlay("accent3", overlay, mColorScheme.getAccent3());
-        } else {
-            assignTonalPaletteToOverlay("neutral1", overlay, mColorScheme.getNeutral1());
-            assignTonalPaletteToOverlay("neutral2", overlay, mColorScheme.getNeutral2());
+    private static DynamicScheme dynamicSchemeFromStyle(Style style, int color,
+            boolean isDark, double contrastLevel) {
+        Hct sourceColorHct = Hct.fromInt(color);
+        switch (style) {
+            case EXPRESSIVE:
+                return new SchemeExpressive(sourceColorHct, isDark, contrastLevel);
+            case SPRITZ:
+                return new SchemeNeutral(sourceColorHct, isDark, contrastLevel);
+            case TONAL_SPOT:
+                return new SchemeTonalSpot(sourceColorHct, isDark, contrastLevel);
+            case FRUIT_SALAD:
+                return new SchemeFruitSalad(sourceColorHct, isDark, contrastLevel);
+            case RAINBOW:
+                return new SchemeRainbow(sourceColorHct, isDark, contrastLevel);
+            case VIBRANT:
+                return new SchemeVibrant(sourceColorHct, isDark, contrastLevel);
+            case MONOCHROMATIC:
+                return new SchemeMonochrome(sourceColorHct, isDark, contrastLevel);
+            default:
+                return null;
         }
-
-        return overlay.build();
     }
 
-    private void assignTonalPaletteToOverlay(String name,
-            FabricatedOverlay.Builder overlay, TonalPalette tonalPalette) {
+    @VisibleForTesting
+    protected boolean isNightMode() {
+        return (mResources.getConfiguration().uiMode
+                & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
+    }
 
+    @VisibleForTesting
+    protected FabricatedOverlay newFabricatedOverlay(String name) {
+        return new FabricatedOverlay.Builder("com.android.systemui", name, "android").build();
+    }
+
+    private void createOverlays(int color) {
+        boolean nightMode = isNightMode();
+        mColorScheme = new ColorScheme(color, nightMode, mThemeStyle);
+        mNeutralOverlay = createNeutralOverlay();
+        mSecondaryOverlay = createAccentOverlay();
+
+        mDynamicSchemeDark = dynamicSchemeFromStyle(
+                mThemeStyle, color, true /* isDark */, mUiContrast);
+        mDynamicSchemeLight = dynamicSchemeFromStyle(
+                mThemeStyle, color, false /* isDark */, mUiContrast);
+        mDynamicOverlay = createDynamicOverlay();
+    }
+
+    protected FabricatedOverlay createNeutralOverlay() {
+        FabricatedOverlay overlay = newFabricatedOverlay("neutral");
+        assignTonalPaletteToOverlay("neutral1", overlay, mColorScheme.getNeutral1());
+        assignTonalPaletteToOverlay("neutral2", overlay, mColorScheme.getNeutral2());
+        return overlay;
+    }
+
+    protected FabricatedOverlay createAccentOverlay() {
+        FabricatedOverlay overlay = newFabricatedOverlay("accent");
+        assignTonalPaletteToOverlay("accent1", overlay, mColorScheme.getAccent1());
+        assignTonalPaletteToOverlay("accent2", overlay, mColorScheme.getAccent2());
+        assignTonalPaletteToOverlay("accent3", overlay, mColorScheme.getAccent3());
+        return overlay;
+    }
+
+    private void assignTonalPaletteToOverlay(String name, FabricatedOverlay overlay,
+            TonalPalette tonalPalette) {
         String resourcePrefix = "android:color/system_" + name;
-        int colorDataType = TypedValue.TYPE_INT_COLOR_ARGB8;
 
         tonalPalette.getAllShadesMapped().forEach((key, value) -> {
             String resourceName = resourcePrefix + "_" + key;
             int colorValue = ColorUtils.setAlphaComponent(value, 0xFF);
-            overlay.setResourceValue(resourceName, colorDataType,
-                    colorValue);
+            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
+                    null /* configuration */);
+        });
+    }
+
+    protected FabricatedOverlay createDynamicOverlay() {
+        FabricatedOverlay overlay = newFabricatedOverlay("dynamic");
+        assignDynamicPaletteToOverlay(overlay, true /* isDark */);
+        assignDynamicPaletteToOverlay(overlay, false /* isDark */);
+        return overlay;
+    }
+
+    private void assignDynamicPaletteToOverlay(FabricatedOverlay overlay, boolean isDark) {
+        String suffix = isDark ? "dark" : "light";
+        DynamicScheme scheme = isDark ? mDynamicSchemeDark : mDynamicSchemeLight;
+        DynamicColors.ALL_DYNAMIC_COLORS_MAPPED.forEach(p -> {
+            String resourceName = "android:color/system_" + p.first + "_" + suffix;
+            int colorValue = p.second.getArgb(scheme);
+            overlay.setResourceValue(resourceName, TYPE_INT_COLOR_ARGB8, colorValue,
+                    null /* configuration */);
         });
     }
 
@@ -568,16 +647,21 @@
         for (UserHandle userHandle : allProfiles) {
             Resources res = userHandle.isSystem()
                     ? mResources : mContext.createContextAsUser(userHandle, 0).getResources();
-            if (!(res.getColor(android.R.color.system_accent1_500, mContext.getTheme())
+            Resources.Theme theme = mContext.getTheme();
+            if (!(res.getColor(android.R.color.system_accent1_500, theme)
                     == mColorScheme.getAccent1().getS500()
-                    && res.getColor(android.R.color.system_accent2_500, mContext.getTheme())
+                    && res.getColor(android.R.color.system_accent2_500, theme)
                     == mColorScheme.getAccent2().getS500()
-                    && res.getColor(android.R.color.system_accent3_500, mContext.getTheme())
+                    && res.getColor(android.R.color.system_accent3_500, theme)
                     == mColorScheme.getAccent3().getS500()
-                    && res.getColor(android.R.color.system_neutral1_500, mContext.getTheme())
+                    && res.getColor(android.R.color.system_neutral1_500, theme)
                     == mColorScheme.getNeutral1().getS500()
-                    && res.getColor(android.R.color.system_neutral2_500, mContext.getTheme())
-                    == mColorScheme.getNeutral2().getS500())) {
+                    && res.getColor(android.R.color.system_neutral2_500, theme)
+                    == mColorScheme.getNeutral2().getS500()
+                    && res.getColor(android.R.color.system_primary_container_dark, theme)
+                    == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeDark)
+                    && res.getColor(android.R.color.system_primary_container_light, theme)
+                    == MaterialDynamicColors.primaryContainer.getArgb(mDynamicSchemeLight))) {
                 return false;
             }
         }
@@ -614,12 +698,11 @@
                 if (!colorString.startsWith("#")) {
                     colorString = "#" + colorString;
                 }
-                int color = Color.parseColor(colorString);
-                mNeutralOverlay = getOverlay(color, NEUTRAL, mThemeStyle);
-                mSecondaryOverlay = getOverlay(color, ACCENT, mThemeStyle);
+                createOverlays(Color.parseColor(colorString));
                 mNeedsOverlayCreation = true;
                 categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
                 categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+                categoryToPackage.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR);
             } catch (Exception e) {
                 // Color.parseColor doesn't catch any exceptions from the calls it makes
                 Log.w(TAG, "Invalid color definition: " + systemPalette.getPackageName(), e);
@@ -631,6 +714,7 @@
                 // fail.
                 categoryToPackage.remove(OVERLAY_CATEGORY_SYSTEM_PALETTE);
                 categoryToPackage.remove(OVERLAY_CATEGORY_ACCENT_COLOR);
+                categoryToPackage.remove(OVERLAY_CATEGORY_DYNAMIC_COLOR);
             } catch (NumberFormatException e) {
                 // This is a package name. All good, let's continue
             }
@@ -647,6 +731,10 @@
                 && mSecondaryOverlay != null) {
             categoryToPackage.put(OVERLAY_CATEGORY_ACCENT_COLOR, mSecondaryOverlay.getIdentifier());
         }
+        if (!categoryToPackage.containsKey(OVERLAY_CATEGORY_DYNAMIC_COLOR)
+                && mDynamicOverlay != null) {
+            categoryToPackage.put(OVERLAY_CATEGORY_DYNAMIC_COLOR, mDynamicOverlay.getIdentifier());
+        }
 
         Set<UserHandle> managedProfiles = new HashSet<>();
         for (UserInfo userInfo : mUserManager.getEnabledProfiles(currentUser)) {
@@ -668,7 +756,7 @@
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[]{
-                    mSecondaryOverlay, mNeutralOverlay
+                    mSecondaryOverlay, mNeutralOverlay, mDynamicOverlay
             }, currentUser, managedProfiles);
         } else {
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
@@ -710,6 +798,7 @@
         pw.println("mMainWallpaperColor=" + Integer.toHexString(mMainWallpaperColor));
         pw.println("mSecondaryOverlay=" + mSecondaryOverlay);
         pw.println("mNeutralOverlay=" + mNeutralOverlay);
+        pw.println("mDynamicOverlay=" + mDynamicOverlay);
         pw.println("mIsMonetEnabled=" + mIsMonetEnabled);
         pw.println("mColorScheme=" + mColorScheme);
         pw.println("mNeedsOverlayCreation=" + mNeedsOverlayCreation);
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 5cf01af..70523bb 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -47,12 +47,14 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withContext
@@ -123,9 +125,25 @@
     featureFlags: FeatureFlags,
 ) : UserRepository {
 
-    private val _userSwitcherSettings = MutableStateFlow(runBlocking { getSettings() })
-    override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> =
-        _userSwitcherSettings.asStateFlow().filterNotNull()
+    private val _userSwitcherSettings: StateFlow<UserSwitcherSettingsModel> =
+        globalSettings
+            .observerFlow(
+                names =
+                    arrayOf(
+                        SETTING_SIMPLE_USER_SWITCHER,
+                        Settings.Global.ADD_USERS_WHEN_LOCKED,
+                        Settings.Global.USER_SWITCHER_ENABLED,
+                    ),
+                userId = UserHandle.USER_SYSTEM,
+            )
+            .onStart { emit(Unit) } // Forces an initial update.
+            .map { getSettings() }
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Eagerly,
+                initialValue = runBlocking { getSettings() },
+            )
+    override val userSwitcherSettings: Flow<UserSwitcherSettingsModel> = _userSwitcherSettings
 
     private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
     override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
@@ -159,7 +177,6 @@
 
     init {
         observeSelectedUser()
-        observeUserSettings()
         if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
             observeUserSwitching()
         }
@@ -247,23 +264,6 @@
             .launchIn(applicationScope)
     }
 
-    private fun observeUserSettings() {
-        globalSettings
-            .observerFlow(
-                names =
-                    arrayOf(
-                        SETTING_SIMPLE_USER_SWITCHER,
-                        Settings.Global.ADD_USERS_WHEN_LOCKED,
-                        Settings.Global.USER_SWITCHER_ENABLED,
-                    ),
-                userId = UserHandle.USER_SYSTEM,
-            )
-            .onStart { emit(Unit) } // Forces an initial update.
-            .map { getSettings() }
-            .onEach { _userSwitcherSettings.value = it }
-            .launchIn(applicationScope)
-    }
-
     private suspend fun getSettings(): UserSwitcherSettingsModel {
         return withContext(backgroundDispatcher) {
             val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
index 9f4a7c8..b9db9c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java
@@ -33,6 +33,7 @@
 import com.android.settingslib.dream.DreamBackend;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
@@ -66,13 +67,16 @@
 
     private ComplicationTypesUpdater mController;
 
+    @Mock
+    private Monitor mMonitor;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>());
 
         mController = new ComplicationTypesUpdater(mDreamBackend, mExecutor,
-                mSecureSettings, mDreamOverlayStateController);
+                mSecureSettings, mDreamOverlayStateController, mMonitor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
index ec448f9..52aaea1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -30,6 +30,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +70,9 @@
     @Mock
     private ComplicationLayoutParams mLayoutParams;
 
+    @Mock
+    private Monitor mMonitor;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -83,7 +87,8 @@
         final DreamClockTimeComplication.Registrant registrant =
                 new DreamClockTimeComplication.Registrant(
                         mDreamOverlayStateController,
-                        mComplication);
+                        mComplication,
+                        mMonitor);
         registrant.start();
         verify(mDreamOverlayStateController).addComplication(eq(mComplication));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index a4cf15c..fc1d38b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -101,6 +102,9 @@
     @Captor
     private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
 
+    @Mock
+    private Monitor mMonitor;
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -126,7 +130,7 @@
     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -139,7 +143,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -152,7 +156,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(false);
@@ -165,7 +169,7 @@
     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(true);
@@ -178,7 +182,7 @@
     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setHaveFavorites(true);
@@ -191,7 +195,7 @@
     public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor);
         registrant.start();
 
         setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
index c8b2b25..77ca958 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java
@@ -33,6 +33,8 @@
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
 import com.android.systemui.plugins.BcSmartspaceDataPlugin;
+import com.android.systemui.shared.condition.Condition;
+import com.android.systemui.shared.condition.Monitor;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -43,6 +45,8 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -60,6 +64,11 @@
     @Mock
     private View mBcSmartspaceView;
 
+    @Mock
+    private Monitor mMonitor;
+
+    private final Set<Condition> mPreconditions = new HashSet<>();
+
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
@@ -79,7 +88,8 @@
         return new SmartSpaceComplication.Registrant(
                 mDreamOverlayStateController,
                 mComplication,
-                mSmartspaceController);
+                mSmartspaceController,
+                mMonitor);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
index 3b5e6b9..d1744c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/LogDiffsForTableTest.kt
@@ -276,6 +276,52 @@
         }
 
     @Test
+    fun intNullable_logsNull() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+            val flow = flow {
+                for (int in listOf(null, 6, null, 8)) {
+                    systemClock.advanceTime(100L)
+                    emit(int)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = 1234,
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) + SEPARATOR + FULL_NAME + SEPARATOR + "1234"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) + SEPARATOR + FULL_NAME + SEPARATOR + "null"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(300L) + SEPARATOR + FULL_NAME + SEPARATOR + "6"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(400L) + SEPARATOR + FULL_NAME + SEPARATOR + "null"
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(500L) + SEPARATOR + FULL_NAME + SEPARATOR + "8"
+                )
+
+            job.cancel()
+        }
+
+    @Test
     fun int_logsUpdates() =
         testScope.runTest {
             systemClock.setCurrentTimeMillis(100L)
@@ -1030,6 +1076,246 @@
             job.cancel()
         }
 
+    // ---- Flow<List<T>> tests ----
+
+    @Test
+    fun list_doesNotLogWhenNotCollected() {
+        val flow = flowOf(listOf(5), listOf(6), listOf(7))
+
+        flow.logDiffsForTable(
+            tableLogBuffer,
+            COLUMN_PREFIX,
+            COLUMN_NAME,
+            initialValue = listOf(1234),
+        )
+
+        val logs = dumpLog()
+        assertThat(logs).doesNotContain(COLUMN_PREFIX)
+        assertThat(logs).doesNotContain(COLUMN_NAME)
+        assertThat(logs).doesNotContain("1234")
+    }
+
+    @Test
+    fun list_logsInitialWhenCollected() =
+        testScope.runTest {
+            val flow = flowOf(listOf(5), listOf(6), listOf(7))
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf(1234),
+                )
+
+            systemClock.setCurrentTimeMillis(3000L)
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(3000L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(1234).toString()
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun list_logsUpdates() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+
+            val listItems =
+                listOf(listOf("val1", "val2"), listOf("val3"), listOf("val4", "val5", "val6"))
+            val flow = flow {
+                for (list in listItems) {
+                    systemClock.advanceTime(100L)
+                    emit(list)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf("val0", "val00"),
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val0", "val00").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val1", "val2").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(300L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val3").toString()
+                )
+            assertThat(logs)
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(400L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf("val4", "val5", "val6").toString()
+                )
+
+            job.cancel()
+        }
+
+    @Test
+    fun list_doesNotLogIfSameValue() =
+        testScope.runTest {
+            systemClock.setCurrentTimeMillis(100L)
+
+            val listItems =
+                listOf(
+                    listOf("val0", "val00"),
+                    listOf("val1"),
+                    listOf("val1"),
+                    listOf("val1", "val2"),
+                )
+            val flow = flow {
+                for (bool in listItems) {
+                    systemClock.advanceTime(100L)
+                    emit(bool)
+                }
+            }
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf("val0", "val00"),
+                )
+
+            val job = launch { flowWithLogging.collect() }
+
+            val logs = dumpLog()
+
+            val expected1 =
+                TABLE_LOG_DATE_FORMAT.format(100L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val0", "val00").toString()
+            val expected3 =
+                TABLE_LOG_DATE_FORMAT.format(300L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1").toString()
+            val expected5 =
+                TABLE_LOG_DATE_FORMAT.format(500L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1", "val2").toString()
+            assertThat(logs).contains(expected1)
+            assertThat(logs).contains(expected3)
+            assertThat(logs).contains(expected5)
+
+            val unexpected2 =
+                TABLE_LOG_DATE_FORMAT.format(200L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val0", "val00")
+            val unexpected4 =
+                TABLE_LOG_DATE_FORMAT.format(400L) +
+                    SEPARATOR +
+                    FULL_NAME +
+                    SEPARATOR +
+                    listOf("val1")
+            assertThat(logs).doesNotContain(unexpected2)
+            assertThat(logs).doesNotContain(unexpected4)
+            job.cancel()
+        }
+
+    @Test
+    fun list_worksForStateFlows() =
+        testScope.runTest {
+            val flow = MutableStateFlow(listOf(1111))
+
+            val flowWithLogging =
+                flow.logDiffsForTable(
+                    tableLogBuffer,
+                    COLUMN_PREFIX,
+                    COLUMN_NAME,
+                    initialValue = listOf(1111),
+                )
+
+            systemClock.setCurrentTimeMillis(50L)
+            val job = launch { flowWithLogging.collect() }
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(50L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(1111).toString()
+                )
+
+            systemClock.setCurrentTimeMillis(100L)
+            flow.emit(listOf(2222, 3333))
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(100L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(2222, 3333).toString()
+                )
+
+            systemClock.setCurrentTimeMillis(200L)
+            flow.emit(listOf(3333, 4444))
+            assertThat(dumpLog())
+                .contains(
+                    TABLE_LOG_DATE_FORMAT.format(200L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(3333, 4444).toString()
+                )
+
+            // Doesn't log duplicates
+            systemClock.setCurrentTimeMillis(300L)
+            flow.emit(listOf(3333, 4444))
+            assertThat(dumpLog())
+                .doesNotContain(
+                    TABLE_LOG_DATE_FORMAT.format(300L) +
+                        SEPARATOR +
+                        FULL_NAME +
+                        SEPARATOR +
+                        listOf(3333, 4444).toString()
+                )
+
+            job.cancel()
+        }
+
     private fun dumpLog(): String {
         val outputWriter = StringWriter()
         tableLogBuffer.dump(PrintWriter(outputWriter), arrayOf())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
index 432764a..c7f3fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableChangeTest.kt
@@ -36,6 +36,17 @@
     }
 
     @Test
+    fun setString_null() {
+        val underTest = TableChange()
+
+        underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+        underTest.set(null as String?)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
     fun setBoolean_isBoolean() {
         val underTest = TableChange()
 
@@ -58,6 +69,17 @@
     }
 
     @Test
+    fun setInt_null() {
+        val underTest = TableChange()
+
+        underTest.reset(timestamp = 100, columnPrefix = "", columnName = "fakeName")
+        underTest.set(null as Int?)
+
+        assertThat(underTest.hasData()).isTrue()
+        assertThat(underTest.getVal()).isEqualTo("null")
+    }
+
+    @Test
     fun setThenReset_isEmpty() {
         val underTest = TableChange()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
index 701cf95..af52459 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinatorTest.java
@@ -117,10 +117,6 @@
         extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true);
         mNotification.extras = extras;
 
-        // GIVEN notification has the permission to display during setup
-        when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID))
-                .thenReturn(PackageManager.PERMISSION_GRANTED);
-
         // THEN don't filter out the notification
         assertFalse(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
     }
@@ -130,15 +126,10 @@
         // GIVEN device is unprovisioned
         when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(false);
 
-        // GIVEN notification has a flag to allow the notification during setup
+        // GIVEN notification does not have the flag to allow the notification during setup
         Bundle extras = new Bundle();
-        extras.putBoolean(SHOW_WHEN_UNPROVISIONED_FLAG, true);
         mNotification.extras = extras;
 
-        // GIVEN notification does NOT have permission to display during setup
-        when(mIPackageManager.checkUidPermission(SETUP_NOTIF_PERMISSION, NOTIF_UID))
-                .thenReturn(PackageManager.PERMISSION_DENIED);
-
         // THEN filter out the notification
         assertTrue(mDeviceProvisionedFilter.shouldFilterOut(mEntry, 0));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 6fb6893..8aaa57f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
@@ -33,7 +35,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.demomode.DemoModeController;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarIconView;
 import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusBarWifiView;
@@ -46,6 +51,8 @@
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
@@ -87,6 +94,61 @@
         testCallOnAdd_forManager(manager);
     }
 
+    @Test
+    public void testRemoveIcon_ignoredForNewPipeline() {
+        IconManager manager = mock(IconManager.class);
+
+        // GIVEN the new pipeline is on
+        StatusBarPipelineFlags flags = mock(StatusBarPipelineFlags.class);
+        when(flags.isIconControlledByFlags("test_icon")).thenReturn(true);
+
+        StatusBarIconController iconController = new StatusBarIconControllerImpl(
+                mContext,
+                mock(CommandQueue.class),
+                mock(DemoModeController.class),
+                mock(ConfigurationController.class),
+                mock(TunerService.class),
+                mock(DumpManager.class),
+                mock(StatusBarIconList.class),
+                flags
+        );
+
+        iconController.addIconGroup(manager);
+
+        // WHEN a request to remove a new icon is sent
+        iconController.removeIcon("test_icon", 0);
+
+        // THEN it is not removed for those icons
+        verify(manager, never()).onRemoveIcon(anyInt());
+    }
+
+    @Test
+    public void testRemoveAllIconsForSlot_ignoredForNewPipeline() {
+        IconManager manager = mock(IconManager.class);
+
+        // GIVEN the new pipeline is on
+        StatusBarPipelineFlags flags = mock(StatusBarPipelineFlags.class);
+        when(flags.isIconControlledByFlags("test_icon")).thenReturn(true);
+
+        StatusBarIconController iconController = new StatusBarIconControllerImpl(
+                mContext,
+                mock(CommandQueue.class),
+                mock(DemoModeController.class),
+                mock(ConfigurationController.class),
+                mock(TunerService.class),
+                mock(DumpManager.class),
+                mock(StatusBarIconList.class),
+                flags
+        );
+
+        iconController.addIconGroup(manager);
+
+        // WHEN a request to remove a new icon is sent
+        iconController.removeAllIconsForSlot("test_icon");
+
+        // THEN it is not removed for those icons
+        verify(manager, never()).onRemoveIcon(anyInt());
+    }
 
     private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) {
         StatusBarIconHolder holder = holderForType(TYPE_ICON);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index 96cca44..4da2104 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.demomode.DemoMode
 import com.android.systemui.demomode.DemoModeController
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
@@ -81,6 +82,7 @@
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
     @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var demoModeController: DemoModeController
     @Mock private lateinit var dumpManager: DumpManager
 
@@ -114,6 +116,7 @@
                 subscriptionManager,
                 telephonyManager,
                 logger,
+                summaryLogger,
                 mobileMappings,
                 fakeBroadcastDispatcher,
                 context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
index 24b9f7d..da208a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt
@@ -16,20 +16,32 @@
 
 package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
 
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
 import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_EMERGENCY
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_OPERATOR
+import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel.Companion.COL_PRIMARY_LEVEL
 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
+import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
+import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
@@ -52,9 +64,10 @@
 class FullMobileConnectionRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: FullMobileConnectionRepository
 
+    private val systemClock = FakeSystemClock()
     private val testDispatcher = UnconfinedTestDispatcher()
     private val testScope = TestScope(testDispatcher)
-    private val tableLogBuffer = mock<TableLogBuffer>()
+    private val tableLogBuffer = TableLogBuffer(maxSize = 100, name = "TestName", systemClock)
     private val mobileFactory = mock<MobileConnectionRepositoryImpl.Factory>()
     private val carrierMergedFactory = mock<CarrierMergedConnectionRepository.Factory>()
 
@@ -347,8 +360,214 @@
                 .isSameInstanceAs(connection1Repeat.tableLogBuffer)
         }
 
-    // TODO(b/238425913): Verify that the logging switches correctly (once the carrier merged repo
-    //   implements logging).
+    @Test
+    fun connectionInfo_logging_notCarrierMerged_getsUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager = mock<TelephonyManager>()
+            createRealMobileRepo(telephonyManager)
+            createRealCarrierMergedRepo(FakeWifiRepository())
+
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up some mobile connection info
+            val serviceState = ServiceState()
+            serviceState.setOperatorName("longName", "OpTypical", "1")
+            serviceState.isEmergencyOnly = false
+            getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
+                .onServiceStateChanged(serviceState)
+
+            // THEN it's logged to the buffer
+            assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpTypical")
+            assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}false")
+
+            // WHEN we update mobile connection info
+            val serviceState2 = ServiceState()
+            serviceState2.setOperatorName("longName", "OpDiff", "1")
+            serviceState2.isEmergencyOnly = true
+            getTelephonyCallbackForType<TelephonyCallback.ServiceStateListener>(telephonyManager)
+                .onServiceStateChanged(serviceState2)
+
+            // THEN the updates are logged
+            assertThat(dumpBuffer()).contains("$COL_OPERATOR${BUFFER_SEPARATOR}OpDiff")
+            assertThat(dumpBuffer()).contains("$COL_EMERGENCY${BUFFER_SEPARATOR}true")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_carrierMerged_getsUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            createRealMobileRepo(mock())
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(wifiRepository)
+
+            initializeRepo(startingIsCarrierMerged = true)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up carrier merged info
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+
+            // THEN the carrier merged info is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN we update the info
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 1,
+                )
+            )
+
+            // THEN the updates are logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_updatesWhenCarrierMergedUpdates() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager = mock<TelephonyManager>()
+            createRealMobileRepo(telephonyManager)
+
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(wifiRepository)
+
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            // WHEN we set up some mobile connection info
+            val signalStrength = mock<SignalStrength>()
+            whenever(signalStrength.level).thenReturn(1)
+
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+
+            // THEN it's logged to the buffer
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            // WHEN isCarrierMerged is set to true
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+            underTest.setIsCarrierMerged(true)
+
+            // THEN the carrier merged info is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN the carrier merge network is updated
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 4,
+                )
+            )
+
+            // THEN the new level is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
+
+            // WHEN isCarrierMerged is set to false
+            underTest.setIsCarrierMerged(false)
+
+            // THEN the typical info is logged
+            // Note: Since our first logs also had the typical info, we need to search the log
+            // contents for after our carrier merged level log.
+            val fullBuffer = dumpBuffer()
+            val carrierMergedContentIndex = fullBuffer.indexOf("${BUFFER_SEPARATOR}4")
+            val bufferAfterCarrierMerged = fullBuffer.substring(carrierMergedContentIndex)
+            assertThat(bufferAfterCarrierMerged).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}1")
+
+            // WHEN the normal network is updated
+            val newMobileInfo =
+                MobileConnectionModel(
+                    operatorAlphaShort = "Mobile Operator 2",
+                    primaryLevel = 0,
+                )
+            mobileRepo.setConnectionInfo(newMobileInfo)
+
+            // THEN the new level is logged
+            assertThat(dumpBuffer()).contains("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}0")
+
+            job.cancel()
+        }
+
+    @Test
+    fun connectionInfo_logging_doesNotLogUpdatesForNotActiveRepo() =
+        testScope.runTest {
+            // SETUP: Use real repositories to verify the diffing still works. (See b/267501739.)
+            val telephonyManager = mock<TelephonyManager>()
+            createRealMobileRepo(telephonyManager)
+
+            val wifiRepository = FakeWifiRepository()
+            createRealCarrierMergedRepo(wifiRepository)
+
+            // WHEN isCarrierMerged = false
+            initializeRepo(startingIsCarrierMerged = false)
+
+            val job = underTest.connectionInfo.launchIn(this)
+
+            val signalStrength = mock<SignalStrength>()
+            whenever(signalStrength.level).thenReturn(1)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+
+            // THEN updates to the carrier merged level aren't logged
+            val networkId = 2
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 4,
+                )
+            )
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}4")
+
+            wifiRepository.setWifiNetwork(
+                WifiNetworkModel.CarrierMerged(
+                    networkId,
+                    SUB_ID,
+                    level = 3,
+                )
+            )
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}3")
+
+            // WHEN isCarrierMerged is set to true
+            underTest.setIsCarrierMerged(true)
+
+            // THEN updates to the normal level aren't logged
+            whenever(signalStrength.level).thenReturn(5)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}5")
+
+            whenever(signalStrength.level).thenReturn(6)
+            getTelephonyCallbackForType<TelephonyCallback.SignalStrengthsListener>(telephonyManager)
+                .onSignalStrengthsChanged(signalStrength)
+            assertThat(dumpBuffer()).doesNotContain("$COL_PRIMARY_LEVEL${BUFFER_SEPARATOR}6")
+
+            job.cancel()
+        }
 
     private fun initializeRepo(startingIsCarrierMerged: Boolean) {
         underTest =
@@ -364,9 +583,68 @@
             )
     }
 
+    private fun createRealMobileRepo(
+        telephonyManager: TelephonyManager,
+    ): MobileConnectionRepositoryImpl {
+        whenever(telephonyManager.subscriptionId).thenReturn(SUB_ID)
+
+        val realRepo =
+            MobileConnectionRepositoryImpl(
+                context,
+                SUB_ID,
+                defaultNetworkName = NetworkNameModel.Default("default"),
+                networkNameSeparator = SEP,
+                telephonyManager,
+                systemUiCarrierConfig = mock(),
+                fakeBroadcastDispatcher,
+                mobileMappingsProxy = mock(),
+                testDispatcher,
+                logger = mock(),
+                tableLogBuffer,
+                testScope.backgroundScope,
+            )
+        whenever(
+                mobileFactory.build(
+                    eq(SUB_ID),
+                    any(),
+                    eq(DEFAULT_NAME),
+                    eq(SEP),
+                )
+            )
+            .thenReturn(realRepo)
+
+        return realRepo
+    }
+
+    private fun createRealCarrierMergedRepo(
+        wifiRepository: FakeWifiRepository,
+    ): CarrierMergedConnectionRepository {
+        wifiRepository.setIsWifiEnabled(true)
+        wifiRepository.setIsWifiDefault(true)
+        val realRepo =
+            CarrierMergedConnectionRepository(
+                SUB_ID,
+                tableLogBuffer,
+                defaultNetworkName = NetworkNameModel.Default("default"),
+                testScope.backgroundScope,
+                wifiRepository,
+            )
+        whenever(carrierMergedFactory.build(eq(SUB_ID), any(), eq(DEFAULT_NAME)))
+            .thenReturn(realRepo)
+
+        return realRepo
+    }
+
+    private fun dumpBuffer(): String {
+        val outputWriter = StringWriter()
+        tableLogBuffer.dump(PrintWriter(outputWriter), arrayOf())
+        return outputWriter.toString()
+    }
+
     private companion object {
         const val SUB_ID = 42
         private val DEFAULT_NAME = NetworkNameModel.Default("default name")
         private const val SEP = "-"
+        private const val BUFFER_SEPARATOR = "|"
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index 3f36bc1..1a5cc9a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -71,7 +71,6 @@
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
 import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
@@ -86,7 +85,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.mockito.Mock
-import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@@ -670,16 +668,8 @@
             job.cancel()
         }
 
-    private fun getTelephonyCallbacks(): List<TelephonyCallback> {
-        val callbackCaptor = argumentCaptor<TelephonyCallback>()
-        Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
-        return callbackCaptor.allValues
-    }
-
     private inline fun <reified T> getTelephonyCallbackForType(): T {
-        val cbs = getTelephonyCallbacks().filterIsInstance<T>()
-        assertThat(cbs.size).isEqualTo(1)
-        return cbs[0]
+        return MobileTelephonyHelpers.getTelephonyCallbackForType(telephonyManager)
     }
 
     /** Convenience constructor for SignalStrength */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index b73348c..fef0981 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -84,6 +84,7 @@
     @Mock private lateinit var subscriptionManager: SubscriptionManager
     @Mock private lateinit var telephonyManager: TelephonyManager
     @Mock private lateinit var logger: ConnectivityPipelineLogger
+    @Mock private lateinit var summaryLogger: TableLogBuffer
     @Mock private lateinit var logBufferFactory: TableLogBufferFactory
 
     private val mobileMappings = FakeMobileMappingsProxy()
@@ -157,6 +158,7 @@
                 subscriptionManager,
                 telephonyManager,
                 logger,
+                summaryLogger,
                 mobileMappings,
                 fakeBroadcastDispatcher,
                 context,
@@ -616,6 +618,7 @@
                     subscriptionManager,
                     telephonyManager,
                     logger,
+                    summaryLogger,
                     mobileMappings,
                     fakeBroadcastDispatcher,
                     context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
new file mode 100644
index 0000000..621f793
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileTelephonyHelpers.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.statusbar.pipeline.mobile.data.repository.prod
+
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.google.common.truth.Truth.assertThat
+import org.mockito.Mockito.verify
+
+/** Helper methods for telephony-related callbacks for mobile tests. */
+object MobileTelephonyHelpers {
+    fun getTelephonyCallbacks(mockTelephonyManager: TelephonyManager): List<TelephonyCallback> {
+        val callbackCaptor = argumentCaptor<TelephonyCallback>()
+        verify(mockTelephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
+        return callbackCaptor.allValues
+    }
+
+    inline fun <reified T> getTelephonyCallbackForType(mockTelephonyManager: TelephonyManager): T {
+        val cbs = getTelephonyCallbacks(mockTelephonyManager).filterIsInstance<T>()
+        assertThat(cbs.size).isEqualTo(1)
+        return cbs[0]
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
index 7aeaa48..b9eda717dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt
@@ -71,6 +71,8 @@
     private val _numberOfLevels = MutableStateFlow(DEFAULT_NUM_LEVELS)
     override val numberOfLevels = _numberOfLevels
 
+    override val isForceHidden = MutableStateFlow(false)
+
     fun setIconGroup(group: SignalIcon.MobileIconGroup) {
         _iconGroup.value = group
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 172755c..2699316 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -73,6 +73,8 @@
     private val _isUserSetup = MutableStateFlow(true)
     override val isUserSetup = _isUserSetup
 
+    override val isForceHidden = MutableStateFlow(false)
+
     /** Always returns a new fake interactor */
     override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
         return FakeMobileIconInteractor(tableLogBuffer)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index c42aba5..f87f651 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -66,6 +66,7 @@
                 mobileIconsInteractor.defaultMobileIconGroup,
                 mobileIconsInteractor.defaultDataSubId,
                 mobileIconsInteractor.isDefaultConnectionFailed,
+                mobileIconsInteractor.isForceHidden,
                 connectionRepository,
             )
     }
@@ -550,6 +551,21 @@
             job.cancel()
         }
 
+    @Test
+    fun isForceHidden_matchesParent() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            mobileIconsInteractor.isForceHidden.value = true
+            assertThat(latest).isTrue()
+
+            mobileIconsInteractor.isForceHidden.value = false
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index bd24922..f8a9783 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.CarrierConfigTracker
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -50,6 +52,7 @@
 @SmallTest
 class MobileIconsInteractorTest : SysuiTestCase() {
     private lateinit var underTest: MobileIconsInteractor
+    private lateinit var connectivityRepository: FakeConnectivityRepository
     private lateinit var connectionsRepository: FakeMobileConnectionsRepository
     private val userSetupRepository = FakeUserSetupRepository()
     private val mobileMappingsProxy = FakeMobileMappingsProxy()
@@ -63,6 +66,8 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        connectivityRepository = FakeConnectivityRepository()
+
         connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer)
         connectionsRepository.setMobileConnectionRepositoryMap(
             mapOf(
@@ -79,6 +84,8 @@
                 connectionsRepository,
                 carrierConfigTracker,
                 logger = mock(),
+                tableLogger = mock(),
+                connectivityRepository,
                 userSetupRepository,
                 testScope.backgroundScope,
             )
@@ -609,6 +616,32 @@
             job.cancel()
         }
 
+    @Test
+    fun isForceHidden_repoHasMobileHidden_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+
+            connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
     companion object {
         private val tableLogBuffer =
             TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index a2c1209..e68a397 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -29,12 +29,14 @@
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.QsMobileIconViewModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,31 +60,37 @@
 
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
+    private lateinit var interactor: FakeMobileIconInteractor
+    private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
 
+    private lateinit var viewModelCommon: MobileIconViewModel
     private lateinit var viewModel: LocationBasedMobileViewModel
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        // This line was necessary to make the onDarkChanged and setStaticDrawableColor tests pass.
+        // But, it maybe *shouldn't* be necessary.
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+
         testableLooper = TestableLooper.get(this)
 
-        val interactor = FakeMobileIconInteractor(tableLogBuffer)
-
-        val viewModelCommon =
-            MobileIconViewModel(
-                subscriptionId = 1,
-                interactor,
-                logger,
-                constants,
-                testScope.backgroundScope,
+        airplaneModeRepository = FakeAirplaneModeRepository()
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                airplaneModeRepository,
+                FakeConnectivityRepository(),
             )
-        viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+
+        interactor = FakeMobileIconInteractor(tableLogBuffer)
+        createViewModel()
     }
 
     // Note: The following tests are more like integration tests, since they stand up a full
-    // [WifiViewModel] and test the interactions between the view, view-binder, and view-model.
+    // [MobileIconViewModel] and test the interactions between the view, view-binder, and
+    // view-model.
 
     @Test
     fun setVisibleState_icon_iconShownDotHidden() {
@@ -130,7 +138,25 @@
     }
 
     @Test
-    fun isIconVisible_alwaysTrue() {
+    fun isIconVisible_noData_outputsFalse() {
+        whenever(constants.hasDataCapabilities).thenReturn(false)
+        createViewModel()
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isFalse()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
+    fun isIconVisible_hasData_outputsTrue() {
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+        createViewModel()
+
         val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
 
         ViewUtils.attachView(view)
@@ -142,6 +168,34 @@
     }
 
     @Test
+    fun isIconVisible_notAirplaneMode_outputsTrue() {
+        airplaneModeRepository.setIsAirplaneMode(false)
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isTrue()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
+    fun isIconVisible_airplaneMode_outputsTrue() {
+        airplaneModeRepository.setIsAirplaneMode(true)
+
+        val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
+
+        ViewUtils.attachView(view)
+        testableLooper.processAllMessages()
+
+        assertThat(view.isIconVisible).isFalse()
+
+        ViewUtils.detachView(view)
+    }
+
+    @Test
     fun onDarkChanged_iconHasNewColor() {
         whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view = ModernStatusBarMobileView.constructAndBind(context, SLOT_NAME, viewModel)
@@ -184,6 +238,18 @@
     private fun View.getDotView(): View {
         return this.requireViewById(R.id.status_bar_dot)
     }
+
+    private fun createViewModel() {
+        viewModelCommon =
+            MobileIconViewModel(
+                subscriptionId = 1,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
+        viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+    }
 }
 
 private const val SLOT_NAME = "TestSlotName"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index c960a06..f983030 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -21,10 +21,13 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.launchIn
@@ -46,8 +49,8 @@
     private lateinit var qsIcon: QsMobileIconViewModel
     private lateinit var keyguardIcon: KeyguardMobileIconViewModel
     private lateinit var interactor: FakeMobileIconInteractor
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
 
@@ -57,6 +60,11 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+            )
         interactor = FakeMobileIconInteractor(tableLogBuffer)
         interactor.apply {
             setLevel(1)
@@ -68,7 +76,13 @@
             isDataConnected.value = true
         }
         commonImpl =
-            MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
+            MobileIconViewModel(
+                SUB_1_ID,
+                interactor,
+                airplaneModeInteractor,
+                constants,
+                testScope.backgroundScope,
+            )
 
         homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags)
         qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
@@ -78,14 +92,14 @@
     @Test
     fun `location based view models receive same icon id when common impl updates`() =
         testScope.runTest {
-            var latestHome: Int? = null
-            val homeJob = homeIcon.iconId.onEach { latestHome = it }.launchIn(this)
+            var latestHome: SignalIconModel? = null
+            val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
 
-            var latestQs: Int? = null
-            val qsJob = qsIcon.iconId.onEach { latestQs = it }.launchIn(this)
+            var latestQs: SignalIconModel? = null
+            val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this)
 
-            var latestKeyguard: Int? = null
-            val keyguardJob = keyguardIcon.iconId.onEach { latestKeyguard = it }.launchIn(this)
+            var latestKeyguard: SignalIconModel? = null
+            val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this)
 
             var expected = defaultSignal(level = 1)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
index b91a4df..bec276a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
@@ -19,16 +19,18 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH
 import com.android.settingslib.AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH_NONE
-import com.android.settingslib.graph.SignalDrawable
 import com.android.settingslib.mobile.TelephonyIcons.THREE_G
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,7 +51,8 @@
 class MobileIconViewModelTest : SysuiTestCase() {
     private lateinit var underTest: MobileIconViewModel
     private lateinit var interactor: FakeMobileIconInteractor
-    @Mock private lateinit var logger: ConnectivityPipelineLogger
+    private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var constants: ConnectivityConstants
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
 
@@ -59,6 +62,15 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        whenever(constants.hasDataCapabilities).thenReturn(true)
+
+        airplaneModeRepository = FakeAirplaneModeRepository()
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                airplaneModeRepository,
+                FakeConnectivityRepository(),
+            )
+
         interactor = FakeMobileIconInteractor(tableLogBuffer)
         interactor.apply {
             setLevel(1)
@@ -69,15 +81,94 @@
             setNumberOfLevels(4)
             isDataConnected.value = true
         }
-        underTest =
-            MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
+        createAndSetViewModel()
     }
 
     @Test
+    fun isVisible_notDataCapable_alwaysFalse() =
+        testScope.runTest {
+            // Create a new view model here so the constants are properly read
+            whenever(constants.hasDataCapabilities).thenReturn(false)
+            createAndSetViewModel()
+
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_notAirplane_notForceHidden_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_airplane_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_forceHidden_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = true
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun isVisible_respondsToUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            interactor.isForceHidden.value = false
+
+            assertThat(latest).isTrue()
+
+            airplaneModeRepository.setIsAirplaneMode(true)
+            assertThat(latest).isFalse()
+
+            airplaneModeRepository.setIsAirplaneMode(false)
+            assertThat(latest).isTrue()
+
+            interactor.isForceHidden.value = true
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
     fun iconId_correctLevel_notCutout() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
             val expected = defaultSignal()
 
             assertThat(latest).isEqualTo(expected)
@@ -90,8 +181,8 @@
         testScope.runTest {
             interactor.setIsDefaultDataEnabled(false)
 
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
             val expected = defaultSignal(level = 1, connected = false)
 
             assertThat(latest).isEqualTo(expected)
@@ -102,8 +193,8 @@
     @Test
     fun `icon - uses empty state - when not in service`() =
         testScope.runTest {
-            var latest: Int? = null
-            val job = underTest.iconId.onEach { latest = it }.launchIn(this)
+            var latest: SignalIconModel? = null
+            val job = underTest.icon.onEach { latest = it }.launchIn(this)
 
             interactor.isInService.value = false
 
@@ -364,14 +455,7 @@
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(false)
-            underTest =
-                MobileIconViewModel(
-                    SUB_1_ID,
-                    interactor,
-                    logger,
-                    constants,
-                    testScope.backgroundScope,
-                )
+            createAndSetViewModel()
 
             var inVisible: Boolean? = null
             val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
@@ -403,14 +487,7 @@
         testScope.runTest {
             // Create a new view model here so the constants are properly read
             whenever(constants.shouldShowActivityConfig).thenReturn(true)
-            underTest =
-                MobileIconViewModel(
-                    SUB_1_ID,
-                    interactor,
-                    logger,
-                    constants,
-                    testScope.backgroundScope,
-                )
+            createAndSetViewModel()
 
             var inVisible: Boolean? = null
             val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
@@ -459,6 +536,16 @@
             containerJob.cancel()
         }
 
+    private fun createAndSetViewModel() {
+        underTest = MobileIconViewModel(
+            SUB_1_ID,
+            interactor,
+            airplaneModeInteractor,
+            constants,
+            testScope.backgroundScope,
+        )
+    }
+
     companion object {
         private const val SUB_1_ID = 1
 
@@ -466,10 +553,11 @@
         fun defaultSignal(
             level: Int = 1,
             connected: Boolean = true,
-        ): Int {
-            return SignalDrawable.getState(level, /* numLevels */ 4, !connected)
+        ): SignalIconModel {
+            return SignalIconModel(level, numberOfLevels = 4, showExclamationMark = !connected)
         }
 
-        fun emptySignal(): Int = SignalDrawable.getEmptyState(4)
+        fun emptySignal(): SignalIconModel =
+            SignalIconModel(level = 0, numberOfLevels = 4, showExclamationMark = true)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 58b50c7..d9268a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -20,11 +20,14 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
+import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
 import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,6 +49,7 @@
     private lateinit var underTest: MobileIconsViewModel
     private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
 
+    private lateinit var airplaneModeInteractor: AirplaneModeInteractor
     @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var logger: ConnectivityPipelineLogger
     @Mock private lateinit var constants: ConnectivityConstants
@@ -57,6 +61,12 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
+        airplaneModeInteractor =
+            AirplaneModeInteractor(
+                FakeAirplaneModeRepository(),
+                FakeConnectivityRepository(),
+            )
+
         val subscriptionIdsFlow =
             interactor.filteredSubscriptions
                 .map { subs -> subs.map { it.subscriptionId } }
@@ -66,6 +76,7 @@
             MobileIconsViewModel(
                 subscriptionIdsFlow,
                 interactor,
+                airplaneModeInteractor,
                 logger,
                 constants,
                 testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
index e1668e8..d51c514 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.stylus
 
+import android.app.ActivityManager
 import android.app.Notification
 import android.content.BroadcastReceiver
 import android.content.Context
@@ -27,6 +28,7 @@
 import android.view.InputDevice
 import androidx.core.app.NotificationManagerCompat
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.any
@@ -54,15 +56,23 @@
 @SmallTest
 class StylusUsiPowerUiTest : SysuiTestCase() {
     @Mock lateinit var notificationManager: NotificationManagerCompat
+
     @Mock lateinit var inputManager: InputManager
+
     @Mock lateinit var handler: Handler
+
     @Mock lateinit var btStylusDevice: InputDevice
+
+    @Mock lateinit var uiEventLogger: UiEventLogger
+
     @Captor lateinit var notificationCaptor: ArgumentCaptor<Notification>
 
     private lateinit var stylusUsiPowerUi: StylusUsiPowerUI
     private lateinit var broadcastReceiver: BroadcastReceiver
     private lateinit var contextSpy: Context
 
+    private val uid = ActivityManager.getCurrentUser()
+
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
@@ -80,7 +90,8 @@
         whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
         whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
 
-        stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
+        stylusUsiPowerUi =
+            StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler, uiEventLogger)
         broadcastReceiver = stylusUsiPowerUi.receiver
     }
 
@@ -197,6 +208,19 @@
     }
 
     @Test
+    fun updateBatteryState_showsNotification_logsNotificationShown() {
+        stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
+
+        verify(uiEventLogger, times(1))
+            .logWithPosition(
+                StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN,
+                uid,
+                contextSpy.packageName,
+                10
+            )
+    }
+
+    @Test
     fun broadcastReceiver_clicked_hasInputDeviceId_startsUsiDetailsActivity() {
         val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
         val activityIntentCaptor = argumentCaptor<Intent>()
@@ -219,4 +243,32 @@
 
         verify(contextSpy, never()).startActivity(any())
     }
+
+    @Test
+    fun broadcastReceiver_clicked_logsNotificationClicked() {
+        val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
+        broadcastReceiver.onReceive(contextSpy, intent)
+
+        verify(uiEventLogger, times(1))
+            .logWithPosition(
+                StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED,
+                uid,
+                contextSpy.packageName,
+                100
+            )
+    }
+
+    @Test
+    fun broadcastReceiver_dismissed_logsNotificationDismissed() {
+        val intent = Intent(StylusUsiPowerUI.ACTION_DISMISSED_LOW_BATTERY)
+        broadcastReceiver.onReceive(contextSpy, intent)
+
+        verify(uiEventLogger, times(1))
+            .logWithPosition(
+                StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED,
+                uid,
+                contextSpy.packageName,
+                100
+            )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 3032ff1f..83439f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -17,6 +17,7 @@
 
 import static com.android.systemui.theme.ThemeOverlayApplier.ANDROID_PACKAGE;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ACCENT_COLOR;
+import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_DYNAMIC_COLOR;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_FONT;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_ANDROID;
 import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_CATEGORY_ICON_LAUNCHER;
@@ -113,6 +114,8 @@
         };
         when(mOverlayManager.getOverlayInfosForTarget(ANDROID_PACKAGE, UserHandle.SYSTEM))
                 .thenReturn(Lists.newArrayList(
+                        createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR,
+                                ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
@@ -123,6 +126,8 @@
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_SHAPE, false),
                         createOverlayInfo(TEST_DISABLED_PREFIX + OVERLAY_CATEGORY_ICON_ANDROID,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_ICON_ANDROID, false),
+                        createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_DYNAMIC_COLOR,
+                                ANDROID_PACKAGE, OVERLAY_CATEGORY_DYNAMIC_COLOR, true),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_ACCENT_COLOR,
                                 ANDROID_PACKAGE, OVERLAY_CATEGORY_ACCENT_COLOR, true),
                         createOverlayInfo(TEST_ENABLED_PREFIX + OVERLAY_CATEGORY_SYSTEM_PALETTE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 1710709..f9b5767 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -47,8 +47,9 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.view.accessibility.AccessibilityManager;
 
-import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -57,7 +58,6 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
-import com.android.systemui.monet.ColorScheme;
 import com.android.systemui.monet.Style;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -115,6 +115,8 @@
     private Resources mResources;
     @Mock
     private WakefulnessLifecycle mWakefulnessLifecycle;
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
     @Captor
     private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiver;
     @Captor
@@ -127,13 +129,13 @@
     private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallback;
     @Captor
     private ArgumentCaptor<ContentObserver> mSettingsObserver;
-    private Style mCurrentStyle;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mFeatureFlags.isEnabled(Flags.MONET)).thenReturn(true);
         when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE);
+        when(mAccessibilityManager.getUiContrast()).thenReturn(0.5f);
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
         when(mResources.getColor(eq(android.R.color.system_accent1_500), any()))
                 .thenReturn(Color.RED);
@@ -148,15 +150,19 @@
         mThemeOverlayController = new ThemeOverlayController(mContext,
                 mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
-                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) {
-            @Nullable
-            @Override
-            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
+                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
+                mAccessibilityManager) {
+            @VisibleForTesting
+            protected boolean isNightMode() {
+                return false;
+            }
+
+            @VisibleForTesting
+            protected FabricatedOverlay newFabricatedOverlay(String name) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
-                        .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
-                mCurrentStyle = style;
-                mColorScheme = new ColorScheme(color, false /* nightMode */, style);
+                        .thenReturn(new OverlayIdentifier(
+                                Integer.toHexString(mColorScheme.getSeed() | 0xff000000)));
                 return overlay;
             }
         };
@@ -416,7 +422,7 @@
 
             mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
 
-            assertThat(mCurrentStyle).isEqualTo(style);
+            assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(style);
         }
     }
 
@@ -432,7 +438,7 @@
 
         mSettingsObserver.getValue().onChange(true, null, 0, mUserTracker.getUserId());
 
-        assertThat(mCurrentStyle).isEqualTo(Style.TONAL_SPOT);
+        assertThat(mThemeOverlayController.mThemeStyle).isEqualTo(Style.TONAL_SPOT);
     }
 
     @Test
@@ -726,17 +732,20 @@
         mThemeOverlayController = new ThemeOverlayController(mContext,
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
-                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) {
-            @Nullable
-            @Override
-            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
+                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
+                mAccessibilityManager) {
+            @VisibleForTesting
+            protected boolean isNightMode() {
+                return false;
+            }
+
+            @VisibleForTesting
+            protected FabricatedOverlay newFabricatedOverlay(String name) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
                         .thenReturn(new OverlayIdentifier("com.thebest.livewallpaperapp.ever"));
-                mColorScheme = new ColorScheme(color, false /* nightMode */, style);
                 return overlay;
             }
-
         };
         mThemeOverlayController.start();
 
@@ -763,14 +772,19 @@
         mThemeOverlayController = new ThemeOverlayController(mContext,
                 mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier,
                 mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController,
-                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle) {
-            @Nullable
-            @Override
-            protected FabricatedOverlay getOverlay(int color, int type, Style style) {
+                mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle,
+                mAccessibilityManager) {
+            @VisibleForTesting
+            protected boolean isNightMode() {
+                return false;
+            }
+
+            @VisibleForTesting
+            protected FabricatedOverlay newFabricatedOverlay(String name) {
                 FabricatedOverlay overlay = mock(FabricatedOverlay.class);
                 when(overlay.getIdentifier())
-                        .thenReturn(new OverlayIdentifier(Integer.toHexString(color | 0xff000000)));
-                mColorScheme = new ColorScheme(color, false /* nightMode */, style);
+                        .thenReturn(new OverlayIdentifier(
+                                Integer.toHexString(mColorScheme.getSeed() | 0xff000000)));
                 return overlay;
             }
         };
diff --git a/services/Android.bp b/services/Android.bp
index 3f3ba06..f8097ec 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -191,6 +191,10 @@
         "service-sdksandbox.stubs.system_server",
     ],
 
+    vintf_fragments: [
+        "manifest_services.xml",
+    ],
+
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
     //javacflags: ["-Xlint"],
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index c87d1c8..8e7d277 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -229,17 +229,12 @@
     }
 
     void onDisplayAdded(@NonNull Display display) {
-        if (mInstalled) {
-            resetStreamStateForDisplay(display.getDisplayId());
-            enableFeaturesForDisplay(display);
-        }
+        enableFeaturesForDisplayIfInstalled(display);
+
     }
 
     void onDisplayRemoved(int displayId) {
-        if (mInstalled) {
-            disableFeaturesForDisplay(displayId);
-            resetStreamStateForDisplay(displayId);
-        }
+        disableFeaturesForDisplayIfInstalled(displayId);
     }
 
     @Override
@@ -479,6 +474,9 @@
 
         final Context displayContext = mContext.createDisplayContext(display);
         final int displayId = display.getDisplayId();
+        if (mAms.isDisplayProxyed(displayId)) {
+            return;
+        }
         if (!mServiceDetectsGestures.contains(displayId)) {
             mServiceDetectsGestures.put(displayId, false);
         }
@@ -613,6 +611,18 @@
             mEventHandler.remove(displayId);
         }
     }
+    void enableFeaturesForDisplayIfInstalled(Display display) {
+        if (mInstalled) {
+            resetStreamStateForDisplay(display.getDisplayId());
+            enableFeaturesForDisplay(display);
+        }
+    }
+    void disableFeaturesForDisplayIfInstalled(int displayId) {
+        if (mInstalled) {
+            disableFeaturesForDisplay(displayId);
+            resetStreamStateForDisplay(displayId);
+        }
+    }
 
     private void disableDisplayIndependentFeatures() {
         if (mAutoclickController != null) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7e4567b..03cf9b4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -474,7 +474,7 @@
                 new MagnificationScaleProvider(mContext));
         mMagnificationProcessor = new MagnificationProcessor(mMagnificationController);
         mCaptioningManagerImpl = new CaptioningManagerImpl(mContext);
-        mProxyManager = new ProxyManager(mLock, mA11yWindowManager);
+        mProxyManager = new ProxyManager(mLock, mA11yWindowManager, mContext);
         mFlashNotificationsController = new FlashNotificationsController(mContext);
         init();
     }
@@ -2495,6 +2495,7 @@
                     }
                     inputFilter = mInputFilter;
                     setInputFilter = true;
+                    mProxyManager.setAccessibilityInputFilter(mInputFilter);
                 }
                 mInputFilter.setUserAndEnabledFeatures(userState.mUserId, flags);
                 mInputFilter.setCombinedGenericMotionEventSources(
@@ -3885,6 +3886,10 @@
         return mProxyManager.unregisterProxy(displayId);
     }
 
+    boolean isDisplayProxyed(int displayId) {
+        return mProxyManager.isProxyed(displayId);
+    }
+
     @Override public float getUiContrast() {
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
             mTraceManager.logTrace(LOG_TAG + ".getUiContrast", FLAGS_ACCESSIBILITY_MANAGER);
@@ -4379,9 +4384,8 @@
         private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
 
-        // TODO: replace name with Settings Secure Key
         private final Uri mAlwaysOnMagnificationUri = Settings.Secure.getUriFor(
-                "accessibility_magnification_always_on_enabled");
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED);
 
         private final Uri mUiContrastUri = Settings.Secure.getUriFor(
                 CONTRAST_LEVEL);
@@ -4615,10 +4619,9 @@
     }
 
     boolean readAlwaysOnMagnificationLocked(AccessibilityUserState userState) {
-        // TODO: replace name const with Settings Secure Key
         final boolean isSettingsAlwaysOnEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
-                "accessibility_magnification_always_on_enabled",
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
                 0, userState.mUserId) == 1;
         final boolean isAlwaysOnFeatureFlagEnabled = mMagnificationController
                 .isAlwaysOnMagnificationFeatureFlagEnabled();
diff --git a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
index b1fa5b1..86b5a12 100644
--- a/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
+++ b/services/accessibility/java/com/android/server/accessibility/FlashNotificationsController.java
@@ -253,7 +253,8 @@
         broadcastFilter.addAction(ACTION_FLASH_NOTIFICATION_START_PREVIEW);
         broadcastFilter.addAction(ACTION_FLASH_NOTIFICATION_STOP_PREVIEW);
         mFlashBroadcastReceiver = new FlashBroadcastReceiver();
-        mContext.registerReceiver(mFlashBroadcastReceiver, broadcastFilter);
+        mContext.registerReceiver(
+                mFlashBroadcastReceiver, broadcastFilter, Context.RECEIVER_EXPORTED);
 
         final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
index 54cdb04..2530338 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java
@@ -19,10 +19,12 @@
 import android.accessibilityservice.IAccessibilityServiceClient;
 import android.content.ComponentName;
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
@@ -47,6 +49,8 @@
 
     private final Object mLock;
 
+    private final Context mContext;
+
     // Used to determine if we should notify AccessibilityManager clients of updates.
     // TODO(254545943): Separate this so each display id has its own state. Currently there is no
     // way to identify from AccessibilityManager which proxy state should be returned.
@@ -57,9 +61,12 @@
 
     private AccessibilityWindowManager mA11yWindowManager;
 
-    ProxyManager(Object lock, AccessibilityWindowManager awm) {
+    private AccessibilityInputFilter mA11yInputFilter;
+
+    ProxyManager(Object lock, AccessibilityWindowManager awm, Context context) {
         mLock = lock;
         mA11yWindowManager = awm;
+        mContext = context;
     }
 
     /**
@@ -109,6 +116,9 @@
             connection.mSystemSupport.onClientChangeLocked(true);
         }
 
+        if (mA11yInputFilter != null) {
+            mA11yInputFilter.disableFeaturesForDisplayIfInstalled(displayId);
+        }
         connection.initializeServiceInterface(client);
     }
 
@@ -120,14 +130,25 @@
     }
 
     private boolean clearConnection(int displayId) {
+        boolean removed = false;
         synchronized (mLock) {
             if (mProxyA11yServiceConnections.contains(displayId)) {
                 mProxyA11yServiceConnections.remove(displayId);
-                return true;
+                removed = true;
             }
         }
-        mA11yWindowManager.stopTrackingDisplayProxy(displayId);
-        return false;
+        if (removed) {
+            mA11yWindowManager.stopTrackingDisplayProxy(displayId);
+            if (mA11yInputFilter != null) {
+                final DisplayManager displayManager = (DisplayManager)
+                        mContext.getSystemService(Context.DISPLAY_SERVICE);
+                final Display proxyDisplay = displayManager.getDisplay(displayId);
+                if (proxyDisplay != null) {
+                    mA11yInputFilter.enableFeaturesForDisplayIfInstalled(proxyDisplay);
+                }
+            }
+        }
+        return removed;
     }
 
     /**
@@ -251,4 +272,8 @@
             proxy.notifyClearAccessibilityNodeInfoCache();
         }
     }
+
+    void setAccessibilityInputFilter(AccessibilityInputFilter filter) {
+        mA11yInputFilter = filter;
+    }
 }
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 6b61e97..b991a02 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -193,6 +193,32 @@
 
     final AugmentedAutofillState mAugmentedAutofillState = new AugmentedAutofillState();
 
+    /**
+     * Lock used to synchronize access to flags.
+     */
+    private final Object mFlagLock = new Object();
+
+    // Flag holders for Autofill PCC classification
+
+    @GuardedBy("mFlagLock")
+    private boolean mPccClassificationEnabled;
+
+    @GuardedBy("mFlagLock")
+    private boolean mPccPreferProviderOverPcc;
+
+    @GuardedBy("mFlagLock")
+    private boolean mPccUseFallbackDetection;
+
+    @GuardedBy("mFlagLock")
+    private String mPccProviderHints;
+
+    // Default flag values for Autofill PCC
+
+    private static final String DEFAULT_PCC_FEATURE_PROVIDER_HINTS = "";
+    private static final boolean DEFAULT_PREFER_PROVIDER_OVER_PCC = true;
+
+    private static final boolean DEFAULT_PCC_USE_FALLBACK = true;
+
     public AutofillManagerService(Context context) {
         super(context,
                 new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE),
@@ -302,6 +328,10 @@
                 case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES:
                 case AutofillFeatureFlags.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT:
                 case AutofillFeatureFlags.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT:
+                case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED:
+                case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS:
+                case AutofillFeatureFlags.DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC:
+                case AutofillFeatureFlags.DEVICE_CONFIG_PCC_USE_FALLBACK:
                     setDeviceConfigProperties();
                     break;
                 case AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
@@ -579,13 +609,38 @@
                     AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES,
                     AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM);
             if (verbose) {
-                Slog.v(mTag, "setDeviceConfigProperties(): "
+                Slog.v(mTag, "setDeviceConfigProperties() for AugmentedAutofill: "
                         + "augmentedIdleTimeout=" + mAugmentedServiceIdleUnbindTimeoutMs
                         + ", augmentedRequestTimeout=" + mAugmentedServiceRequestTimeoutMs
                         + ", smartSuggestionMode="
                         + getSmartSuggestionModeToString(mSupportedSmartSuggestionModes));
             }
         }
+        synchronized (mFlagLock) {
+            mPccClassificationEnabled = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_AUTOFILL,
+                    AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_CLASSIFICATION_ENABLED,
+                    AutofillFeatureFlags.DEFAULT_AUTOFILL_PCC_CLASSIFICATION_ENABLED);
+            mPccPreferProviderOverPcc = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_AUTOFILL,
+                    AutofillFeatureFlags.DEVICE_CONFIG_PREFER_PROVIDER_OVER_PCC,
+                    DEFAULT_PREFER_PROVIDER_OVER_PCC);
+            mPccUseFallbackDetection = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_AUTOFILL,
+                    AutofillFeatureFlags.DEVICE_CONFIG_PCC_USE_FALLBACK,
+                    DEFAULT_PCC_USE_FALLBACK);
+            mPccProviderHints = DeviceConfig.getString(
+                    DeviceConfig.NAMESPACE_AUTOFILL,
+                    AutofillFeatureFlags.DEVICE_CONFIG_AUTOFILL_PCC_FEATURE_PROVIDER_HINTS,
+                    DEFAULT_PCC_FEATURE_PROVIDER_HINTS);
+            if (verbose) {
+                Slog.v(mTag, "setDeviceConfigProperties() for PCC: "
+                        + "mPccClassificationEnabled=" + mPccClassificationEnabled
+                        + ", mPccPreferProviderOverPcc=" + mPccPreferProviderOverPcc
+                        + ", mPccUseFallbackDetection=" + mPccUseFallbackDetection
+                        + ", mPccProviderHints=" + mPccProviderHints);
+            }
+        }
     }
 
     private void updateCachedServices() {
@@ -791,6 +846,46 @@
         }
     }
 
+    /**
+     * Whether the Autofill PCC Classification feature is enabled.
+     */
+    public boolean isPccClassificationEnabled() {
+        synchronized (mFlagLock) {
+            return mPccClassificationEnabled;
+        }
+    }
+
+    /**
+     * Whether the Autofill Provider shouldbe preferred over PCC results for selecting datasets.
+     */
+    public boolean preferProviderOverPcc() {
+        synchronized (mFlagLock) {
+            return mPccPreferProviderOverPcc;
+        }
+    }
+
+    /**
+     * Whether to use the fallback for detection.
+     * If true, use data from secondary source if primary not present .
+     * For eg: if we prefer PCC over provider, and PCC detection didn't classify a field, however,
+     * autofill provider did, this flag would decide whether we use that result, and show some
+     * presentation for that particular field.
+     */
+    public boolean shouldUsePccFallback() {
+        synchronized (mFlagLock) {
+            return mPccUseFallbackDetection;
+        }
+    }
+
+    /**
+     * Provides Autofill Hints that would be requested by the service from the Autofill Provider.
+     */
+    public String getPccProviderHints() {
+        synchronized (mFlagLock) {
+            return mPccProviderHints;
+        }
+    }
+
     @Nullable
     @VisibleForTesting
     static Map<String, String[]> getAllowedCompatModePackages(String setting) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 2b529bf..c5c9288 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -170,6 +170,8 @@
             "android.service.autofill.action.DELAYED_FILL";
     private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID";
 
+    private static final String PCC_HINTS_DELIMITER = ",";
+
     final Object mLock;
 
     private final AutofillManagerServiceImpl mService;
@@ -564,6 +566,7 @@
                 if (mPendingInlineSuggestionsRequest.isServiceSupported()) {
                     mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(),
                             mPendingFillRequest.getFillContexts(),
+                            mPendingFillRequest.getHints(),
                             mPendingFillRequest.getClientState(),
                             mPendingFillRequest.getFlags(),
                             mPendingInlineSuggestionsRequest,
@@ -672,8 +675,10 @@
 
                 final ArrayList<FillContext> contexts =
                         mergePreviousSessionLocked(/* forSave= */ false);
+                final List<String> hints = getTypeHintsForProvider();
+
                 mDelayedFillPendingIntent = createPendingIntent(requestId);
-                request = new FillRequest(requestId, contexts, mClientState, flags,
+                request = new FillRequest(requestId, contexts, hints, mClientState, flags,
                         /*inlineSuggestionsRequest=*/ null,
                         /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null
                             ? null
@@ -705,6 +710,22 @@
     }
 
     /**
+     * Get the list of valid autofill hint types from Device flags
+     * Returns empty list if PCC is off or no types available
+    */
+    private List<String> getTypeHintsForProvider() {
+        if (!mService.getMaster().isPccClassificationEnabled()) {
+            return Collections.EMPTY_LIST;
+        }
+        final String typeHints = mService.getMaster().getPccProviderHints();
+        if (TextUtils.isEmpty(typeHints)) {
+            return new ArrayList<>();
+        }
+
+        return List.of(typeHints.split(PCC_HINTS_DELIMITER));
+    }
+
+    /**
      * Assist Data Receiver for PCC
      */
     private final class PccAssistDataReceiverImpl extends IAssistDataReceiver.Stub {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index bd072f5..c8caab9 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -146,6 +146,7 @@
     ],
 
     static_libs: [
+        "android.frameworks.location.altitude-V1-java", // AIDL
         "android.hardware.authsecret-V1.0-java",
         "android.hardware.authsecret-V1-java",
         "android.hardware.boot-V1.0-java", // HIDL
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 9c2de65..17ef9a2 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -104,8 +104,18 @@
 
     /**
      * Reports any activity that could potentially have caused the CPU to wake up.
-     * Accepts a timestamp to allow the reporter to report it before or after the event.
+     * Accepts a timestamp to allow free ordering between the event and its reporting.
+     * @param subsystem The subsystem this activity should be attributed to.
+     * @param elapsedMillis The time when this activity happened in the elapsed timebase.
+     * @param uids The uid (or uids) that should be blamed for this activity.
      */
     public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
             long elapsedMillis, @NonNull int... uids);
+
+    /**
+     * Reports a sound trigger recognition event that may have woken up the CPU.
+     * @param elapsedMillis The time when the event happened in the elapsed timebase.
+     * @param uid The uid that requested this trigger.
+     */
+    public abstract void noteWakingSoundTrigger(long elapsedMillis, int uid);
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index cfd22e8..a66e598 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -846,7 +846,7 @@
             mCallForwarding[i] =  false;
             mCellIdentity[i] = null;
             mCellInfo.add(i, Collections.EMPTY_LIST);
-            mImsReasonInfo.add(i, null);
+            mImsReasonInfo.add(i, new ImsReasonInfo());
             mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
@@ -1265,10 +1265,13 @@
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
-                    try {
-                        r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(r.phoneId));
-                    } catch (RemoteException ex) {
-                        remove(r.binder);
+                    ImsReasonInfo imsReasonInfo = mImsReasonInfo.get(r.phoneId);
+                    if (imsReasonInfo != null) {
+                        try {
+                            r.callback.onImsCallDisconnectCauseChanged(imsReasonInfo);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                 }
                 if (events.contains(
@@ -2418,6 +2421,11 @@
         int phoneId = getPhoneIdFromSubId(subId);
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
+                if (imsReasonInfo == null) {
+                    loge("ImsReasonInfo is null, subId=" + subId + ", phoneId=" + phoneId);
+                    mImsReasonInfo.set(phoneId, new ImsReasonInfo());
+                    return;
+                }
                 mImsReasonInfo.set(phoneId, imsReasonInfo);
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e1d1f6c..2aeaa2f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -962,7 +962,7 @@
     private static final String KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION =
             "enable_wait_for_finish_attach_application";
 
-    private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = true;
+    private static final boolean DEFAULT_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION = false;
 
     /** @see #KEY_ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION */
     public volatile boolean mEnableWaitForFinishAttachApplication =
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bb73877..c759af5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13282,8 +13282,10 @@
         // restored. This distinction is important for system-process packages that live in the
         // system user's process but backup/restore data for non-system users.
         // TODO (b/123688746): Handle all system-process packages with singleton check.
-        final int instantiatedUserId =
-                PLATFORM_PACKAGE_NAME.equals(packageName) ? UserHandle.USER_SYSTEM : targetUserId;
+        boolean useSystemUser = PLATFORM_PACKAGE_NAME.equals(packageName)
+                || getPackageManagerInternal().getSystemUiServiceComponent().getPackageName()
+                        .equals(packageName);
+        final int instantiatedUserId = useSystemUser ? UserHandle.USER_SYSTEM : targetUserId;
 
         IPackageManager pm = AppGlobals.getPackageManager();
         ApplicationInfo app = null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index f73594c..19235c9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -483,6 +483,12 @@
             Objects.requireNonNull(uids);
             mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
         }
+
+        @Override
+        public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
+            // TODO(b/267717665): Pipe to noteCpuWakingActivity once SoundTrigger starts using this.
+            Slog.w(TAG, "Sound trigger event dispatched to uid " + uid);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 7290f32..c07ef1d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -273,7 +273,7 @@
                         performReceiveLocked(oldRecord.resultToApp, oldRecord.resultTo,
                                 oldRecord.intent,
                                 Activity.RESULT_CANCELED, null, null,
-                                false, false, r.shareIdentity, oldRecord.userId,
+                                false, false, oldRecord.shareIdentity, oldRecord.userId,
                                 oldRecord.callingUid, r.callingUid, r.callerPackage,
                                 SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
                     } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index b952ce0..f954420 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1064,7 +1064,7 @@
         if (thread != null) {
             mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
                     app, OOM_ADJ_REASON_FINISH_RECEIVER);
-            if (r.shareIdentity) {
+            if (r.shareIdentity && app.uid != r.callingUid) {
                 mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
                         UserHandle.getAppId(app.uid), r.callingUid, true);
             }
diff --git a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java b/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
index 153403a..63575ba 100644
--- a/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
+++ b/services/core/java/com/android/server/am/BroadcastReceiverBatch.java
@@ -171,8 +171,9 @@
     // Add a ReceiverInfo for a registered receiver.
     void schedule(@Nullable IIntentReceiver receiver, Intent intent,
             int resultCode, @Nullable String data, @Nullable Bundle extras, boolean ordered,
-            boolean sticky, boolean assumeDelivered, int sendingUser, int callingUid,
-            String callingPackage, int processState, @Nullable BroadcastRecord r, int index) {
+            boolean sticky, boolean assumeDelivered, int sendingUser, int sendingUid,
+            @Nullable String sendingPackage, int processState, @Nullable BroadcastRecord r,
+            int index) {
         ReceiverInfo ri = new ReceiverInfo();
         ri.intent = intent;
         ri.data = data;
@@ -185,8 +186,8 @@
         ri.receiver = receiver;
         ri.ordered = ordered;
         ri.sticky = sticky;
-        ri.sentFromUid = callingUid;
-        ri.sentFromPackage = callingPackage;
+        ri.sendingUid = sendingUid;
+        ri.sendingPackage = sendingPackage;
 
         mReceivers.add(ri);
         mCookies.add(cookiePool.next().set(r, index));
@@ -195,7 +196,7 @@
     void schedule(@Nullable Intent intent, @Nullable ActivityInfo activityInfo,
             @Nullable CompatibilityInfo compatInfo, int resultCode, @Nullable String data,
             @Nullable Bundle extras, boolean sync, boolean assumeDelivered, int sendingUser,
-            int callingUid, @Nullable String callingPackage, int processState,
+            int sendingUid, @Nullable String sendingPackage, int processState,
             @Nullable BroadcastRecord r, int index) {
         ReceiverInfo ri = new ReceiverInfo();
         ri.intent = intent;
@@ -209,8 +210,8 @@
         ri.activityInfo = activityInfo;
         ri.compatInfo = compatInfo;
         ri.sync = sync;
-        ri.sentFromUid = callingUid;
-        ri.sentFromPackage = callingPackage;
+        ri.sendingUid = sendingUid;
+        ri.sendingPackage = sendingPackage;
         mReceivers.add(ri);
         mCookies.add(cookiePool.next().set(r, index));
     }
@@ -223,21 +224,21 @@
     ArrayList<ReceiverInfo> registeredReceiver(@Nullable IIntentReceiver receiver,
             @Nullable Intent intent, int resultCode, @Nullable String data,
             @Nullable Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
-            int sendingUser, int callingUid, String callingPackage, int processState) {
+            int sendingUser, int sendingUid, @Nullable String sendingPackage, int processState) {
         reset();
         schedule(receiver, intent, resultCode, data, extras, ordered, sticky, assumeDelivered,
-                sendingUser, callingUid, callingPackage, processState, null, 0);
+                sendingUser, sendingUid, sendingPackage, processState, null, 0);
         return receivers();
     }
 
     ArrayList<ReceiverInfo> manifestReceiver(@Nullable Intent intent,
             @Nullable ActivityInfo activityInfo, @Nullable CompatibilityInfo compatInfo,
             int resultCode, @Nullable String data, @Nullable Bundle extras, boolean sync,
-            boolean assumeDelivered, int sendingUser, int callingUid, String callingPackage,
-            int processState) {
+            boolean assumeDelivered, int sendingUser, int sendingUid,
+            @Nullable String sendingPackage, int processState) {
         reset();
         schedule(intent, activityInfo, compatInfo, resultCode, data, extras, sync, assumeDelivered,
-                sendingUser, callingUid, callingPackage, processState, null, 0);
+                sendingUser, sendingUid, sendingPackage, processState, null, 0);
         return receivers();
     }
 
diff --git a/services/core/java/com/android/server/am/SameProcessApplicationThread.java b/services/core/java/com/android/server/am/SameProcessApplicationThread.java
index dcb02ea..82dd5c2 100644
--- a/services/core/java/com/android/server/am/SameProcessApplicationThread.java
+++ b/services/core/java/com/android/server/am/SameProcessApplicationThread.java
@@ -48,12 +48,12 @@
     @Override
     public void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
             int resultCode, String data, Bundle extras, boolean ordered, boolean assumeDelivered,
-            int sendingUser, int processState, int sentFromUid, String sentFromPackage) {
+            int sendingUser, int processState, int sendingUid, String sendingPackage) {
         mHandler.post(() -> {
             try {
                 mWrapped.scheduleReceiver(intent, info, compatInfo, resultCode, data, extras,
-                        ordered, assumeDelivered, sendingUser, processState, sentFromUid,
-                        sentFromPackage);
+                        ordered, assumeDelivered, sendingUser, processState, sendingUid,
+                        sendingPackage);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -63,12 +63,12 @@
     @Override
     public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode,
             String data, Bundle extras, boolean ordered, boolean sticky, boolean assumeDelivered,
-            int sendingUser, int processState, int sentFromUid, String sentFromPackage) {
+            int sendingUser, int processState, int sendingUid, String sendingPackage) {
         mHandler.post(() -> {
             try {
                 mWrapped.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras,
-                        ordered, sticky, assumeDelivered, sendingUser, processState, sentFromUid,
-                        sentFromPackage);
+                        ordered, sticky, assumeDelivered, sendingUser, processState, sendingUid,
+                        sendingPackage);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -82,11 +82,11 @@
             if (r.registered) {
                 scheduleRegisteredReceiver(r.receiver, r.intent,
                         r.resultCode, r.data, r.extras, r.ordered, r.sticky, r.assumeDelivered,
-                        r.sendingUser, r.processState, r.sentFromUid, r.sentFromPackage);
+                        r.sendingUser, r.processState, r.sendingUid, r.sendingPackage);
             } else {
                 scheduleReceiver(r.intent, r.activityInfo, r.compatInfo,
                         r.resultCode, r.data, r.extras, r.sync, r.assumeDelivered,
-                        r.sendingUser, r.processState, r.sentFromUid, r.sentFromPackage);
+                        r.sendingUser, r.processState, r.sendingUid, r.sendingPackage);
             }
         }
     }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ec2e254..0318b24 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -488,31 +488,65 @@
      * migrated already.
      */
     private void migrateSyncJobNamespaceIfNeeded() {
-        if (mSyncStorageEngine.isJobNamespaceMigrated()) {
+        final boolean namespaceMigrated = mSyncStorageEngine.isJobNamespaceMigrated();
+        final boolean attributionFixed = mSyncStorageEngine.isJobAttributionFixed();
+        if (namespaceMigrated && attributionFixed) {
             return;
         }
         final JobScheduler jobSchedulerDefaultNamespace =
                 mContext.getSystemService(JobScheduler.class);
-        final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs();
-        // Wait until we've confirmed that all syncs have been migrated to the new namespace
-        // before we persist successful migration to our status file. This is done to avoid
+        if (!namespaceMigrated) {
+            final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs();
+            // Wait until we've confirmed that all syncs have been migrated to the new namespace
+            // before we persist successful migration to our status file. This is done to avoid
+            // internal consistency issues if the devices reboots right after SyncManager has
+            // done the migration on its side but before JobScheduler has finished persisting
+            // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk,
+            // then nothing that happened afterwards should have been persisted either, so there's
+            // no concern over activity happening after the migration causing issues.
+            boolean allSyncsMigrated = true;
+            for (int i = pendingJobs.size() - 1; i >= 0; --i) {
+                final JobInfo job = pendingJobs.get(i);
+                final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
+                if (op != null) {
+                    // This is a sync. Move it over to SyncManager's namespace.
+                    mJobScheduler.scheduleAsPackage(job,
+                            op.owningPackage, op.target.userId, op.wakeLockName());
+                    jobSchedulerDefaultNamespace.cancel(job.getId());
+                    allSyncsMigrated = false;
+                }
+            }
+            mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated);
+        }
+
+        // Fix attribution for any syncs that were previously scheduled using
+        // JobScheduler.schedule() instead of JobScheduler.scheduleAsPackage().
+        final List<JobInfo> namespacedJobs = LocalServices.getService(JobSchedulerInternal.class)
+                .getSystemScheduledOwnJobs(mJobScheduler.getNamespace());
+        // Wait until we've confirmed that all syncs have been proper attribution
+        // before we persist attribution state to our status file. This is done to avoid
         // internal consistency issues if the devices reboots right after SyncManager has
-        // done the migration on its side but before JobScheduler has finished persisting
+        // rescheduled the job on its side but before JobScheduler has finished persisting
         // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk,
         // then nothing that happened afterwards should have been persisted either, so there's
         // no concern over activity happening after the migration causing issues.
-        boolean allSyncsMigrated = true;
-        for (int i = pendingJobs.size() - 1; i >= 0; --i) {
-            final JobInfo job = pendingJobs.get(i);
+        // This case is done to fix issues for a subset of test devices.
+        // TODO: remove this attribution check/fix code
+        boolean allSyncsAttributed = true;
+        for (int i = namespacedJobs.size() - 1; i >= 0; --i) {
+            final JobInfo job = namespacedJobs.get(i);
             final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
             if (op != null) {
-                // This is a sync. Move it over to SyncManager's namespace.
-                mJobScheduler.schedule(job);
-                jobSchedulerDefaultNamespace.cancel(job.getId());
-                allSyncsMigrated = false;
+                // This is a sync. Make sure it's properly attributed to the app
+                // instead of the system.
+                // Since the job ID stays the same, scheduleAsPackage will replace the scheduled
+                // job, so we don't need to call cancel as well.
+                mJobScheduler.scheduleAsPackage(job,
+                        op.owningPackage, op.target.userId, op.wakeLockName());
+                allSyncsAttributed = false;
             }
         }
-        mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated);
+        mSyncStorageEngine.setJobAttributionFixed(allSyncsAttributed);
     }
 
     private synchronized void verifyJobScheduler() {
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9f3302d..b890bbd 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -173,6 +173,7 @@
     private volatile boolean mIsClockValid;
 
     private volatile boolean mIsJobNamespaceMigrated;
+    private volatile boolean mIsJobAttributionFixed;
 
     static {
         sAuthorityRenames = new HashMap<String, String>();
@@ -852,6 +853,20 @@
         return mIsJobNamespaceMigrated;
     }
 
+    void setJobAttributionFixed(boolean fixed) {
+        if (mIsJobAttributionFixed == fixed) {
+            return;
+        }
+        mIsJobAttributionFixed = fixed;
+        // This isn't urgent enough to write synchronously. Post it to the handler thread so
+        // SyncManager can move on with whatever it was doing.
+        mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY);
+    }
+
+    boolean isJobAttributionFixed() {
+        return mIsJobAttributionFixed;
+    }
+
     public Pair<Long, Long> getBackoff(EndPoint info) {
         synchronized (mAuthorities) {
             AuthorityInfo authority = getAuthorityLocked(info, "getBackoff");
@@ -2120,6 +2135,10 @@
                     mIsJobNamespaceMigrated =
                             proto.readBoolean(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED);
                     break;
+                case (int) SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED:
+                    mIsJobAttributionFixed =
+                            proto.readBoolean(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED);
+                    break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     return;
             }
@@ -2389,6 +2408,7 @@
         }
 
         proto.write(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED, mIsJobNamespaceMigrated);
+        proto.write(SyncStatusProto.IS_JOB_ATTRIBUTION_FIXED, mIsJobAttributionFixed);
 
         proto.flush();
     }
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index d7306b7..66fe019 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -294,8 +294,6 @@
                         new int[] {ACTIVITY_TYPE_DREAM});
 
                 mListener.onDreamStopped(dream.mToken);
-            } else if (dream.mCanDoze && !mCurrentDream.mCanDoze) {
-                mListener.stopDozing(dream.mToken);
             }
 
         } finally {
@@ -343,7 +341,6 @@
      */
     public interface Listener {
         void onDreamStopped(Binder token);
-        void stopDozing(Binder token);
     }
 
     private final class DreamRecord implements DeathRecipient, ServiceConnection {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index caa5036..7802b9d 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -500,12 +500,7 @@
         }
 
         synchronized (mLock) {
-            if (mCurrentDream == null) {
-                return;
-            }
-
-            final boolean sameDream = mCurrentDream.token == token;
-            if ((sameDream && mCurrentDream.isDozing) || (!sameDream && !mCurrentDream.isDozing)) {
+            if (mCurrentDream != null && mCurrentDream.token == token && mCurrentDream.isDozing) {
                 mCurrentDream.isDozing = false;
                 mDozeWakeLock.release();
                 mPowerManagerInternal.setDozeOverrideFromDreamManager(
@@ -671,6 +666,10 @@
 
         Slog.i(TAG, "Entering dreamland.");
 
+        if (mCurrentDream != null && mCurrentDream.isDozing) {
+            stopDozingInternal(mCurrentDream.token);
+        }
+
         mCurrentDream = new DreamRecord(name, userId, isPreviewMode, canDoze);
 
         if (!mCurrentDream.name.equals(mAmbientDisplayComponent)) {
@@ -776,11 +775,6 @@
                 }
             }
         }
-
-        @Override
-        public void stopDozing(Binder token) {
-            stopDozingInternal(token);
-        }
     };
 
     private final ContentObserver mDozeEnabledObserver = new ContentObserver(null) {
diff --git a/services/core/java/com/android/server/location/altitude/AltitudeService.java b/services/core/java/com/android/server/location/altitude/AltitudeService.java
new file mode 100644
index 0000000..b321e4d
--- /dev/null
+++ b/services/core/java/com/android/server/location/altitude/AltitudeService.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.location.altitude;
+
+import android.content.Context;
+import android.frameworks.location.altitude.AddMslAltitudeToLocationRequest;
+import android.frameworks.location.altitude.AddMslAltitudeToLocationResponse;
+import android.frameworks.location.altitude.IAltitudeService;
+import android.location.Location;
+import android.location.altitude.AltitudeConverter;
+import android.os.RemoteException;
+
+import com.android.server.SystemService;
+
+import java.io.IOException;
+
+/**
+ * Manages altitude information exchange through the HAL, e.g., geoid height requests such that
+ * vendors can perform altitude conversions.
+ *
+ * @hide
+ */
+public class AltitudeService extends IAltitudeService.Stub {
+
+    private final AltitudeConverter mAltitudeConverter = new AltitudeConverter();
+    private final Context mContext;
+
+    /** @hide */
+    public AltitudeService(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public AddMslAltitudeToLocationResponse addMslAltitudeToLocation(
+            AddMslAltitudeToLocationRequest request) throws RemoteException {
+        Location location = new Location("");
+        location.setLatitude(request.latitudeDegrees);
+        location.setLongitude(request.longitudeDegrees);
+        location.setAltitude(request.altitudeMeters);
+        location.setVerticalAccuracyMeters(request.verticalAccuracyMeters);
+        try {
+            mAltitudeConverter.addMslAltitudeToLocation(mContext, location);
+        } catch (IOException e) {
+            throw new RemoteException(e);
+        }
+
+        AddMslAltitudeToLocationResponse response = new AddMslAltitudeToLocationResponse();
+        response.mslAltitudeMeters = location.getMslAltitudeMeters();
+        response.mslAltitudeAccuracyMeters = location.getMslAltitudeAccuracyMeters();
+        return response;
+    }
+
+    @Override
+    public String getInterfaceHash() {
+        return IAltitudeService.HASH;
+    }
+
+    @Override
+    public int getInterfaceVersion() {
+        return IAltitudeService.VERSION;
+    }
+
+    /** @hide */
+    public static class Lifecycle extends SystemService {
+
+        private static final String SERVICE_NAME = IAltitudeService.DESCRIPTOR + "/default";
+
+        private AltitudeService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mService = new AltitudeService(getContext());
+            publishBinderService(SERVICE_NAME, mService);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index d61e45b..149a915 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2553,7 +2553,7 @@
      */
     @NonNull
     public RemoteLockscreenValidationResult
-            validateRemoteLockScreen2(@NonNull byte[] encryptedCredential) {
+            validateRemoteLockscreen(@NonNull byte[] encryptedCredential) {
         return mRecoverableKeyStoreManager.validateRemoteLockscreen(encryptedCredential, 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 33dc7ef..6bf64eb 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -1126,11 +1126,10 @@
     }
 
     private void checkVerifyRemoteLockscreenPermission() {
-        // TODO(b/254335492): Check new system permission
         mContext.enforceCallingOrSelfPermission(
-                Manifest.permission.RECOVER_KEYSTORE,
+                Manifest.permission.CHECK_REMOTE_LOCKSCREEN,
                 "Caller " + Binder.getCallingUid()
-                        + " doesn't have verifyRemoteLockscreen permission.");
+                        + " doesn't have CHECK_REMOTE_LOCKSCREEN permission.");
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
         mCleanupManager.registerRecoveryAgent(userId, uid);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0aa822b..94d5d53 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -114,6 +114,7 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
@@ -271,6 +272,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.MetricsLogger;
@@ -577,11 +579,11 @@
     private float mInCallNotificationVolume;
     private Binder mCallNotificationToken = null;
 
-    private static final boolean ONGOING_DISMISSAL = SystemProperties.getBoolean(
-            "persist.sysui.notification.ongoing_dismissal", true);
     @VisibleForTesting
     protected boolean mSystemExemptFromDismissal = false;
 
+    private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+
     // used as a mutex for access to all active notifications & listeners
     final Object mNotificationLock = new Object();
     @GuardedBy("mNotificationLock")
@@ -1208,7 +1210,8 @@
                 }
             }
 
-            int mustNotHaveFlags = ONGOING_DISMISSAL ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT;
+            int mustNotHaveFlags = mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)
+                    ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT;
             cancelNotification(callingUid, callingPid, pkg, tag, id,
                     /* mustHaveFlags= */ 0,
                     /* mustNotHaveFlags= */ mustNotHaveFlags,
@@ -2219,7 +2222,8 @@
             TelephonyManager telephonyManager, ActivityManagerInternal ami,
             MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper,
             UsageStatsManagerInternal usageStatsManagerInternal,
-            TelecomManager telecomManager, NotificationChannelLogger channelLogger) {
+            TelecomManager telecomManager, NotificationChannelLogger channelLogger,
+            SystemUiSystemPropertiesFlags.FlagResolver flagResolver) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2417,6 +2421,8 @@
         mMsgPkgsAllowedAsConvos = Set.of(getStringArrayResource(
                 com.android.internal.R.array.config_notificationMsgPkgsAllowedAsConvos));
 
+        mFlagResolver = flagResolver;
+
         mStatsManager = statsManager;
 
         mToastRateLimiter = toastRateLimiter;
@@ -2548,7 +2554,7 @@
                         AppGlobals.getPermissionManager()),
                 LocalServices.getService(UsageStatsManagerInternal.class),
                 getContext().getSystemService(TelecomManager.class),
-                new NotificationChannelLoggerImpl());
+                new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver());
 
         publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false,
                 DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL);
@@ -6516,7 +6522,7 @@
 
         // Fix the notification as best we can.
         try {
-            fixNotification(notification, pkg, tag, id, userId);
+            fixNotification(notification, pkg, tag, id, userId, notificationUid);
         } catch (Exception e) {
             if (notification.isForegroundService()) {
                 throw new SecurityException("Invalid FGS notification", e);
@@ -6694,14 +6700,15 @@
 
     @VisibleForTesting
     protected void fixNotification(Notification notification, String pkg, String tag, int id,
-            int userId) throws NameNotFoundException, RemoteException {
+            @UserIdInt int userId, int notificationUid) throws NameNotFoundException,
+            RemoteException {
         final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                 pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                 (userId == UserHandle.USER_ALL) ? USER_SYSTEM : userId);
         Notification.addFieldsFromContext(ai, notification);
 
         // Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS
-        if (ONGOING_DISMISSAL) {
+        if (mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)) {
             if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
                     && canBeNonDismissible(ai, notification)) {
                 notification.flags |= FLAG_NO_DISMISS;
@@ -6710,17 +6717,31 @@
             }
         }
 
-        int canColorize = mPackageManagerClient.checkPermission(
-                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
+        int canColorize = getContext().checkPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, -1, notificationUid);
+
         if (canColorize == PERMISSION_GRANTED) {
             notification.flags |= Notification.FLAG_CAN_COLORIZE;
         } else {
             notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
         }
 
+        if (notification.extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, false)) {
+            int hasShowDuringSetupPerm = getContext().checkPermission(
+                    android.Manifest.permission.NOTIFICATION_DURING_SETUP, -1, notificationUid);
+            if (hasShowDuringSetupPerm != PERMISSION_GRANTED) {
+                notification.extras.remove(Notification.EXTRA_ALLOW_DURING_SETUP);
+                if (DBG) {
+                    Slog.w(TAG, "warning: pkg " + pkg + " attempting to show during setup"
+                            + " without holding perm "
+                            + Manifest.permission.NOTIFICATION_DURING_SETUP);
+                }
+            }
+        }
+
         if (notification.fullScreenIntent != null && ai.targetSdkVersion >= Build.VERSION_CODES.Q) {
-            int fullscreenIntentPermission = mPackageManagerClient.checkPermission(
-                    android.Manifest.permission.USE_FULL_SCREEN_INTENT, pkg);
+            int fullscreenIntentPermission = getContext().checkPermission(
+                    android.Manifest.permission.USE_FULL_SCREEN_INTENT, -1, notificationUid);
             if (fullscreenIntentPermission != PERMISSION_GRANTED) {
                 notification.fullScreenIntent = null;
                 Slog.w(TAG, "Package " + pkg +
@@ -6765,8 +6786,8 @@
 
         // Ensure MediaStyle has correct permissions for remote device extras
         if (notification.isStyle(Notification.MediaStyle.class)) {
-            int hasMediaContentControlPermission = mPackageManager.checkPermission(
-                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, pkg, userId);
+            int hasMediaContentControlPermission = getContext().checkPermission(
+                    android.Manifest.permission.MEDIA_CONTENT_CONTROL, -1, notificationUid);
             if (hasMediaContentControlPermission != PERMISSION_GRANTED) {
                 notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_DEVICE);
                 notification.extras.remove(Notification.EXTRA_MEDIA_REMOTE_ICON);
@@ -6780,8 +6801,8 @@
 
         // Ensure only allowed packages have a substitute app name
         if (notification.extras.containsKey(Notification.EXTRA_SUBSTITUTE_APP_NAME)) {
-            int hasSubstituteAppNamePermission = mPackageManager.checkPermission(
-                    permission.SUBSTITUTE_NOTIFICATION_APP_NAME, pkg, userId);
+            int hasSubstituteAppNamePermission = getContext().checkPermission(
+                    permission.SUBSTITUTE_NOTIFICATION_APP_NAME, -1, notificationUid);
             if (hasSubstituteAppNamePermission != PERMISSION_GRANTED) {
                 notification.extras.remove(Notification.EXTRA_SUBSTITUTE_APP_NAME);
                 if (DBG) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 062f0fc..d471c8a 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -301,7 +301,7 @@
                     && shellPkgName.equals(overlayInfo.packageName));
 
             initIfNeeded();
-            onSwitchUser(UserHandle.USER_SYSTEM);
+            onStartUser(UserHandle.USER_SYSTEM);
 
             publishBinderService(Context.OVERLAY_SERVICE, mService);
             publishLocalService(OverlayManagerService.class, this);
@@ -324,7 +324,7 @@
                 final UserInfo userInfo = users.get(i);
                 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
                     // Initialize any users that can't be switched to, as their state would
-                    // never be setup in onSwitchUser(). We will switch to the system user right
+                    // never be setup in onStartUser(). We will switch to the system user right
                     // after this, and its state will be setup there.
                     updatePackageManagerLocked(mImpl.updateOverlaysForUser(users.get(i).id));
                 }
@@ -333,13 +333,13 @@
     }
 
     @Override
-    public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
-        onSwitchUser(to.getUserIdentifier());
+    public void onUserStarting(TargetUser user) {
+        onStartUser(user.getUserIdentifier());
     }
 
-    private void onSwitchUser(@UserIdInt int newUserId) {
+    private void onStartUser(@UserIdInt int newUserId) {
         try {
-            traceBegin(TRACE_TAG_RRO, "OMS#onSwitchUser " + newUserId);
+            traceBegin(TRACE_TAG_RRO, "OMS#onStartUser " + newUserId);
             // ensure overlays in the settings are up-to-date, and propagate
             // any asset changes to the rest of the system
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 198c339..477a8a6 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3154,8 +3154,9 @@
                 }
             }
 
-            final ActivityOptions clientOptions = ActivityOptions.makeBasic();
-            clientOptions.setIgnorePendingIntentCreatorForegroundState(true);
+            final ActivityOptions clientOptions = ActivityOptions.makeBasic()
+                    .setPendingIntentCreatorBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED);
             PendingIntent clientIntent = PendingIntent.getActivityAsUser(
                     mContext, 0, Intent.createChooser(
                             new Intent(Intent.ACTION_SET_WALLPAPER),
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 4507637..502bfd1 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -781,6 +781,7 @@
                 // the running transition finish.
                 final Transition transition = r != null
                         && r.mTransitionController.inPlayingTransition(r)
+                        && !r.mTransitionController.isCollecting()
                         ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null;
                 if (transition != null) {
                     r.mTransitionController.requestStartTransition(transition, null /*startTask */,
@@ -820,6 +821,7 @@
                 // visibility while playing transition, there won't able to commit visibility until
                 // the running transition finish.
                 final Transition transition = r.mTransitionController.inPlayingTransition(r)
+                        && !r.mTransitionController.isCollecting()
                         ? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
                 if (transition != null) {
                     r.mTransitionController.requestStartTransition(transition, null /*startTask */,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c289153..2b9364c2 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3919,6 +3919,10 @@
         }
     }
 
+    boolean isFinishing() {
+        return finishing;
+    }
+
     /**
      * This method is to only be called from the client via binder when the activity is destroyed
      * AND finished.
diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
index e04900c..47bdba3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java
@@ -250,10 +250,7 @@
         }
         ActivityRecord topActivity = mDisplayContent.topRunningActivity(
                     /* considerKeyguardState= */ true);
-        if (topActivity == null
-                // Checking windowing mode on activity level because we don't want to
-                // show toast in case of activity embedding.
-                || topActivity.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
+        if (!isTreatmentEnabledForActivity(topActivity)) {
             return;
         }
         showToast(R.string.display_rotation_camera_compat_toast_after_rotation);
@@ -309,21 +306,28 @@
     }
 
     boolean isActivityEligibleForOrientationOverride(@NonNull ActivityRecord activity) {
-        return isTreatmentEnabledForDisplay() && isCameraActiveInFullscreen(activity);
+        return isTreatmentEnabledForDisplay()
+                && isCameraActive(activity, /* mustBeFullscreen */ true);
     }
 
+
     /**
      * Whether camera compat treatment is applicable for the given activity.
      *
      * <p>Conditions that need to be met:
      * <ul>
-     *     <li>{@link #isCameraActiveForPackage} is {@code true} for the activity.
+     *     <li>Camera is active for the package.
      *     <li>The activity is in fullscreen
      *     <li>The activity has fixed orientation but not "locked" or "nosensor" one.
      * </ul>
      */
     boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity) {
-        return activity != null && isCameraActiveInFullscreen(activity)
+        return isTreatmentEnabledForActivity(activity, /* mustBeFullscreen */ true);
+    }
+
+    private boolean isTreatmentEnabledForActivity(@Nullable ActivityRecord activity,
+            boolean mustBeFullscreen) {
+        return activity != null && isCameraActive(activity, mustBeFullscreen)
                 && activity.getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED
                 // "locked" and "nosensor" values are often used by camera apps that can't
                 // handle dynamic changes so we shouldn't force rotate them.
@@ -331,8 +335,10 @@
                 && activity.getOverrideOrientation() != SCREEN_ORIENTATION_LOCKED;
     }
 
-    private boolean isCameraActiveInFullscreen(@NonNull ActivityRecord activity) {
-        return !activity.inMultiWindowMode()
+    private boolean isCameraActive(@NonNull ActivityRecord activity, boolean mustBeFullscreen) {
+        // Checking windowing mode on activity level because we don't want to
+        // apply treatment in case of activity embedding.
+        return (!mustBeFullscreen || !activity.inMultiWindowMode())
                 && mCameraIdPackageBiMap.containsPackageName(activity.packageName)
                 && activity.mLetterboxUiController.shouldForceRotateForCameraCompat();
     }
@@ -385,7 +391,8 @@
         }
         // Checking that the whole app is in multi-window mode as we shouldn't show toast
         // for the activity embedding case.
-        if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
+        if (topActivity.getTask().getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
+                && isTreatmentEnabledForActivity(topActivity, /* mustBeFullscreen */ false)) {
             showToast(R.string.display_rotation_camera_compat_toast_in_split_screen);
         }
     }
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 9681789..1aa0ec3 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -105,6 +105,7 @@
 
 import java.io.PrintWriter;
 import java.util.function.BooleanSupplier;
+import java.util.function.Predicate;
 
 /** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
 // TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
@@ -114,6 +115,9 @@
 // TODO(b/263021211): Consider renaming to more generic CompatUIController.
 final class LetterboxUiController {
 
+    private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE =
+            activityRecord -> activityRecord.fillsParent() && !activityRecord.isFinishing();
+
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
 
     private static final float UNDEFINED_ASPECT_RATIO = 0f;
@@ -1390,7 +1394,8 @@
             return;
         }
         final ActivityRecord firstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity(
-                ActivityRecord::fillsParent, mActivityRecord, false /* includeBoundary */,
+                FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */,
+                mActivityRecord /* boundary */, false /* includeBoundary */,
                 true /* traverseTopToBottom */);
         if (firstOpaqueActivityBeneath == null) {
             // We skip letterboxing if the translucent activity doesn't have any opaque
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index 2345e3f..c8518c5 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -97,7 +97,7 @@
         if (response != null) {
             respondToClientWithResponseAndFinish(response);
         } else {
-            respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREDENTIAL,
+            respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
                     "Invalid response");
         }
     }
@@ -119,6 +119,12 @@
         }
     }
 
+    @Override
+    public void onUiSelectorInvocationFailure() {
+        respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
+                "No create options available.");
+    }
+
     private void respondToClientWithResponseAndFinish(CreateCredentialResponse response) {
         Log.i(TAG, "respondToClientWithResponseAndFinish");
         if (isSessionCancelled()) {
@@ -166,8 +172,8 @@
                 Log.i(TAG, "in onProviderStatusChanged - isUiInvocationNeeded");
                 getProviderDataAndInitiateUi();
             } else {
-                respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREDENTIAL,
-                        "No credentials available");
+                respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
+                        "No create options available.");
             }
         }
     }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 6857239..edffad2 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -472,8 +472,8 @@
             if (providerSessions.isEmpty()) {
                 try {
                     callback.onError(
-                            CreateCredentialException.TYPE_NO_CREDENTIAL,
-                            "No credentials available on this device.");
+                            CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
+                            "No create options available.");
                 } catch (RemoteException e) {
                     Log.i(
                             TAG,
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 797601a..a6f6a830 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -56,19 +56,29 @@
     };
 
     private void handleUiResult(int resultCode, Bundle resultData) {
-        if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION) {
-            UserSelectionDialogResult selection = UserSelectionDialogResult
-                    .fromResultData(resultData);
-            if (selection != null) {
-                mCallbacks.onUiSelection(selection);
-            } else {
-                Slog.i(TAG, "No selection found in UI result");
-            }
-        } else if (resultCode == UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED) {
-            mCallbacks.onUiCancellation(/* isUserCancellation= */ true);
-        } else if (resultCode
-                == UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS) {
-            mCallbacks.onUiCancellation(/* isUserCancellation= */ false);
+        switch (resultCode) {
+            case UserSelectionDialogResult.RESULT_CODE_DIALOG_COMPLETE_WITH_SELECTION:
+                UserSelectionDialogResult selection = UserSelectionDialogResult
+                        .fromResultData(resultData);
+                if (selection != null) {
+                    mCallbacks.onUiSelection(selection);
+                } else {
+                    Slog.i(TAG, "No selection found in UI result");
+                }
+                break;
+            case UserSelectionDialogResult.RESULT_CODE_DIALOG_USER_CANCELED:
+                mCallbacks.onUiCancellation(/* isUserCancellation= */ true);
+                break;
+            case UserSelectionDialogResult.RESULT_CODE_CANCELED_AND_LAUNCHED_SETTINGS:
+                mCallbacks.onUiCancellation(/* isUserCancellation= */ false);
+                break;
+            case UserSelectionDialogResult.RESULT_CODE_DATA_PARSING_FAILURE:
+                mCallbacks.onUiSelectorInvocationFailure();
+                break;
+            default:
+                Slog.i(TAG, "Unknown error code returned from the UI");
+                mCallbacks.onUiSelectorInvocationFailure();
+                break;
         }
     }
 
@@ -80,6 +90,9 @@
         void onUiSelection(UserSelectionDialogResult selection);
         /** Called when the UI is canceled without a successful provider result. */
         void onUiCancellation(boolean isUserCancellation);
+
+        /** Called when the selector UI fails to come up (mostly due to parsing issue today). */
+        void onUiSelectorInvocationFailure();
     }
     public CredentialManagerUi(Context context, int userId,
             CredentialManagerUiCallback callbacks) {
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index e732c23..3324999 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -147,6 +147,12 @@
     }
 
     @Override
+    public void onUiSelectorInvocationFailure() {
+        respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
+                    "No credentials to show on the selector.");
+    }
+
+    @Override
     public void onProviderStatusChanged(ProviderSession.Status status,
             ComponentName componentName) {
         Log.i(TAG, "in onStatusChanged with status: " + status);
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index 20e358c..3f285b7 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -96,13 +96,14 @@
         if (propagateToProvider) {
             return new BeginCreateCredentialRequest(
                     type,
-                    candidateQueryData
+                    candidateQueryData,
+                    callingAppInfo
             );
         }
         return new BeginCreateCredentialRequest(
                 type,
-                candidateQueryData,
-                callingAppInfo);
+                candidateQueryData
+        );
     }
 
     @Nullable
@@ -168,7 +169,16 @@
     private void onUpdateResponse(BeginCreateCredentialResponse response) {
         Log.i(TAG, "updateResponse with save entries");
         mProviderResponse = response;
-        updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED);
+        if (isEmptyResponse(response)) {
+            updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
+        } else {
+            updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED);
+        }
+    }
+
+    private boolean isEmptyResponse(BeginCreateCredentialResponse response) {
+        return (response.getCreateEntries() == null || response.getCreateEntries().isEmpty())
+                && response.getRemoteCreateEntry() == null;
     }
 
     @Override
@@ -294,7 +304,7 @@
             ProviderPendingIntentResponse pendingIntentResponse) {
         if (pendingIntentResponse == null) {
             Log.i(TAG, "pendingIntentResponse is null");
-            return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREDENTIAL);
+            return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREATE_OPTIONS);
         }
         if (PendingIntentResultHandler.isValidResponse(pendingIntentResponse)) {
             CreateCredentialException exception = PendingIntentResultHandler
@@ -306,7 +316,7 @@
         } else if (PendingIntentResultHandler.isCancelledResponse(pendingIntentResponse)) {
             return new CreateCredentialException(CreateCredentialException.TYPE_USER_CANCELED);
         } else {
-            return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREDENTIAL);
+            return new CreateCredentialException(CreateCredentialException.TYPE_NO_CREATE_OPTIONS);
         }
         return null;
     }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index 9fba95b..764aac3 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -108,7 +108,6 @@
         Log.i(TAG, "Unable to create provider session");
         return null;
     }
-
     private static BeginGetCredentialRequest constructQueryPhaseRequest(
             android.credentials.GetCredentialRequest filteredRequest,
             CallingAppInfo callingAppInfo,
@@ -306,18 +305,20 @@
             Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
             credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
                     credentialEntry.getSlice(),
-                    /*fillInIntent=*/setUpFillInIntent(credentialEntry.getType())));
+                    /*fillInIntent=*/setUpFillInIntent(credentialEntry
+                    .getBeginGetCredentialOption().getId())));
         }
         return credentialUiEntries;
     }
 
-    private Intent setUpFillInIntent(@Nullable String id) {
+    private Intent setUpFillInIntent(@NonNull String id) {
         // TODO: Determine if we should skip this entry if entry id is not set, or is set
         // but does not resolve to a valid option. For now, not skipping it because
         // it may be possible that the provider adds their own extras and expects to receive
         // those and complete the flow.
-        if (id == null || mBeginGetOptionToCredentialOptionMap.get(id) == null) {
+        if (mBeginGetOptionToCredentialOptionMap.get(id) == null) {
             Log.i(TAG, "Id from Credential Entry does not resolve to a valid option");
+            return new Intent();
         }
         return new Intent().putExtra(CredentialProviderService.EXTRA_GET_CREDENTIAL_REQUEST,
                 new GetCredentialRequest(
@@ -445,7 +446,22 @@
     /** Updates the response being maintained in state by this provider session. */
     private void onUpdateResponse(BeginGetCredentialResponse response) {
         mProviderResponse = response;
-        updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+        if (isEmptyResponse(response)) {
+            updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
+        } else {
+            updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+        }
+    }
+
+    private boolean isEmptyResponse(BeginGetCredentialResponse response) {
+        if ((response.getCredentialEntries() == null || response.getCredentialEntries().isEmpty())
+                && (response.getAuthenticationActions() == null || response
+                .getAuthenticationActions().isEmpty())
+                && (response.getActions() == null || response.getActions().isEmpty())
+                && response.getRemoteCredentialEntry() == null) {
+            return true;
+        }
+        return false;
     }
 
     private void onUpdateEmptyResponse() {
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 1ae0f3c..1aec934 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -133,7 +133,7 @@
         PENDING_INTENT_INVOKED,
         CREDENTIAL_RECEIVED_FROM_SELECTION,
         SAVE_ENTRIES_RECEIVED, CANCELED,
-        NO_CREDENTIALS, COMPLETE
+        NO_CREDENTIALS, EMPTY_RESPONSE, COMPLETE
     }
 
     /** Converts exception to a provider session status. */
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 9f1bd8f..fdd0e81 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -144,6 +144,11 @@
         finishSession(/*propagateCancellation=*/false);
     }
 
+    @Override
+    public void onUiSelectorInvocationFailure() {
+        Log.i(TAG, "onUiSelectorInvocationFailure");
+    }
+
     protected void finishSession(boolean propagateCancellation) {
         Log.i(TAG, "finishing session");
         if (propagateCancellation) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cae6c39..271a3d3 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -142,6 +142,7 @@
 import com.android.server.lights.LightsService;
 import com.android.server.locales.LocaleManagerService;
 import com.android.server.location.LocationManagerService;
+import com.android.server.location.altitude.AltitudeService;
 import com.android.server.logcat.LogcatManagerService;
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.metrics.MediaMetricsManagerService;
@@ -2119,6 +2120,14 @@
             }
             t.traceEnd();
 
+            t.traceBegin("StartAltitudeService");
+            try {
+                mSystemServiceManager.startService(AltitudeService.Lifecycle.class);
+            } catch (Throwable e) {
+                reportWtf("starting AltitudeService service", e);
+            }
+            t.traceEnd();
+
             t.traceBegin("StartLocationTimeZoneManagerService");
             try {
                 mSystemServiceManager.startService(LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS);
diff --git a/services/manifest_services.xml b/services/manifest_services.xml
new file mode 100644
index 0000000..7638915
--- /dev/null
+++ b/services/manifest_services.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.frameworks.location.altitude</name>
+        <version>1</version>
+        <fqname>IAltitudeService/default</fqname>
+    </hal>
+</manifest>
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 32b9864..cc5c2f3 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -448,8 +448,7 @@
                 mA11yms.getCurrentUserIdLocked());
         Settings.Secure.putIntForUser(
                 mTestableContext.getContentResolver(),
-                // TODO: replace name with Settings Secure Key
-                "accessibility_magnification_always_on_enabled",
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
                 1, mA11yms.getCurrentUserIdLocked());
 
         mA11yms.readAlwaysOnMagnificationLocked(userState);
@@ -466,8 +465,7 @@
                 mA11yms.getCurrentUserIdLocked());
         Settings.Secure.putIntForUser(
                 mTestableContext.getContentResolver(),
-                // TODO: replace name with Settings Secure Key
-                "accessibility_magnification_always_on_enabled",
+                Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED,
                 1, mA11yms.getCurrentUserIdLocked());
 
         mA11yms.readAlwaysOnMagnificationLocked(userState);
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 32cb8c4..ad6d6b9 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
@@ -1316,10 +1316,10 @@
 
         mRecoverableKeyStoreManager.startRemoteLockscreenValidation(mLockSettingsService);
 
-        // TODO(b/254335492): Check new system permission
         verify(mMockContext, times(1))
                 .enforceCallingOrSelfPermission(
-                        eq(Manifest.permission.RECOVER_KEYSTORE), any());
+                        eq(Manifest.permission.CHECK_REMOTE_LOCKSCREEN), any());
+        mRemoteLockscreenValidationSessionStorage.finishSession(mUserId);
     }
     @Test
     public void startRemoteLockscreenValidation_returnsCredentailsType() throws Exception {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 38c2f40..c31c23f 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -19,6 +19,7 @@
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
 import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_CAN_COLORIZE;
@@ -71,6 +72,7 @@
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
+import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -201,6 +203,7 @@
 
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -373,6 +376,9 @@
     BroadcastReceiver mPackageIntentReceiver;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker;
+
+    TestFlagResolver mTestFlagResolver = new TestFlagResolver();
+
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
     @Mock
@@ -521,7 +527,7 @@
                 mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
                 mock(TelephonyManager.class),
                 mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
-                mTelecomManager, mLogger);
+                mTelecomManager, mLogger, mTestFlagResolver);
         // Return first true for RoleObserver main-thread check
         when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper);
@@ -1594,6 +1600,8 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -1622,6 +1630,8 @@
 
     @Test
     public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
         DeviceConfig.setProperty(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
@@ -4211,8 +4221,35 @@
     }
 
     @Test
+    public void testNoNotificationDuringSetupPermission() throws Exception {
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.NOTIFICATION_DURING_SETUP, PERMISSION_GRANTED);
+        Bundle extras = new Bundle();
+        extras.putBoolean(EXTRA_ALLOW_DURING_SETUP, true);
+        Notification.Builder nb = new Notification.Builder(mContext,
+                mTestNotificationChannel.getId())
+                .setContentTitle("foo")
+                .addExtras(extras)
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testNoNotificationDuringSetupPermission", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
+                nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId());
+        waitForIdle();
+
+        NotificationRecord posted = mService.findNotificationLocked(
+                PKG, nr.getSbn().getTag(), nr.getSbn().getId(), nr.getSbn().getUserId());
+
+        assertTrue(posted.getNotification().extras.containsKey(EXTRA_ALLOW_DURING_SETUP));
+    }
+
+    @Test
     public void testNoFakeColorizedPermission() throws Exception {
-        when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_DENIED);
         Notification.Builder nb = new Notification.Builder(mContext,
                 mTestNotificationChannel.getId())
                 .setContentTitle("foo")
@@ -4237,9 +4274,8 @@
     @Test
     public void testMediaStyleRemote_hasPermission() throws RemoteException {
         String deviceName = "device";
-        when(mPackageManager.checkPermission(
-                eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
-                .thenReturn(PERMISSION_GRANTED);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.MEDIA_CONTENT_CONTROL, PERMISSION_GRANTED);
         Notification.MediaStyle style = new Notification.MediaStyle();
         style.setRemotePlaybackInfo(deviceName, 0, null);
         Notification.Builder nb = new Notification.Builder(mContext,
@@ -4266,9 +4302,8 @@
     @Test
     public void testMediaStyleRemote_noPermission() throws RemoteException {
         String deviceName = "device";
-        when(mPackageManager.checkPermission(
-                eq(android.Manifest.permission.MEDIA_CONTENT_CONTROL), any(), anyInt()))
-                .thenReturn(PERMISSION_DENIED);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.MEDIA_CONTENT_CONTROL, PERMISSION_DENIED);
         Notification.MediaStyle style = new Notification.MediaStyle();
         style.setRemotePlaybackInfo(deviceName, 0, null);
         Notification.Builder nb = new Notification.Builder(mContext,
@@ -4294,9 +4329,8 @@
     @Test
     public void testSubstituteAppName_hasPermission() throws RemoteException {
         String subName = "Substitute Name";
-        when(mPackageManager.checkPermission(
-                eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt()))
-                .thenReturn(PERMISSION_GRANTED);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, PERMISSION_GRANTED);
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, subName);
         Notification.Builder nb = new Notification.Builder(mContext,
@@ -4321,9 +4355,8 @@
 
     @Test
     public void testSubstituteAppName_noPermission() throws RemoteException {
-        when(mPackageManager.checkPermission(
-                eq(android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME), any(), anyInt()))
-                .thenReturn(PERMISSION_DENIED);
+        mContext.getTestablePermissions().setPermission(
+                android.Manifest.permission.SUBSTITUTE_NOTIFICATION_APP_NAME, PERMISSION_DENIED);
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, "Substitute Name");
         Notification.Builder nb = new Notification.Builder(mContext,
@@ -8290,7 +8323,7 @@
         assertNotNull(n.publicVersion.bigContentView);
         assertNotNull(n.publicVersion.headsUpContentView);
 
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         assertNull(n.contentView);
         assertNull(n.bigContentView);
@@ -10092,8 +10125,8 @@
             throws Exception {
         // Given: a notification from an app on the system partition has the flag
         // FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -10104,21 +10137,18 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
     public void fixMediaNotification_withOnGoingFlag_shouldBeNonDismissible()
             throws Exception {
         // Given: a media notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .setStyle(new Notification.MediaStyle()
@@ -10126,32 +10156,26 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
     public void fixNonExemptNotification_withOnGoingFlag_shouldBeDismissible() throws Exception {
         // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
@@ -10159,28 +10183,25 @@
             throws Exception {
         // Given: a non-exempt notification has the flag FLAG_NO_DISMISS set (even though this is
         // not allowed)
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .build();
         n.flags |= Notification.FLAG_NO_DISMISS;
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
     public void fixSystemNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
         // Given: a notification from an app on the system partition doesn't have the flag
         // FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .build();
@@ -10191,13 +10212,10 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
@@ -10205,8 +10223,8 @@
             throws Exception {
         // Given: a notification from an app on the system partition doesn't have the flag
         // FLAG_ONGOING_EVENT set, but has the flag FLAG_NO_DISMISS set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .build();
@@ -10218,20 +10236,17 @@
                 .thenReturn(systemAppInfo);
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
     public void fixMediaNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
         // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .setStyle(new Notification.MediaStyle()
@@ -10239,13 +10254,10 @@
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
@@ -10253,8 +10265,8 @@
             throws Exception {
         // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set,
         // but has the flag FLAG_NO_DISMISS set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .setStyle(new Notification.MediaStyle()
@@ -10263,34 +10275,27 @@
         n.flags |= Notification.FLAG_NO_DISMISS;
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be cleared
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
     public void fixNonExempt_Notification_withoutOnGoingFlag_shouldBeDismissible()
             throws Exception {
         // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
@@ -10298,21 +10303,18 @@
             throws Exception {
         when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         mService.setSystemExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 
     @Test
@@ -10322,21 +10324,19 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         mService.setSystemExemptFromDismissal(true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should be set
         assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
 
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
         mService.setSystemExemptFromDismissal(false);
     }
 
@@ -10347,20 +10347,17 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
-        mService.setOngoingDismissal(true);
+        // feature flag: ALLOW_DISMISS_ONGOING is on
+        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         mService.setSystemExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
 
         // When: fix the notification with NotificationManagerService
-        mService.fixNotification(n, PKG, "tag", 9, 0);
+        mService.fixNotification(n, PKG, "tag", 9, 0, mUid);
 
         // Then: the notification's flag FLAG_NO_DISMISS should not be set
         assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
-
-        // Avoid affecting other tests
-        mService.setOngoingDismissal(false);
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 98c156e..6f2627a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -48,7 +48,6 @@
 import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -169,7 +168,7 @@
                     mock(ActivityManagerInternal.class),
                     mock(MultiRateLimiter.class), mock(PermissionHelper.class),
                     mock(UsageStatsManagerInternal.class), mock (TelecomManager.class),
-                    mock(NotificationChannelLogger.class));
+                    mock(NotificationChannelLogger.class), new TestFlagResolver());
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java b/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java
new file mode 100644
index 0000000..3b9726e
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestFlagResolver.java
@@ -0,0 +1,35 @@
+/*
+ * 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.server.notification;
+
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class TestFlagResolver implements SystemUiSystemPropertiesFlags.FlagResolver {
+    private Map<SystemUiSystemPropertiesFlags.Flag, Boolean> mOverrides = new HashMap<>();
+
+    @Override
+    public boolean isEnabled(SystemUiSystemPropertiesFlags.Flag flag) {
+        return mOverrides.getOrDefault(flag, flag.mDefaultValue);
+    }
+
+    public void setFlagOverride(SystemUiSystemPropertiesFlags.Flag flag, boolean isEnabled) {
+        mOverrides.put(flag, isEnabled);
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index 1306d57..dd6923d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -36,8 +36,6 @@
     int countLogSmartSuggestionsVisible = 0;
     Set<Integer> mChannelToastsSent = new HashSet<>();
 
-    public boolean ONGOING_DISMISSAL = false;
-
     String stringArrayResourceValue;
     @Nullable
     NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
@@ -162,11 +160,6 @@
         }
     }
 
-    // Mock SystemProperties
-    protected void setOngoingDismissal(boolean ongoingDismissal) {
-        ONGOING_DISMISSAL = ongoingDismissal;
-    }
-
     protected void setSystemExemptFromDismissal(boolean isOn) {
         mSystemExemptFromDismissal = isOn;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
index 4954e89..c2b3783 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java
@@ -147,6 +147,20 @@
     }
 
     @Test
+    public void testOpenedCameraInSplitScreen_orientationNotFixed_doNotShowToast() {
+        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+        spyOn(mTask);
+        spyOn(mDisplayRotationCompatPolicy);
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
+        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mTask).getWindowingMode();
+
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_in_split_screen);
+    }
+
+    @Test
     public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
         when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
                     /* checkDeviceConfig */ anyBoolean()))
@@ -172,7 +186,7 @@
     @Test
     public void testOnScreenRotationAnimationFinished_notFullscreen_doNotShowToast() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
-        doReturn(WINDOWING_MODE_MULTI_WINDOW).when(mActivity).getWindowingMode();
+        doReturn(true).when(mActivity).inMultiWindowMode();
         spyOn(mDisplayRotationCompatPolicy);
 
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
@@ -184,6 +198,18 @@
     }
 
     @Test
+    public void testOnScreenRotationAnimationFinished_orientationNotFixed_doNotShowToast() {
+        configureActivity(SCREEN_ORIENTATION_UNSPECIFIED);
+        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
+        spyOn(mDisplayRotationCompatPolicy);
+
+        mDisplayRotationCompatPolicy.onScreenRotationAnimationFinished();
+
+        verify(mDisplayRotationCompatPolicy, never()).showToast(
+                R.string.display_rotation_camera_compat_toast_after_rotation);
+    }
+
+    @Test
     public void testOnScreenRotationAnimationFinished_showToast() {
         configureActivity(SCREEN_ORIENTATION_PORTRAIT);
         mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index fe7e04d..74f492f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -260,6 +260,22 @@
     }
 
     @Test
+    public void testCheckOpaqueIsLetterboxedWhenStrategyIsApplied() {
+        mWm.mLetterboxConfiguration.setTranslucentLetterboxingOverrideEnabled(true);
+        setUpDisplaySizeWithApp(2000, 1000);
+        prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+        mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        // Translucent Activity
+        final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
+                .setLaunchedFromUid(mActivity.getUid())
+                .build();
+        doReturn(false).when(translucentActivity).fillsParent();
+        spyOn(mActivity);
+        mTask.addChild(translucentActivity);
+        verify(mActivity).isFinishing();
+    }
+
+    @Test
     public void testRestartProcessIfVisible() {
         setUpDisplaySizeWithApp(1000, 2500);
         doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
new file mode 100644
index 0000000..a27a5fd
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlTests.java
@@ -0,0 +1,116 @@
+/*
+ * 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.server.wm;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.os.Parcel;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Class for testing {@link SurfaceControl}.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:SurfaceControlTests
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class SurfaceControlTests {
+
+    @SmallTest
+    @Test
+    public void testUseValidSurface() {
+        SurfaceControl sc = buildTestSurface();
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.setVisibility(sc, false);
+        sc.release();
+    }
+
+    @SmallTest
+    @Test
+    public void testUseInvalidSurface() {
+        SurfaceControl sc = buildTestSurface();
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        sc.release();
+        try {
+            t.setVisibility(sc, false);
+            fail("Expected exception from updating invalid surface");
+        } catch (Exception e) {
+            // Expected exception
+        }
+    }
+
+    @SmallTest
+    @Test
+    public void testUseInvalidSurface_debugEnabled() {
+        SurfaceControl sc = buildTestSurface();
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        try {
+            SurfaceControl.setDebugUsageAfterRelease(true);
+            sc.release();
+            try {
+                t.setVisibility(sc, false);
+                fail("Expected exception from updating invalid surface");
+            } catch (IllegalStateException ise) {
+                assertNotNull(ise.getCause());
+            } catch (Exception e) {
+                fail("Expected IllegalStateException with cause");
+            }
+        } finally {
+            SurfaceControl.setDebugUsageAfterRelease(false);
+        }
+    }
+
+    @SmallTest
+    @Test
+    public void testWriteInvalidSurface_debugEnabled() {
+        SurfaceControl sc = buildTestSurface();
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        Parcel p = Parcel.obtain();
+        try {
+            SurfaceControl.setDebugUsageAfterRelease(true);
+            sc.release();
+            try {
+                sc.writeToParcel(p, 0 /* flags */);
+                fail("Expected exception from writing invalid surface to parcel");
+            } catch (IllegalStateException ise) {
+                assertNotNull(ise.getCause());
+            } catch (Exception e) {
+                fail("Expected IllegalStateException with cause");
+            }
+        } finally {
+            SurfaceControl.setDebugUsageAfterRelease(false);
+            p.recycle();
+        }
+    }
+
+    private SurfaceControl buildTestSurface() {
+        return new SurfaceControl.Builder()
+                .setContainerLayer()
+                .setName("SurfaceControlTests")
+                .setCallsite("SurfaceControlTests")
+                .build();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 12e5ed9..77b2638 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1627,6 +1627,8 @@
                     int status, int mRequest, long mFunctions, boolean mChargingFunctions);
 
         public abstract void getUsbSpeedCb(int speed);
+
+        public abstract void resetCb(int status);
     }
 
     private static final class UsbHandlerLegacy extends UsbHandler {
@@ -1988,14 +1990,30 @@
             return true;
         }
 
+        /**
+         * This callback function is only applicable for USB Gadget HAL,
+         * USBHandlerLegacy does not supported it.
+         */
         @Override
         public void setCurrentUsbFunctionsCb(long functions,
                     int status, int mRequest, long mFunctions, boolean mChargingFunctions){
         }
 
+        /**
+         * This callback function is only applicable for USB Gadget HAL,
+         * USBHandlerLegacy does not supported it.
+         */
         @Override
         public void getUsbSpeedCb(int speed){
         }
+
+        /**
+         * This callback function is only applicable for USB Gadget HAL,
+         * USBHandlerLegacy does not supported it.
+         */
+        @Override
+        public void resetCb(int status){
+        }
     }
 
     private static final class UsbHandlerHal extends UsbHandler {
@@ -2147,6 +2165,7 @@
                     }
                     break;
                 case MSG_RESET_USB_GADGET:
+                    operationId = sUsbOperationCount.incrementAndGet();
                     synchronized (mGadgetProxyLock) {
                         if (mUsbGadgetHal == null) {
                             Slog.e(TAG, "reset Usb Gadget mUsbGadgetHal is null");
@@ -2160,7 +2179,7 @@
                             if (mConfigured) {
                                 mResetUsbGadgetDisableDebounce = true;
                             }
-                            mUsbGadgetHal.reset();
+                            mUsbGadgetHal.reset(operationId);
                         } catch (Exception e) {
                             Slog.e(TAG, "reset Usb Gadget failed", e);
                             mResetUsbGadgetDisableDebounce = false;
@@ -2222,6 +2241,12 @@
             mUsbSpeed = speed;
         }
 
+        @Override
+        public void resetCb(int status) {
+            if (status != Status.SUCCESS)
+                Slog.e(TAG, "resetCb fail");
+        }
+
         private void setUsbConfig(long config, boolean chargingFunctions, int operationId) {
             if (true) Slog.d(TAG, "setUsbConfig(" + config + ") request:" + ++mCurrentRequest);
             /**
@@ -2363,6 +2388,10 @@
         mHandler.getUsbSpeedCb(speed);
     }
 
+    public void resetCb(int status) {
+        mHandler.resetCb(status);
+    }
+
     /**
      * Returns a dup of the control file descriptor for the given function.
      */
diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java
index bdfe60a..d47ccc7 100644
--- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java
+++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetAidl.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.hardware.usb.gadget.IUsbGadget;
 import android.hardware.usb.gadget.IUsbGadgetCallback;
+import android.hardware.usb.Status;
 import android.hardware.usb.UsbManager.UsbGadgetHalVersion;
 import android.os.ServiceManager;
 import android.os.IBinder;
@@ -139,14 +140,15 @@
     }
 
     @Override
-    public void reset() {
+    public void reset(long operationId) {
         try {
             synchronized (mGadgetProxyLock) {
-                mGadgetProxy.reset();
+                mGadgetProxy.reset(new UsbGadgetCallback(), operationId);
             }
         } catch (RemoteException e) {
             logAndPrintException(mPw,
-                    "RemoteException while calling getUsbSpeed", e);
+                    "RemoteException while calling getUsbSpeed"
+                    + ", opID:" + operationId, e);
             return;
         }
     }
@@ -155,7 +157,7 @@
     public void setCurrentUsbFunctions(int mRequest, long mFunctions,
             boolean mChargingFunctions, int timeout, long operationId) {
         try {
-            mUsbGadgetCallback = new UsbGadgetCallback(mRequest,
+            mUsbGadgetCallback = new UsbGadgetCallback(null, mRequest,
                                       mFunctions, mChargingFunctions);
             synchronized (mGadgetProxyLock) {
                 mGadgetProxy.setCurrentUsbFunctions(mFunctions, mUsbGadgetCallback,
@@ -174,6 +176,7 @@
     }
 
     private class UsbGadgetCallback extends IUsbGadgetCallback.Stub {
+        public IndentingPrintWriter mPw;
         public int mRequest;
         public long mFunctions;
         public boolean mChargingFunctions;
@@ -181,8 +184,9 @@
         UsbGadgetCallback() {
         }
 
-        UsbGadgetCallback(int request, long functions,
+        UsbGadgetCallback(IndentingPrintWriter pw, int request, long functions,
                 boolean chargingFunctions) {
+            mPw = pw;
             mRequest = request;
             mFunctions = functions;
             mChargingFunctions = chargingFunctions;
@@ -191,6 +195,15 @@
         @Override
         public void setCurrentUsbFunctionsCb(long functions,
                 int status, long transactionId) {
+            if (status == Status.SUCCESS) {
+                logAndPrint(Log.INFO, mPw, "Usb setCurrentUsbFunctionsCb"
+                + " ,functions:" + functions + " ,status:" + status
+                + " ,transactionId:" + transactionId);
+            } else {
+                logAndPrint(Log.ERROR, mPw, "Usb setCurrentUsbFunctionsCb failed"
+                + " ,functions:" + functions + " ,status:" + status
+                + " ,transactionId:" + transactionId);
+            }
             mDeviceManager.setCurrentUsbFunctionsCb(functions, status,
                     mRequest, mFunctions, mChargingFunctions);
         }
@@ -198,15 +211,38 @@
         @Override
         public void getCurrentUsbFunctionsCb(long functions,
                 int status, long transactionId) {
+            if (status == Status.SUCCESS) {
+                logAndPrint(Log.INFO, mPw, "Usb getCurrentUsbFunctionsCb"
+                + " ,functions:" + functions + " ,status:" + status
+                + " ,transactionId:" + transactionId);
+            } else {
+                logAndPrint(Log.ERROR, mPw, "Usb getCurrentUsbFunctionsCb failed"
+                + " ,functions:" + functions + " ,status:" + status
+                + " ,transactionId:" + transactionId);
+            }
             mDeviceManager.getCurrentUsbFunctionsCb(functions, status);
         }
 
         @Override
         public void getUsbSpeedCb(int speed, long transactionId) {
+            logAndPrint(Log.INFO, mPw, "getUsbSpeedCb speed:"
+                + speed + " ,transactionId:" + transactionId);
             mDeviceManager.getUsbSpeedCb(speed);
         }
 
         @Override
+        public void resetCb(int status, long transactionId) {
+            if (status == Status.SUCCESS) {
+                logAndPrint(Log.INFO, mPw, "Usb resetCb status:"
+                + status + " ,transactionId:" + transactionId);
+            } else {
+                logAndPrint(Log.ERROR, mPw, "Usb resetCb status"
+                + status + " ,transactionId:" + transactionId);
+            }
+            mDeviceManager.resetCb(status);
+        }
+
+        @Override
         public String getInterfaceHash() {
             return IUsbGadgetCallback.HASH;
         }
diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java
index 267247b..7b52f46 100644
--- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java
+++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHal.java
@@ -112,8 +112,11 @@
      * This function is used to reset USB gadget driver.
      * Performs USB data connection reset. The connection will disconnect and
      * reconnect.
+     *
+     * @param transactionId Used for tracking the current request and is passed down to the HAL
+     *                      implementation as needed.
      */
-    public void reset();
+    public void reset(long transactionId);
 
     /**
      * Invoked to query the version of current gadget hal implementation.
diff --git a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
index 3e5ecc5..2c5fcb8 100644
--- a/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
+++ b/services/usb/java/com/android/server/usb/hal/gadget/UsbGadgetHidl.java
@@ -188,7 +188,7 @@
     }
 
     @Override
-    public void reset() {
+    public void reset(long transactionId) {
         try {
             synchronized(mGadgetProxyLock) {
                 if (android.hardware.usb.gadget.V1_2.IUsbGadget.castFrom(mGadgetProxy) != null) {
@@ -199,7 +199,7 @@
             }
         } catch (RemoteException e) {
             logAndPrintException(mPw,
-                    "RemoteException while calling getUsbSpeed", e);
+                    "RemoteException while calling reset", e);
             return;
         }
     }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 0abed0b..717f4e7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -230,7 +230,7 @@
     class LocalService extends VoiceInteractionManagerInternal {
         @Override
         public void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
-                @Nullable String attributionTag, @NonNull Bundle options) {
+                @Nullable String attributionTag, @Nullable Bundle options) {
             if (DEBUG) {
                 Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity);
             }
@@ -426,7 +426,7 @@
 
         // TODO: VI Make sure the caller is the current user or profile
         void startLocalVoiceInteraction(@NonNull final IBinder token,
-                @Nullable String attributionTag, @NonNull Bundle options) {
+                @Nullable String attributionTag, @Nullable Bundle options) {
             if (mImpl == null) return;
 
             final int callingUid = Binder.getCallingUid();
@@ -945,7 +945,7 @@
         }
 
         @Override
-        public void showSession(@NonNull Bundle args, int flags, @Nullable String attributionTag) {
+        public void showSession(@Nullable Bundle args, int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 enforceIsCurrentVoiceInteractionService();
 
@@ -976,7 +976,7 @@
         }
 
         @Override
-        public boolean showSessionFromSession(@NonNull IBinder token, @NonNull Bundle sessionArgs,
+        public boolean showSessionFromSession(@NonNull IBinder token, @Nullable Bundle sessionArgs,
                 int flags, @Nullable String attributionTag) {
             synchronized (this) {
                 if (mImpl == null) {
@@ -1828,7 +1828,7 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
         @Override
-        public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+        public boolean showSessionForActiveService(@Nullable Bundle args, int sourceFlags,
                 @Nullable String attributionTag,
                 @Nullable IVoiceInteractionSessionShowCallback showCallback,
                 @Nullable IBinder activityToken) {
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index 061b71b..d676eee 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -110,8 +110,15 @@
             return;
         }
 
-        // Don't report if the server-side flag isn't loaded, as it implies other anomaly report
-        // related config hasn't loaded.
+        //always write atoms to statsd
+        TelephonyStatsLog.write(
+                TELEPHONY_ANOMALY_DETECTED,
+                carrierId,
+                eventId.getLeastSignificantBits(),
+                eventId.getMostSignificantBits());
+
+        // Don't report via Intent if the server-side flag isn't loaded, as it implies other anomaly
+        // report related config hasn't loaded.
         try {
             boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED,
@@ -122,12 +129,6 @@
             return;
         }
 
-        TelephonyStatsLog.write(
-                TELEPHONY_ANOMALY_DETECTED,
-                carrierId,
-                eventId.getLeastSignificantBits(),
-                eventId.getMostSignificantBits());
-
         // If this event has already occurred, skip sending intents for it; regardless log its
         // invocation here.
         Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
index b83b400..c4d760f 100644
--- a/telephony/java/android/telephony/DataFailCause.java
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -1012,6 +1012,8 @@
     public static final int IWLAN_DNS_RESOLUTION_NAME_FAILURE = 0x4004;
     /** No response received from the DNS Server due to a timeout*/
     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;
 
     // 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
@@ -1505,6 +1507,7 @@
         sFailCauseMap.put(IWLAN_IKEV2_CERT_INVALID, "IWLAN_IKEV2_CERT_INVALID");
         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(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/satellite/ISatellitePositionUpdateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
similarity index 82%
rename from telephony/java/android/telephony/satellite/ISatellitePositionUpdateCallback.aidl
rename to telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
index 5c3fa32..6fb0979 100644
--- a/telephony/java/android/telephony/satellite/ISatellitePositionUpdateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
@@ -19,10 +19,11 @@
 import android.telephony.satellite.PointingInfo;
 
 /**
- * Callback for position updates from the satellite service.
+ * Interface for satellite state listener.
  * @hide
  */
-oneway interface ISatellitePositionUpdateCallback {
+oneway interface ISatelliteStateListener {
+    void onSatelliteProvisionStateChanged(in int[] features, in boolean provisioned);
     void onSatellitePositionUpdate(in PointingInfo pointingInfo);
     void onMessageTransferStateUpdate(in int state);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteCallback.java b/telephony/java/android/telephony/satellite/SatelliteCallback.java
new file mode 100644
index 0000000..1b82d06
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteCallback.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.telephony.satellite.stub.SatelliteImplBase;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * A callback class for monitoring changes in specific satellite service states on the device,
+ * including provision state, position update, message transfer state and others.
+ * <p>
+ * To register a callback, use a {@link SatelliteCallback} which implements the interested
+ * interfaces. For example,
+ * FakeSatelliteProvisionStateCallback extends {@link SatelliteCallback} implements
+ * {@link SatelliteCallback.SatelliteProvisionStateListener}.
+ * <p>
+ * Then override the methods for the state that you wish to receive updates for, and
+ * pass your SatelliteCallback object to the corresponding register function like
+ * {@link SatelliteManager#registerForSatelliteProvisionStateChanged}.
+ * <p>
+ *
+ * @hide
+ */
+public class SatelliteCallback {
+    private ISatelliteStateListener mCallbackStub;
+
+    /**
+     * The SatelliteCallback needs an executor to execute the callback interfaces.
+     */
+    public void init(@NonNull Executor executor) {
+        if (executor == null) {
+            throw new IllegalArgumentException("SatelliteCallback executor must be non-null");
+        }
+        mCallbackStub = new ISatelliteStateListenerStub(this, executor);
+    }
+
+    public ISatelliteStateListener getCallbackStub() {
+        return mCallbackStub;
+    }
+
+    /**
+     * Interface for satellite provision state change listener.
+     */
+    public interface SatelliteProvisionStateListener {
+        /**
+         * Called when satellite provision state changes.
+         *
+         * @param features The list of provisioned features.
+         * @param provisioned The new provision state. {@code true} means satellite is provisioned
+         *                    {@code false} means satellite is not provisioned.
+         */
+        void onSatelliteProvisionStateChanged(
+                @SatelliteImplBase.Feature int[] features, boolean provisioned);
+    }
+
+    /**
+     * Interface for position update change listener.
+     */
+    public interface SatellitePositionUpdateListener {
+        /**
+         * Called when the satellite position changes.
+         *
+         * @param pointingInfo The pointing info containing the satellite location.
+         */
+        void onSatellitePositionUpdate(@NonNull PointingInfo pointingInfo);
+
+        /**
+         * Called when satellite message transfer state changes.
+         *
+         * @param state The new message transfer state.
+         */
+        void onMessageTransferStateUpdate(
+                @SatelliteManager.SatelliteMessageTransferState int state);
+    }
+
+    private static class ISatelliteStateListenerStub extends ISatelliteStateListener.Stub {
+        private WeakReference<SatelliteCallback> mSatelliteCallbackWeakRef;
+        private Executor mExecutor;
+
+        ISatelliteStateListenerStub(SatelliteCallback satelliteCallback, Executor executor) {
+            mSatelliteCallbackWeakRef = new WeakReference<>(satelliteCallback);
+            mExecutor = executor;
+        }
+
+        public void onSatelliteProvisionStateChanged(
+                @SatelliteImplBase.Feature int[] features, boolean provisioned) {
+            SatelliteProvisionStateListener listener =
+                    (SatelliteProvisionStateListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onSatelliteProvisionStateChanged(features, provisioned)));
+        }
+
+        public void onSatellitePositionUpdate(@NonNull PointingInfo pointingInfo) {
+            SatellitePositionUpdateListener listener =
+                    (SatellitePositionUpdateListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onSatellitePositionUpdate(pointingInfo)));
+        }
+
+        public void onMessageTransferStateUpdate(
+                @SatelliteManager.SatelliteMessageTransferState int state) {
+            SatellitePositionUpdateListener listener =
+                    (SatellitePositionUpdateListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onMessageTransferStateUpdate(state)));
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7a60c58..ea9bca3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -26,26 +26,29 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
 import android.os.RemoteException;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
-import android.util.ArrayMap;
+import android.telephony.satellite.stub.SatelliteImplBase;
 
+import com.android.internal.telephony.IIntArrayConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.ITelephony;
 import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 /**
  * Manages satellite operations such as provisioning, pointing, messaging, location sharing, etc.
- * To get the object, call {@link Context#getSystemService(Context.SATELLITE_SERVICE)}.
+ * To get the object, call {@link Context#getSystemService(String)}. This object is associated
+ * with the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} and the satellite service is
+ * associated with the device rather than a subscription.
  * To create an instance of {@link SatelliteManager} associated with a specific subscription ID,
  * call {@link #createForSubscriptionId(int)}.
  *
@@ -56,18 +59,11 @@
     private static final String TAG = "SatelliteManager";
 
     /**
-     * Map of all SatellitePositionUpdateCallback and their associated callback ids.
-     */
-    private final Map<SatellitePositionUpdateCallback, Integer> mSatellitePositionUpdateCallbacks =
-            new ArrayMap<>();
-
-    /**
-     * AtomicInteger for the id of the next SatellitePositionUpdateCallback.
-     */
-    private final AtomicInteger mSatellitePositionUpdateCallbackId = new AtomicInteger(0);
-
-    /**
-     * The subscription ID for this SatelliteManager.
+     * The subscription ID for this SatelliteManager. If the
+     * {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} is used, the satellite service will be
+     * associated with the device rather than a subscription. If an active subscription ID
+     * {@link SubscriptionManager#isActiveSubId(int)} is provided, the satellite service will be
+     * associated with that active subscription.
      */
     private final int mSubId;
 
@@ -82,7 +78,6 @@
      * @param context The context the SatelliteManager belongs to.
      */
     public SatelliteManager(@Nullable Context context) {
-        // TODO: replace DEFAULT_SUBSCRIPTION_ID with DEFAULT_SATELLITE_SUBSCRIPTION_ID
         this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
 
@@ -120,23 +115,24 @@
      */
     public static final int SATELLITE_SERVICE_SERVER_ERROR = 2;
     /**
-     * Unexpected telephony internal error.
+     * Telephony is not in a valid state to serve requests from clients.
      */
-    public static final int SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR = 3;
+    public static final int SATELLITE_SERVICE_INVALID_TELEPHONY_STATE = 3;
     /**
-     * Modem error received from the satellite service.
+     * RIL layer got an unexpected or incorrect response from modem for a request.
      */
-    public static final int SATELLITE_SERVICE_MODEM_ERROR = 4;
+    public static final int SATELLITE_SERVICE_UNEXPECTED_MODEM_RESPONSE = 4;
     /**
-     * System error received from the satellite service.
+     * An error in the RIL layer occurs when processing a request. This generic RIL error should be
+     * used only when other RIL specific errors cannot be used.
      */
-    public static final int SATELLITE_SERVICE_SYSTEM_ERROR = 5;
+    public static final int SATELLITE_SERVICE_RIL_ERROR = 5;
     /**
-     * Invalid arguments passed.
+     * RIL layer has received a request with invalid arguments from the Telephony framework.
      */
     public static final int SATELLITE_SERVICE_INVALID_ARGUMENTS = 6;
     /**
-     * Invalid modem state.
+     * Satellite modem is not in a valid state to serve requests from clients.
      */
     public static final int SATELLITE_SERVICE_INVALID_MODEM_STATE = 7;
     /**
@@ -144,67 +140,203 @@
      */
     public static final int SATELLITE_SERVICE_INVALID_SIM_STATE = 8;
     /**
-     * Invalid state.
+     * RIL layer is not in a valid state to serve requests from clients.
      */
-    public static final int SATELLITE_SERVICE_INVALID_STATE = 9;
+    public static final int SATELLITE_SERVICE_INVALID_RIL_STATE = 9;
     /**
-     * Satellite service is unavailable.
+     * Radio did not start or is resetting.
      */
-    public static final int SATELLITE_SERVICE_NOT_AVAILABLE = 10;
+    public static final int SATELLITE_SERVICE_RADIO_NOT_AVAILABLE = 10;
     /**
-     * Satellite service is not supported by the device or OS.
+     * The request is not supported by either the satellite modem or the network.
      */
-    public static final int SATELLITE_SERVICE_NOT_SUPPORTED = 11;
+    public static final int SATELLITE_SERVICE_REQUEST_NOT_SUPPORTED = 11;
     /**
-     * Satellite service is rate limited.
+     * Requests denied by the satellite modem or the network due to overly-frequent requests.
      */
-    public static final int SATELLITE_SERVICE_RATE_LIMITED = 12;
+    public static final int SATELLITE_SERVICE_REQUEST_RATE_LIMITED = 12;
     /**
-     * Satellite service has no memory available.
+     * Satellite modem or network has no resources available to handle requests from clients.
      */
-    public static final int SATELLITE_SERVICE_NO_MEMORY = 13;
+    public static final int SATELLITE_SERVICE_NO_RESOURCES = 13;
     /**
-     * Satellite service has no resources available.
+     * Telephony framework failed to send a request to the satellite service on the device or the
+     * satellite modem.
      */
-    public static final int SATELLITE_SERVICE_NO_RESOURCES = 14;
+    public static final int SATELLITE_SERVICE_REQUEST_FAILED = 14;
     /**
-     * Failed to send a request to the satellite service.
+     * A generic error which should be used only when other specific errors cannot be used.
      */
-    public static final int SATELLITE_SERVICE_REQUEST_FAILED = 15;
+    public static final int SATELLITE_SERVICE_ERROR = 15;
     /**
-     * Failed to send a request to the satellite service for the given subscription ID.
+     * Satellite service is disabled on the requested subscription or the device.
      */
-    public static final int SATELLITE_SERVICE_INVALID_SUBSCRIPTION_ID = 16;
+    public static final int SATELLITE_SERVICE_DISABLED = 16;
     /**
-     * Error received from satellite service.
+     * Satellite is already provisioned for the subscription or the device.
      */
-    public static final int SATELLITE_SERVICE_ERROR = 17;
+    public static final int SATELLITE_SERVICE_ALREADY_PROVISIONED = 17;
     /**
-     * Satellite service is disabled on the requested subscription.
+     * Provisioning is already in progress.
      */
-    public static final int SATELLITE_SERVICE_DISABLED = 18;
+    public static final int SATELLITE_SERVICE_PROVISION_IN_PROGRESS = 18;
+    /**
+     * The ongoing request was aborted by either the satellite modem or the network.
+     */
+    public static final int SATELLITE_SERVICE_REQUEST_ABORTED = 19;
+    /**
+     * The device/subscription is barred to access the satellite service.
+     */
+    public static final int SATELLITE_SERVICE_ACCESS_BARRED = 20;
+    /**
+     * The requesting feature is not supported by the satellite service provider.
+     */
+    public static final int SATELLITE_SERVICE_FEATURE_NOT_SUPPORTED = 21;
+    /**
+     * The modem of the device is not compatible with the satellite service provider.
+     */
+    public static final int SATELLITE_SERVICE_MODEM_INCOMPATIBLE = 22;
+    /**
+     * The satellite network is not ready to serve requests from clients.
+     */
+    public static final int SATELLITE_SERVICE_NETWORK_NOT_READY = 23;
+    /**
+     * The satellite server is not ready to serve requests from clients.
+     */
+    public static final int SATELLITE_SERVICE_SERVER_NOT_READY = 24;
+    /**
+     * The request was rejected by the satellite server.
+     */
+    public static final int SATELLITE_SERVICE_SERVER_REJECT = 25;
+    /**
+     * Satellite modem timeout to receive ACK or response from the satellite network after
+     * sending a request to the network.
+     */
+    public static final int SATELLITE_SERVICE_NETWORK_TIMEOUT = 26;
+    /**
+     * Satellite modem cannot detect any satellite signal.
+     */
+    public static final int SATELLITE_SERVICE_NO_SATELLITE_SIGNAL = 27;
+    /**
+     * Device does not have a subscription.
+     */
+    public static final int SATELLITE_SERVICE_NO_SUBSCRIPTION = 28;
+    /**
+     * Operation is not allowed by either the satellite modem, or satellite network, or satellite
+     * server.
+     */
+    public static final int SATELLITE_SERVICE_OPERATION_NOT_ALLOWED = 29;
+    /**
+     * The radio technology is not supported by the satellite service provider.
+     */
+    public static final int SATELLITE_SERVICE_RADIO_TECHNOLOGY_NOT_SUPPORTED = 30;
+    /**
+     * SIM is absent.
+     */
+    public static final int SATELLITE_SERVICE_SIM_ABSENT = 31;
+    /**
+     * SIM is busy.
+     */
+    public static final int SATELLITE_SERVICE_SIM_BUSY = 32;
+    /**
+     * Received error from SIM card.
+     */
+    public static final int SATELLITE_SERVICE_SIM_ERR = 33;
+    /**
+     * The target EF is full.
+     */
+    public static final int SATELLITE_SERVICE_SIM_FULL = 34;
+    /**
+     * The subscription/user is not authorized to register with the satellite service provider.
+     */
+    public static final int SATELLITE_SERVICE_SUBSCRIBER_NOT_AUTHORIZED = 35;
+    /**
+     * The callback was already registered with Telephony framework.
+     */
+    public static final int SATELLITE_SERVICE_CALLBACK_ALREADY_REGISTERED = 36;
+    /**
+     * The callback was not registered with Telephony framework.
+     */
+    public static final int SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED = 37;
+    /**
+     * The request cannot be performed since the subscriber/user's account balance is not
+     * sufficient.
+     */
+    public static final int SATELLITE_SERVICE_NOT_SUFFICIENT_ACCOUNT_BALANCE = 38;
+    /**
+     * While processing a request from the Telephony framework, the satellite modem detects
+     * terrestrial signal, aborts the request, and switches to the terrestrial network.
+     */
+    public static final int SATELLITE_SERVICE_SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 39;
+    /**
+     * The subscriber/user is not registered with the service provider.
+     */
+    public static final int SATELLITE_SERVICE_UNIDENTIFIED_SUBSCRIBER = 40;
+    /**
+     * The contact to be added/removed is either not existing or not valid.
+     */
+    public static final int SATELLITE_SERVICE_INVALID_CONTACT = 41;
+    /**
+     * The encoding scheme is not supported by either the satellite provider or the device.
+     */
+    public static final int SATELLITE_SERVICE_ENCODING_NOT_SUPPORTED = 42;
+    /**
+     * Received error from the satellite network. This generic error code should be used only when
+     * the error cannot be mapped to other specific network error codes.
+     */
+    public static final int SATELLITE_SERVICE_NETWORK_ERROR = 43;
+    /**
+     * Modem hit unexpected error scenario while handling this request.
+     */
+    public static final int SATELLITE_SERVICE_MODEM_ERROR = 44;
 
     /** @hide */
     @IntDef(prefix = {"SATELLITE_SERVICE_"}, value = {
             SATELLITE_SERVICE_SUCCESS,
             SATELLITE_SERVICE_SERVER_NOT_REACHABLE,
             SATELLITE_SERVICE_SERVER_ERROR,
-            SATELLITE_SERVICE_TELEPHONY_INTERNAL_ERROR,
-            SATELLITE_SERVICE_MODEM_ERROR,
-            SATELLITE_SERVICE_SYSTEM_ERROR,
+            SATELLITE_SERVICE_INVALID_TELEPHONY_STATE,
+            SATELLITE_SERVICE_UNEXPECTED_MODEM_RESPONSE,
+            SATELLITE_SERVICE_RIL_ERROR,
             SATELLITE_SERVICE_INVALID_ARGUMENTS,
             SATELLITE_SERVICE_INVALID_MODEM_STATE,
             SATELLITE_SERVICE_INVALID_SIM_STATE,
-            SATELLITE_SERVICE_INVALID_STATE,
-            SATELLITE_SERVICE_NOT_AVAILABLE,
-            SATELLITE_SERVICE_NOT_SUPPORTED,
-            SATELLITE_SERVICE_RATE_LIMITED,
-            SATELLITE_SERVICE_NO_MEMORY,
+            SATELLITE_SERVICE_INVALID_RIL_STATE,
+            SATELLITE_SERVICE_RADIO_NOT_AVAILABLE,
+            SATELLITE_SERVICE_REQUEST_NOT_SUPPORTED,
+            SATELLITE_SERVICE_REQUEST_RATE_LIMITED,
             SATELLITE_SERVICE_NO_RESOURCES,
             SATELLITE_SERVICE_REQUEST_FAILED,
-            SATELLITE_SERVICE_INVALID_SUBSCRIPTION_ID,
             SATELLITE_SERVICE_ERROR,
-            SATELLITE_SERVICE_DISABLED
+            SATELLITE_SERVICE_DISABLED,
+            SATELLITE_SERVICE_ALREADY_PROVISIONED,
+            SATELLITE_SERVICE_PROVISION_IN_PROGRESS,
+            SATELLITE_SERVICE_REQUEST_ABORTED,
+            SATELLITE_SERVICE_ACCESS_BARRED,
+            SATELLITE_SERVICE_FEATURE_NOT_SUPPORTED,
+            SATELLITE_SERVICE_MODEM_INCOMPATIBLE,
+            SATELLITE_SERVICE_NETWORK_NOT_READY,
+            SATELLITE_SERVICE_SERVER_NOT_READY,
+            SATELLITE_SERVICE_SERVER_REJECT,
+            SATELLITE_SERVICE_NETWORK_TIMEOUT,
+            SATELLITE_SERVICE_NO_SATELLITE_SIGNAL,
+            SATELLITE_SERVICE_NO_SUBSCRIPTION,
+            SATELLITE_SERVICE_OPERATION_NOT_ALLOWED,
+            SATELLITE_SERVICE_RADIO_TECHNOLOGY_NOT_SUPPORTED,
+            SATELLITE_SERVICE_SIM_ABSENT,
+            SATELLITE_SERVICE_SIM_BUSY,
+            SATELLITE_SERVICE_SIM_ERR,
+            SATELLITE_SERVICE_SIM_FULL,
+            SATELLITE_SERVICE_SUBSCRIBER_NOT_AUTHORIZED,
+            SATELLITE_SERVICE_CALLBACK_ALREADY_REGISTERED,
+            SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED,
+            SATELLITE_SERVICE_NOT_SUFFICIENT_ACCOUNT_BALANCE,
+            SATELLITE_SERVICE_SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL,
+            SATELLITE_SERVICE_UNIDENTIFIED_SUBSCRIBER,
+            SATELLITE_SERVICE_INVALID_CONTACT,
+            SATELLITE_SERVICE_ENCODING_NOT_SUPPORTED,
+            SATELLITE_SERVICE_NETWORK_ERROR,
+            SATELLITE_SERVICE_MODEM_ERROR
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SatelliteServiceResult {}
@@ -241,73 +373,29 @@
     public @interface SatelliteMessageTransferState {}
 
     /**
-     * Callback for position updates from the satellite service.
-     */
-    public interface SatellitePositionUpdateCallback {
-        /**
-         * Called when the satellite position changes.
-         *
-         * @param pointingInfo The pointing info containing the satellite location.
-         */
-        void onSatellitePositionUpdate(@NonNull PointingInfo pointingInfo);
-
-        /**
-         * Called when satellite message transfer state changes.
-         *
-         * @param state The new message transfer state.
-         */
-        void onMessageTransferStateUpdate(@SatelliteMessageTransferState int state);
-    }
-
-    /**
      * Start receiving satellite position updates.
      * This can be called by the pointing UI when the user starts pointing to the satellite.
      * Modem should continue to report the pointing input as the device or satellite moves.
      * Satellite position updates are started only on {@link #SATELLITE_SERVICE_SUCCESS}.
      * All other results indicate that this operation failed.
      *
-     * @param executor The executor to run callbacks on.
-     * @param callback The callback to notify of changes in satellite position.
+     * @param executor - The executor on which the callback will be called.
+     * @param callback The callback to notify of changes in satellite position. This
+     *                 SatelliteCallback should implement the interface
+     *                 {@link SatelliteCallback.SatellitePositionUpdateListener}.
      * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @SatelliteServiceResult public int startSatellitePositionUpdates(
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull SatellitePositionUpdateCallback callback) {
+            @NonNull Executor executor, @NonNull SatelliteCallback callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int id;
-                if (mSatellitePositionUpdateCallbacks.containsKey(callback)) {
-                    id = mSatellitePositionUpdateCallbacks.get(callback);
-                } else {
-                    id = mSatellitePositionUpdateCallbackId.getAndIncrement();
-                }
-                int result = telephony.startSatellitePositionUpdates(mSubId, id,
-                        new ISatellitePositionUpdateCallback.Stub() {
-                            @Override
-                            public void onSatellitePositionUpdate(
-                                    @NonNull PointingInfo pointingInfo) {
-                                logd("onSatellitePositionUpdate: pointingInfo=" + pointingInfo);
-                                executor.execute(() -> Binder.withCleanCallingIdentity(
-                                        () -> callback.onSatellitePositionUpdate(pointingInfo)));
-                            }
-
-                            @Override
-                            public void onMessageTransferStateUpdate(
-                                    @SatelliteMessageTransferState int state) {
-                                logd("onMessageTransferStateUpdate: state=" + state);
-                                executor.execute(() -> Binder.withCleanCallingIdentity(
-                                        () -> callback.onMessageTransferStateUpdate(state)));
-                            }
-                        });
-                if (result == SATELLITE_SERVICE_SUCCESS) {
-                    mSatellitePositionUpdateCallbacks.put(callback, id);
-                }
-                return result;
+                callback.init(executor);
+                return telephony.startSatellitePositionUpdates(mSubId, callback.getCallbackStub());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -325,30 +413,20 @@
      * All other results indicate that this operation failed.
      *
      * @param callback The callback that was passed in {@link
-     *                 #startSatellitePositionUpdates(Executor, SatellitePositionUpdateCallback)}.
+     *                 #startSatellitePositionUpdates(Executor, SatelliteCallback)}.
      * @return The result of the operation.
      * @throws IllegalArgumentException if the callback is invalid.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @SatelliteServiceResult public int stopSatellitePositionUpdates(
-            @NonNull SatellitePositionUpdateCallback callback) {
+            @NonNull SatelliteCallback callback) {
         Objects.requireNonNull(callback);
 
-        if (!mSatellitePositionUpdateCallbacks.containsKey(callback)) {
-            throw new IllegalArgumentException(
-                    "startSatellitePositionUpdates was never called with the callback provided.");
-        }
-
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                int result = telephony.stopSatellitePositionUpdates(mSubId,
-                        mSatellitePositionUpdateCallbacks.get(callback));
-                if (result == SATELLITE_SERVICE_SUCCESS) {
-                    mSatellitePositionUpdateCallbacks.remove(callback);
-                    // TODO: Notify SmsHandler that pointing UI stopped
-                }
-                return result;
+                return telephony.stopSatellitePositionUpdates(mSubId, callback.getCallbackStub());
+                // TODO: Notify SmsHandler that pointing UI stopped
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -399,6 +477,164 @@
         return SATELLITE_SERVICE_REQUEST_FAILED;
     }
 
+
+    /**
+     * Register the subscription with a satellite provider. This is needed if the provider allows
+     * dynamic registration.
+     *
+     * @param features List of features to be provisioned.
+     * @param executor The optional executor to run callbacks on.
+     * @param callback The optional callback to get the error code of the request.
+     * @param cancellationSignal The optional signal used by the caller to cancel the provision
+     *                           request. Even when the cancellation is signaled, Telephony will
+     *                           still trigger the callback to return the result of this request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void provisionSatelliteService(
+            @NonNull @SatelliteImplBase.Feature int[] features,
+            @Nullable @CallbackExecutor Executor executor,
+            @SatelliteServiceResult @Nullable Consumer<Integer> callback,
+            @Nullable CancellationSignal cancellationSignal) {
+        Objects.requireNonNull(features);
+
+        ICancellationSignal cancelRemote = null;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer callbackStub = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        if (executor == null || callback == null) {
+                            logd("provisionSatelliteService: executor and/or callback is null");
+                            return;
+                        }
+                        Binder.withCleanCallingIdentity(() -> {
+                            executor.execute(() -> callback.accept(result));
+                        });
+                    }
+                };
+                cancelRemote = telephony.provisionSatelliteService(mSubId, features, callbackStub);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("provisionSatelliteService RemoteException=" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        if (cancellationSignal != null) {
+            cancellationSignal.setRemote(cancelRemote);
+        }
+    }
+
+    /**
+     * Register for the satellite provision state change.
+     *
+     * @param executor - The executor on which the callback will be called.
+     * @param callback The callback to handle the satellite provision state changed event. This
+     *                 SatelliteCallback should implement the interface
+     *                 {@link SatelliteCallback.SatelliteProvisionStateListener}.
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteServiceResult
+    public int registerForSatelliteProvisionStateChanged(
+            @NonNull Executor executor, @NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                callback.init(executor);
+                return telephony.registerForSatelliteProvisionStateChanged(
+                        mSubId, callback.getCallbackStub());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForSatelliteProvisionStateChanged RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_SERVICE_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregister for the satellite provision state change.
+     *
+     * @param callback The callback that was passed to
+     * {@link #registerForSatelliteProvisionStateChanged(Executor, SatelliteCallback)}
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteServiceResult
+    public int unregisterForSatelliteProvisionStateChanged(@NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(callback);
+
+        if (callback.getCallbackStub() == null) {
+            loge("unregisterForSatelliteProvisionStateChanged: callbackStub is null");
+            return SATELLITE_SERVICE_CALLBACK_NOT_REGISTERED;
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.unregisterForSatelliteProvisionStateChanged(
+                        mSubId, callback.getCallbackStub());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForSatelliteProvisionStateChanged RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_SERVICE_REQUEST_FAILED;
+    }
+
+    /**
+     * Get the list of provisioned satellite features.
+     *
+     * @param executor The executor to run callbacks on.
+     * @param resultListener The callback to get the list of provisioned features when the request
+     *                       returns success result.
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteServiceResult
+    public int getProvisionedSatelliteFeatures(
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<int[]> resultListener) {
+        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(executor);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntArrayConsumer callbackStub = new IIntArrayConsumer.Stub() {
+                    @Override
+                    public void accept(int[] result) {
+                        Binder.withCleanCallingIdentity(() -> {
+                            executor.execute(() -> resultListener.accept(result));
+                        });
+                    }
+                };
+                return telephony.getProvisionedSatelliteFeatures(mSubId, callbackStub);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("getProvisionedSatelliteFeatures() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_SERVICE_REQUEST_FAILED;
+    }
+
     private static ITelephony getITelephony() {
         ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
                 .getTelephonyServiceManager()
diff --git a/telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl b/telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl
new file mode 100644
index 0000000..c208755
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.internal.telephony;
+
+// Copies consumer pattern for an operation that requires an int array result from another
+// process to finish.
+oneway interface IIntArrayConsumer {
+    void accept(in int[] result);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5486365..24483a8 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -21,6 +21,7 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.os.Bundle;
+import android.os.ICancellationSignal;
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
@@ -66,13 +67,14 @@
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsConfigCallback;
-import android.telephony.satellite.ISatellitePositionUpdateCallback;
+import android.telephony.satellite.ISatelliteStateListener;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IBooleanConsumer;
 import com.android.internal.telephony.ICallForwardingInfoCallback;
 import com.android.internal.telephony.IccLogicalChannelRequest;
 import com.android.internal.telephony.IImsStateCallback;
+import com.android.internal.telephony.IIntArrayConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.OperatorInfo;
@@ -2702,16 +2704,52 @@
     /**
      * Start receiving satellite pointing updates.
      */
-    int startSatellitePositionUpdates(int subId, int callbackId,
-            in ISatellitePositionUpdateCallback callback);
+    int startSatellitePositionUpdates(int subId, in ISatelliteStateListener callback);
 
     /**
      * Stop receiving satellite pointing updates.
      */
-    int stopSatellitePositionUpdates(int subId, int callbackId);
+    int stopSatellitePositionUpdates(int subId, ISatelliteStateListener callback);
 
     /**
      * Get maximum number of characters per text message on satellite.
      */
     int getMaxCharactersPerSatelliteTextMessage(int subId, IIntegerConsumer internalCallback);
-}
\ No newline at end of file
+
+    /**
+     * Register the subscription with a satellite provider.
+     * This is needed to register the subscription if the provider allows dynamic registration.
+     *
+     * @param subId The subId of the subscription to be provisioned.
+     * @param features List of features to be provisioned.
+     * @param callback The callback to get the error code of the request.
+     * @return The signal transport used by callers to cancel the provision request.
+     */
+    ICancellationSignal provisionSatelliteService(int subId, in int[] features,
+            in IIntegerConsumer callback);
+
+    /**
+     * Register for the satellite provision state change.
+     *
+     * @param subId The subId of the subscription to be provisioned.
+     * @param callback The callback to handle the satellite provision state changed event.
+     */
+    int registerForSatelliteProvisionStateChanged(int subId, ISatelliteStateListener callback);
+
+    /**
+     * Unregister for the satellite provision state change.
+     *
+     * @param subId The subId of the subscription associated with the satellite service.
+     * @param callback The callback that was passed to
+     *                   registerForSatelliteProvisionStateChanged.
+     */
+    int unregisterForSatelliteProvisionStateChanged(int subId, ISatelliteStateListener callback);
+
+    /**
+     * Get the list of provisioned satellite features.
+     *
+     * @param subId The subId of the subscription to be provisioned.
+     * @param callback The callback to get the list of provisioned satellite features.
+     */
+    int getProvisionedSatelliteFeatures(int subId, IIntArrayConsumer callback);
+}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 6e56963..45daab3 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -120,6 +120,31 @@
     int BLOCKED_DUE_TO_CALL = 69;                   /* SMS is blocked due to call control */
     int RF_HARDWARE_ISSUE = 70;                     /* RF HW issue is detected */
     int NO_RF_CALIBRATION_INFO = 71;                /* No RF calibration in device */
+    int ENCODING_NOT_SUPPORTED = 72;                /* The encoding scheme is not supported by
+                                                       either the network or the MS. */
+    int FEATURE_NOT_SUPPORTED = 73;                 /* The requesting feature is not supported by
+                                                       the service provider. */
+    int INVALID_CONTACT = 74;                       /* The contact to be added is either not
+                                                       existing or not valid. */
+    int MODEM_INCOMPATIBLE = 75;                    /* The modem of the MS is not compatible with
+                                                       the service provider. */
+    int NETWORK_TIMEOUT = 76;                       /* Modem timeout to receive ACK or response from
+                                                       network after sending a request to it. */
+    int NO_SATELLITE_SIGNAL = 77;                   /* Modem fails to communicate with the satellite
+                                                       network since there is no satellite signal.*/
+    int NOT_SUFFICIENT_ACCOUNT_BALANCE = 78;        /* The request cannot be performed since the
+                                                       subscriber's account balance is not
+                                                       sufficient. */
+    int RADIO_TECHNOLOGY_NOT_SUPPORTED = 79;        /* The radio technology is not supported by the
+                                                       service provider. */
+    int SUBSCRIBER_NOT_AUTHORIZED = 80;             /* The subscription is not authorized to
+                                                       register with the service provider. */
+    int SWITCHED_FROM_SATELLITE_TO_TERRESTRIAL = 81; /* While processing a request from the
+                                                       Framework the satellite modem detects
+                                                       terrestrial signal, aborts the request, and
+                                                       switches to the terrestrial network. */
+    int UNIDENTIFIED_SUBSCRIBER = 82;               /* The subscriber is not registered with the
+                                                       service provider */
 
     // Below is list of OEM specific error codes which can by used by OEMs in case they don't want to
     // reveal particular replacement for Generic failure
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
index 413f92c..ae3dcb8 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshActivity.java
@@ -32,7 +32,6 @@
 
 import java.nio.FloatBuffer;
 import java.nio.ShortBuffer;
-import java.util.ArrayList;
 
 public class MeshActivity extends Activity {
     @Override
@@ -115,9 +114,11 @@
                     + "      color = vec4(1.0, 0.0, 0.0, 1.0);"
                     + "      return varyings.position;\n"
                     + "}";
-            ArrayList<Attribute> attList = new ArrayList<>();
-            attList.add(new Attribute(MeshSpecification.FLOAT2, 0, "position"));
-            ArrayList<Varying> varyList = new ArrayList<>();
+            Attribute[] attList = new Attribute[]{
+                    new Attribute(MeshSpecification.TYPE_FLOAT2, 0, "position"),
+
+            };
+            Varying[] varyList = new Varying[0];
             return MeshSpecification.make(attList, 8, varyList, vs, fs);
         }
     }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
index e62db6b..01ca2fc 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MeshLargeActivity.java
@@ -32,7 +32,6 @@
 
 import java.nio.FloatBuffer;
 import java.nio.ShortBuffer;
-import java.util.ArrayList;
 
 public class MeshLargeActivity extends Activity {
     @Override
@@ -131,44 +130,37 @@
                     + "      color = vec4(1.0, 0.0, 0.0, 1.0);"
                     + "      return varyings.position;\n"
                     + "}";
-            ArrayList<Attribute> attList = new ArrayList<>();
-            attList.add(new Attribute(MeshSpecification.FLOAT2, 0, "position"));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    8,
-                    "test"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    24,
-                    "test2"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    40,
-                    "test3"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    56,
-                    "test4"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    72,
-                    "test5"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    88,
-                    "test6"
-            ));
-            attList.add(new Attribute(
-                    MeshSpecification.FLOAT4,
-                    104,
-                    "test7"
-            ));
-            ArrayList<Varying> varyList = new ArrayList<>();
+            Attribute[] attList = new Attribute[]{
+                    new Attribute(MeshSpecification.TYPE_FLOAT2, 0, "position"),
+                    new Attribute(MeshSpecification.TYPE_FLOAT4, 8, "test"),
+                    new Attribute(MeshSpecification.TYPE_FLOAT4, 24, "test2"),
+                    new Attribute(
+                            MeshSpecification.TYPE_FLOAT4,
+                            40,
+                            "test3"
+                    ),
+                    new Attribute(
+                            MeshSpecification.TYPE_FLOAT4,
+                            56,
+                            "test4"
+                    ),
+                    new Attribute(
+                            MeshSpecification.TYPE_FLOAT4,
+                            72,
+                            "test5"
+                    ),
+                    new Attribute(
+                            MeshSpecification.TYPE_FLOAT4,
+                            88,
+                            "test6"
+                    ),
+                    new Attribute(
+                            MeshSpecification.TYPE_FLOAT4,
+                            104,
+                            "test7"
+                    )
+            };
+            Varying[] varyList = new Varying[0];
             return MeshSpecification.make(attList, 120, varyList, vs, fs);
         }
     }
diff --git a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
index 4103ca7..210e3ea 100644
--- a/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
+++ b/tests/UsbTests/src/com/android/server/usb/UsbHandlerTest.java
@@ -148,6 +148,10 @@
         public void getUsbSpeedCb(int speed){
         }
 
+        @Override
+        public void resetCb(int status){
+        }
+
     }
 
     @Before