Merge "Do not intercept LANGUAGE_SWITCH in the framework layer"
diff --git a/core/api/current.txt b/core/api/current.txt
index 74ff672..c70fa37 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -50935,6 +50935,7 @@
     method public int getScaledDoubleTapSlop();
     method public int getScaledEdgeSlop();
     method public int getScaledFadingEdgeLength();
+    method public int getScaledHandwritingGestureLineMargin();
     method public int getScaledHandwritingSlop();
     method public float getScaledHorizontalScrollFactor();
     method public int getScaledHoverSlop();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 7c1c8ba..c170f74 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -401,6 +401,7 @@
 
   public static final class R.dimen {
     field public static final int config_restrictedIconSize = 17104903; // 0x1050007
+    field public static final int config_viewConfigurationHandwritingGestureLineMargin;
   }
 
   public static final class R.drawable {
@@ -7076,6 +7077,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER) public static android.media.tv.tuner.filter.SharedFilter openSharedFilter(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.filter.SharedFilterCallback);
     method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
     method public int removeOutputPid(@IntRange(from=0) int);
+    method public int requestFrontendById(int);
     method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
     method public int setLnaEnabled(boolean);
     method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
@@ -11979,7 +11981,7 @@
   public final class HotwordAudioStream implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.media.AudioFormat getAudioFormat();
-    method @NonNull public android.os.ParcelFileDescriptor getAudioStream();
+    method @NonNull public android.os.ParcelFileDescriptor getAudioStreamParcelFileDescriptor();
     method @NonNull public android.os.PersistableBundle getMetadata();
     method @Nullable public android.media.AudioTimestamp getTimestamp();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -11990,7 +11992,7 @@
     ctor public HotwordAudioStream.Builder(@NonNull android.media.AudioFormat, @NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.service.voice.HotwordAudioStream build();
     method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioFormat(@NonNull android.media.AudioFormat);
-    method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStream(@NonNull android.os.ParcelFileDescriptor);
+    method @NonNull public android.service.voice.HotwordAudioStream.Builder setAudioStreamParcelFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
     method @NonNull public android.service.voice.HotwordAudioStream.Builder setMetadata(@NonNull android.os.PersistableBundle);
     method @NonNull public android.service.voice.HotwordAudioStream.Builder setTimestamp(@NonNull android.media.AudioTimestamp);
   }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7a9f3c1..67d4416 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -658,7 +658,8 @@
                                 "Received config update for non-existing activity");
                     }
                     activity.mMainThread.handleActivityConfigurationChanged(
-                            ActivityClientRecord.this, overrideConfig, newDisplayId);
+                            ActivityClientRecord.this, overrideConfig, newDisplayId,
+                            false /* alwaysReportChange */);
                 }
 
                 @Override
@@ -5859,13 +5860,13 @@
      * @return {@link Configuration} instance sent to client, null if not sent.
      */
     private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,
-            Configuration newBaseConfig, int displayId) {
+            Configuration newBaseConfig, int displayId, boolean alwaysReportChange) {
         r.tmpConfig.setTo(newBaseConfig);
         if (r.overrideConfig != null) {
             r.tmpConfig.updateFrom(r.overrideConfig);
         }
         final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,
-                r.tmpConfig, r.overrideConfig, displayId);
+                r.tmpConfig, r.overrideConfig, displayId, alwaysReportChange);
         freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
         return reportedConfig;
     }
@@ -5881,7 +5882,8 @@
      * @return Configuration sent to client, null if no changes and not moved to different display.
      */
     private Configuration performActivityConfigurationChanged(Activity activity,
-            Configuration newConfig, Configuration amOverrideConfig, int displayId) {
+            Configuration newConfig, Configuration amOverrideConfig, int displayId,
+            boolean alwaysReportChange) {
         final IBinder activityToken = activity.getActivityToken();
 
         // WindowConfiguration differences aren't considered as public, check it separately.
@@ -5900,9 +5902,9 @@
         final boolean shouldUpdateResources = hasPublicResConfigChange
                 || shouldUpdateResources(activityToken, currentResConfig, newConfig,
                 amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);
-        final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig,
-                r != null ? r.mSizeConfigurations : null,
-                activity.mActivityInfo.getRealConfigChanged());
+        final boolean shouldReportChange = shouldReportChange(
+                activity.mCurrentConfig, newConfig, r != null ? r.mSizeConfigurations : null,
+                activity.mActivityInfo.getRealConfigChanged(), alwaysReportChange);
         // Nothing significant, don't proceed with updating and reporting.
         if (!shouldUpdateResources && !shouldReportChange) {
             return null;
@@ -5962,12 +5964,18 @@
     @VisibleForTesting
     public static boolean shouldReportChange(@Nullable Configuration currentConfig,
             @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets,
-            int handledConfigChanges) {
+            int handledConfigChanges, boolean alwaysReportChange) {
         final int publicDiff = currentConfig.diffPublicOnly(newConfig);
         // Don't report the change if there's no public diff between current and new config.
         if (publicDiff == 0) {
             return false;
         }
+
+        // Report the change regardless if the changes across size-config-buckets.
+        if (alwaysReportChange) {
+            return true;
+        }
+
         final int diffWithBucket = SizeConfigurationBuckets.filterDiff(publicDiff, currentConfig,
                 newConfig, sizeBuckets);
         // Compare to the diff which filter the change without crossing size buckets with
@@ -6094,6 +6102,18 @@
         }
     }
 
+    @Override
+    public void handleActivityConfigurationChanged(ActivityClientRecord r,
+            @NonNull Configuration overrideConfig, int displayId) {
+        handleActivityConfigurationChanged(r, overrideConfig, displayId,
+                // This is the only place that uses alwaysReportChange=true. The entry point should
+                // be from ActivityConfigurationChangeItem or MoveToDisplayItem, so the server side
+                // has confirmed the activity should handle the configuration instead of relaunch.
+                // If Activity#onConfigurationChanged is called unexpectedly, then we can know it is
+                // something wrong from server side.
+                true /* alwaysReportChange */);
+    }
+
     /**
      * Handle new activity configuration and/or move to a different display. This method is a noop
      * if {@link #updatePendingActivityConfiguration(IBinder, Configuration)} has been
@@ -6104,9 +6124,8 @@
      * @param displayId Id of the display where activity was moved to, -1 if there was no move and
      *                  value didn't change.
      */
-    @Override
-    public void handleActivityConfigurationChanged(ActivityClientRecord r,
-            @NonNull Configuration overrideConfig, int displayId) {
+    void handleActivityConfigurationChanged(ActivityClientRecord r,
+            @NonNull Configuration overrideConfig, int displayId, boolean alwaysReportChange) {
         synchronized (mPendingOverrideConfigs) {
             final Configuration pendingOverrideConfig = mPendingOverrideConfigs.get(r.token);
             if (overrideConfig.isOtherSeqNewer(pendingOverrideConfig)) {
@@ -6150,7 +6169,8 @@
         }
         final Configuration reportedConfig = performConfigurationChangedForActivity(r,
                 mConfigurationController.getCompatConfiguration(),
-                movedToDifferentDisplay ? displayId : r.activity.getDisplayId());
+                movedToDifferentDisplay ? displayId : r.activity.getDisplayId(),
+                alwaysReportChange);
         // Notify the ViewRootImpl instance about configuration changes. It may have initiated this
         // update to make sure that resources are updated before updating itself.
         if (viewRoot != null) {
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index a8d8c75..5517c57 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -1196,7 +1196,8 @@
         return sb.toString();
     }
 
-    private static String reasonCodeToString(@Reason int reason) {
+    /** @hide */
+    public static String reasonCodeToString(@Reason int reason) {
         switch (reason) {
             case REASON_EXIT_SELF:
                 return "EXIT_SELF";
diff --git a/core/java/android/app/LocaleManager.java b/core/java/android/app/LocaleManager.java
index 794c694..be53a62 100644
--- a/core/java/android/app/LocaleManager.java
+++ b/core/java/android/app/LocaleManager.java
@@ -127,6 +127,7 @@
      * <p>This API can be used by an app's installer
      * (per {@link android.content.pm.InstallSourceInfo#getInstallingPackageName}) to retrieve
      * the app's locales.
+     * <p>This API can be used by the current input method to retrieve locales of another packages.
      * All other cases require {@code android.Manifest.permission#READ_APP_SPECIFIC_LOCALES}.
      * Apps should generally retrieve their own locales via their in-process LocaleLists,
      * or by calling {@link #getApplicationLocales()}.
diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/ui/ProviderData.java
index 18e6ba4..38bd4e5 100644
--- a/core/java/android/credentials/ui/ProviderData.java
+++ b/core/java/android/credentials/ui/ProviderData.java
@@ -41,7 +41,7 @@
             "android.credentials.ui.extra.PROVIDER_DATA_LIST";
 
     @NonNull
-    private final String mPackageName;
+    private final String mProviderId;
     @NonNull
     private final List<Entry> mCredentialEntries;
     @NonNull
@@ -50,11 +50,11 @@
     private final Entry mAuthenticationEntry;
 
     public ProviderData(
-            @NonNull String packageName,
+            @NonNull String providerId,
             @NonNull List<Entry> credentialEntries,
             @NonNull List<Entry> actionChips,
             @Nullable Entry authenticationEntry) {
-        mPackageName = packageName;
+        mProviderId = providerId;
         mCredentialEntries = credentialEntries;
         mActionChips = actionChips;
         mAuthenticationEntry = authenticationEntry;
@@ -62,8 +62,8 @@
 
     /** Returns the provider package name. */
     @NonNull
-    public String getPackageName() {
-        return mPackageName;
+    public String getProviderId() {
+        return mProviderId;
     }
 
     @NonNull
@@ -82,9 +82,9 @@
     }
 
     protected ProviderData(@NonNull Parcel in) {
-        String packageName = in.readString8();
-        mPackageName = packageName;
-        AnnotationValidations.validate(NonNull.class, null, mPackageName);
+        String providerId = in.readString8();
+        mProviderId = providerId;
+        AnnotationValidations.validate(NonNull.class, null, mProviderId);
 
         List<Entry> credentialEntries = new ArrayList<>();
         in.readTypedList(credentialEntries, Entry.CREATOR);
@@ -102,7 +102,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString8(mPackageName);
+        dest.writeString8(mProviderId);
         dest.writeTypedList(mCredentialEntries);
         dest.writeTypedList(mActionChips);
         dest.writeTypedObject(mAuthenticationEntry, flags);
diff --git a/core/java/android/credentials/ui/UserSelectionResult.java b/core/java/android/credentials/ui/UserSelectionResult.java
index 0927fb8..2ac5593 100644
--- a/core/java/android/credentials/ui/UserSelectionResult.java
+++ b/core/java/android/credentials/ui/UserSelectionResult.java
@@ -43,11 +43,16 @@
     @NonNull
     private final IBinder mRequestToken;
 
+    @NonNull
+    private final String mProviderId;
+
     // TODO: consider switching to string or other types, depending on the service implementation.
     private final int mEntryId;
 
-    public UserSelectionResult(@NonNull IBinder requestToken, int entryId) {
+    public UserSelectionResult(@NonNull IBinder requestToken, @NonNull String providerId,
+            int entryId) {
         mRequestToken = requestToken;
+        mProviderId = providerId;
         mEntryId = entryId;
     }
 
@@ -57,23 +62,33 @@
         return mRequestToken;
     }
 
+    /** Returns provider package name whose entry was selected by the user. */
+    @NonNull
+    public String getProviderId() {
+        return mProviderId;
+    }
+
     /** Returns the id of the visual entry that the user selected. */
-    public int geEntryId() {
+    public int getEntryId() {
         return mEntryId;
     }
 
     protected UserSelectionResult(@NonNull Parcel in) {
         IBinder requestToken = in.readStrongBinder();
+        String providerId = in.readString8();
         int entryId = in.readInt();
 
         mRequestToken = requestToken;
         AnnotationValidations.validate(NonNull.class, null, mRequestToken);
+        mProviderId = providerId;
+        AnnotationValidations.validate(NonNull.class, null, mProviderId);
         mEntryId = entryId;
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStrongBinder(mRequestToken);
+        dest.writeString8(mProviderId);
         dest.writeInt(mEntryId);
     }
 
diff --git a/core/java/android/service/voice/HotwordAudioStream.java b/core/java/android/service/voice/HotwordAudioStream.java
index 18375ad..1c4d14c91 100644
--- a/core/java/android/service/voice/HotwordAudioStream.java
+++ b/core/java/android/service/voice/HotwordAudioStream.java
@@ -57,10 +57,10 @@
      * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
      */
     @NonNull
-    private final ParcelFileDescriptor mAudioStream;
+    private final ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
 
     /**
-     * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+     * The timestamp when the audio stream was captured by the Audio platform.
      *
      * <p>
      * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
@@ -74,6 +74,8 @@
      * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
      * timestamps.
      * </p>
+     *
+     * @see #getAudioStreamParcelFileDescriptor()
      */
     @Nullable
     private final AudioTimestamp mTimestamp;
@@ -143,15 +145,15 @@
     @DataClass.Generated.Member
     /* package-private */ HotwordAudioStream(
             @NonNull AudioFormat audioFormat,
-            @NonNull ParcelFileDescriptor audioStream,
+            @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor,
             @Nullable AudioTimestamp timestamp,
             @NonNull PersistableBundle metadata) {
         this.mAudioFormat = audioFormat;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mAudioFormat);
-        this.mAudioStream = audioStream;
+        this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
         com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioStream);
+                NonNull.class, null, mAudioStreamParcelFileDescriptor);
         this.mTimestamp = timestamp;
         this.mMetadata = metadata;
         com.android.internal.util.AnnotationValidations.validate(
@@ -173,12 +175,12 @@
      * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
      */
     @DataClass.Generated.Member
-    public @NonNull ParcelFileDescriptor getAudioStream() {
-        return mAudioStream;
+    public @NonNull ParcelFileDescriptor getAudioStreamParcelFileDescriptor() {
+        return mAudioStreamParcelFileDescriptor;
     }
 
     /**
-     * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+     * The timestamp when the audio stream was captured by the Audio platform.
      *
      * <p>
      * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
@@ -192,6 +194,8 @@
      * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
      * timestamps.
      * </p>
+     *
+     * @see #getAudioStreamParcelFileDescriptor()
      */
     @DataClass.Generated.Member
     public @Nullable AudioTimestamp getTimestamp() {
@@ -214,7 +218,7 @@
 
         return "HotwordAudioStream { " +
                 "audioFormat = " + mAudioFormat + ", " +
-                "audioStream = " + mAudioStream + ", " +
+                "audioStreamParcelFileDescriptor = " + mAudioStreamParcelFileDescriptor + ", " +
                 "timestamp = " + timestampToString() + ", " +
                 "metadata = " + mMetadata +
         " }";
@@ -234,7 +238,7 @@
         //noinspection PointlessBooleanExpression
         return true
                 && Objects.equals(mAudioFormat, that.mAudioFormat)
-                && Objects.equals(mAudioStream, that.mAudioStream)
+                && Objects.equals(mAudioStreamParcelFileDescriptor, that.mAudioStreamParcelFileDescriptor)
                 && Objects.equals(mTimestamp, that.mTimestamp)
                 && Objects.equals(mMetadata, that.mMetadata);
     }
@@ -247,7 +251,7 @@
 
         int _hash = 1;
         _hash = 31 * _hash + Objects.hashCode(mAudioFormat);
-        _hash = 31 * _hash + Objects.hashCode(mAudioStream);
+        _hash = 31 * _hash + Objects.hashCode(mAudioStreamParcelFileDescriptor);
         _hash = 31 * _hash + Objects.hashCode(mTimestamp);
         _hash = 31 * _hash + Objects.hashCode(mMetadata);
         return _hash;
@@ -263,7 +267,7 @@
         if (mTimestamp != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeTypedObject(mAudioFormat, flags);
-        dest.writeTypedObject(mAudioStream, flags);
+        dest.writeTypedObject(mAudioStreamParcelFileDescriptor, flags);
         parcelTimestamp(dest, flags);
         dest.writeTypedObject(mMetadata, flags);
     }
@@ -281,16 +285,16 @@
 
         byte flg = in.readByte();
         AudioFormat audioFormat = (AudioFormat) in.readTypedObject(AudioFormat.CREATOR);
-        ParcelFileDescriptor audioStream = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
+        ParcelFileDescriptor audioStreamParcelFileDescriptor = (ParcelFileDescriptor) in.readTypedObject(ParcelFileDescriptor.CREATOR);
         AudioTimestamp timestamp = unparcelTimestamp(in);
         PersistableBundle metadata = (PersistableBundle) in.readTypedObject(PersistableBundle.CREATOR);
 
         this.mAudioFormat = audioFormat;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mAudioFormat);
-        this.mAudioStream = audioStream;
+        this.mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
         com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAudioStream);
+                NonNull.class, null, mAudioStreamParcelFileDescriptor);
         this.mTimestamp = timestamp;
         this.mMetadata = metadata;
         com.android.internal.util.AnnotationValidations.validate(
@@ -321,7 +325,7 @@
     public static final class Builder {
 
         private @NonNull AudioFormat mAudioFormat;
-        private @NonNull ParcelFileDescriptor mAudioStream;
+        private @NonNull ParcelFileDescriptor mAudioStreamParcelFileDescriptor;
         private @Nullable AudioTimestamp mTimestamp;
         private @NonNull PersistableBundle mMetadata;
 
@@ -332,19 +336,19 @@
          *
          * @param audioFormat
          *   The {@link AudioFormat} of the audio stream.
-         * @param audioStream
+         * @param audioStreamParcelFileDescriptor
          *   This stream starts with the audio bytes used for hotword detection, but continues streaming
          *   the audio until the stream is shutdown by the {@link HotwordDetectionService}.
          */
         public Builder(
                 @NonNull AudioFormat audioFormat,
-                @NonNull ParcelFileDescriptor audioStream) {
+                @NonNull ParcelFileDescriptor audioStreamParcelFileDescriptor) {
             mAudioFormat = audioFormat;
             com.android.internal.util.AnnotationValidations.validate(
                     NonNull.class, null, mAudioFormat);
-            mAudioStream = audioStream;
+            mAudioStreamParcelFileDescriptor = audioStreamParcelFileDescriptor;
             com.android.internal.util.AnnotationValidations.validate(
-                    NonNull.class, null, mAudioStream);
+                    NonNull.class, null, mAudioStreamParcelFileDescriptor);
         }
 
         /**
@@ -363,15 +367,15 @@
          * the audio until the stream is shutdown by the {@link HotwordDetectionService}.
          */
         @DataClass.Generated.Member
-        public @NonNull Builder setAudioStream(@NonNull ParcelFileDescriptor value) {
+        public @NonNull Builder setAudioStreamParcelFileDescriptor(@NonNull ParcelFileDescriptor value) {
             checkNotUsed();
             mBuilderFieldsSet |= 0x2;
-            mAudioStream = value;
+            mAudioStreamParcelFileDescriptor = value;
             return this;
         }
 
         /**
-         * The timestamp when the {@link #getAudioStream()} was captured by the Audio platform.
+         * The timestamp when the audio stream was captured by the Audio platform.
          *
          * <p>
          * The {@link HotwordDetectionService} egressing the audio is the owner of the underlying
@@ -385,6 +389,8 @@
          * {@link HotwordDetectedResult#getHotwordDurationMillis()} to translate these durations to
          * timestamps.
          * </p>
+         *
+         * @see #getAudioStreamParcelFileDescriptor()
          */
         @DataClass.Generated.Member
         public @NonNull Builder setTimestamp(@NonNull AudioTimestamp value) {
@@ -418,7 +424,7 @@
             }
             HotwordAudioStream o = new HotwordAudioStream(
                     mAudioFormat,
-                    mAudioStream,
+                    mAudioStreamParcelFileDescriptor,
                     mTimestamp,
                     mMetadata);
             return o;
@@ -433,10 +439,10 @@
     }
 
     @DataClass.Generated(
-            time = 1665463434564L,
+            time = 1665976240224L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/voice/HotwordAudioStream.java",
-            inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStream\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static  android.media.AudioTimestamp defaultTimestamp()\nprivate static  android.os.PersistableBundle defaultMetadata()\nprivate  java.lang.String timestampToString()\nprivate  void parcelTimestamp(android.os.Parcel,int)\nprivate static @android.annotation.Nullable android.media.AudioTimestamp unparcelTimestamp(android.os.Parcel)\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
+            inputSignatures = "private final @android.annotation.NonNull android.media.AudioFormat mAudioFormat\nprivate final @android.annotation.NonNull android.os.ParcelFileDescriptor mAudioStreamParcelFileDescriptor\nprivate final @android.annotation.Nullable android.media.AudioTimestamp mTimestamp\nprivate final @android.annotation.NonNull android.os.PersistableBundle mMetadata\nprivate static  android.media.AudioTimestamp defaultTimestamp()\nprivate static  android.os.PersistableBundle defaultMetadata()\nprivate  java.lang.String timestampToString()\nprivate  void parcelTimestamp(android.os.Parcel,int)\nprivate static @android.annotation.Nullable android.media.AudioTimestamp unparcelTimestamp(android.os.Parcel)\nclass HotwordAudioStream extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/voice/HotwordDetectedResult.java b/core/java/android/service/voice/HotwordDetectedResult.java
index 6255d00..e22bbd8 100644
--- a/core/java/android/service/voice/HotwordDetectedResult.java
+++ b/core/java/android/service/voice/HotwordDetectedResult.java
@@ -32,6 +32,7 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -203,7 +204,7 @@
     @NonNull
     private final List<HotwordAudioStream> mAudioStreams;
     private static List<HotwordAudioStream> defaultAudioStreams() {
-        return new ArrayList<>();
+        return Collections.emptyList();
     }
 
     /**
@@ -364,9 +365,27 @@
         }
     }
 
+    /**
+     * The list of the audio streams containing audio bytes that were used for hotword detection.
+     */
+    public @NonNull List<HotwordAudioStream> getAudioStreams() {
+        return List.copyOf(mAudioStreams);
+    }
+
     @DataClass.Suppress("addAudioStreams")
     abstract static class BaseBuilder {
-
+        /**
+         * The list of the audio streams containing audio bytes that were used for hotword
+         * detection.
+         */
+        public @NonNull Builder setAudioStreams(@NonNull List<HotwordAudioStream> value) {
+            Objects.requireNonNull(value, "value should not be null");
+            final Builder builder = (Builder) this;
+            // If the code gen flag in build() is changed, we must update the flag e.g. 0x200 here.
+            builder.mBuilderFieldsSet |= 0x200;
+            builder.mAudioStreams = List.copyOf(value);
+            return builder;
+        }
     }
 
 
@@ -555,14 +574,6 @@
     }
 
     /**
-     * The list of the audio streams containing audio bytes that were used for hotword detection.
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<HotwordAudioStream> getAudioStreams() {
-        return mAudioStreams;
-    }
-
-    /**
      * App-specific extras to support trigger.
      *
      * <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -881,17 +892,6 @@
         }
 
         /**
-         * The list of the audio streams containing audio bytes that were used for hotword detection.
-         */
-        @DataClass.Generated.Member
-        public @NonNull Builder setAudioStreams(@NonNull List<HotwordAudioStream> value) {
-            checkNotUsed();
-            mBuilderFieldsSet |= 0x200;
-            mAudioStreams = value;
-            return this;
-        }
-
-        /**
          * App-specific extras to support trigger.
          *
          * <p>The size of this bundle will be limited to {@link #getMaxBundleSize}. Results will larger
@@ -984,10 +984,10 @@
     }
 
     @DataClass.Generated(
-            time = 1664876310951L,
+            time = 1665995595979L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/service/voice/HotwordDetectedResult.java",
-            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final  int CONFIDENCE_LEVEL_HIGH\npublic static final  int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final  int HOTWORD_OFFSET_UNSET\npublic static final  int AUDIO_CHANNEL_UNSET\nprivate static final  int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final  int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final  java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate  int mHotwordOffsetMillis\nprivate  int mHotwordDurationMillis\nprivate  int mAudioChannel\nprivate  boolean mHotwordDetectionPersonalized\nprivate final  int mScore\nprivate final  int mPersonalizedScore\nprivate final  int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static  int sMaxBundleSize\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultScore()\nprivate static  int defaultPersonalizedScore()\npublic static  int getMaxScore()\nprivate static  int defaultHotwordPhraseId()\npublic static  int getMaxHotwordPhraseId()\nprivate static  java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static  android.os.PersistableBundle defaultExtras()\npublic static  int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static  int getParcelableSize(android.os.Parcelable)\npublic static  int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static  int bitCount(long)\nprivate  void onConstructed()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int CONFIDENCE_LEVEL_NONE\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final  int CONFIDENCE_LEVEL_HIGH\npublic static final  int CONFIDENCE_LEVEL_VERY_HIGH\npublic static final  int HOTWORD_OFFSET_UNSET\npublic static final  int AUDIO_CHANNEL_UNSET\nprivate static final  int LIMIT_HOTWORD_OFFSET_MAX_VALUE\nprivate static final  int LIMIT_AUDIO_CHANNEL_MAX_VALUE\npublic static final  java.lang.String EXTRA_PROXIMITY_METERS\nprivate final @android.service.voice.HotwordDetectedResult.HotwordConfidenceLevelValue int mConfidenceLevel\nprivate @android.annotation.Nullable android.media.MediaSyncEvent mMediaSyncEvent\nprivate  int mHotwordOffsetMillis\nprivate  int mHotwordDurationMillis\nprivate  int mAudioChannel\nprivate  boolean mHotwordDetectionPersonalized\nprivate final  int mScore\nprivate final  int mPersonalizedScore\nprivate final  int mHotwordPhraseId\nprivate final @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> mAudioStreams\nprivate final @android.annotation.NonNull android.os.PersistableBundle mExtras\nprivate static  int sMaxBundleSize\nprivate static  int defaultConfidenceLevel()\nprivate static  int defaultScore()\nprivate static  int defaultPersonalizedScore()\npublic static  int getMaxScore()\nprivate static  int defaultHotwordPhraseId()\npublic static  int getMaxHotwordPhraseId()\nprivate static  java.util.List<android.service.voice.HotwordAudioStream> defaultAudioStreams()\nprivate static  android.os.PersistableBundle defaultExtras()\npublic static  int getMaxBundleSize()\npublic @android.annotation.Nullable android.media.MediaSyncEvent getMediaSyncEvent()\npublic static  int getParcelableSize(android.os.Parcelable)\npublic static  int getUsageSize(android.service.voice.HotwordDetectedResult)\nprivate static  int bitCount(long)\nprivate  void onConstructed()\npublic @android.annotation.NonNull java.util.List<android.service.voice.HotwordAudioStream> getAudioStreams()\nclass HotwordDetectedResult extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)\npublic @android.annotation.NonNull android.service.voice.HotwordDetectedResult.Builder setAudioStreams(java.util.List<android.service.voice.HotwordAudioStream>)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index e5a535b..f51d9ba 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -218,6 +218,11 @@
     private static final int WINDOW_TOUCH_SLOP = 16;
 
     /**
+     * Margin in dips around text line bounds where stylus handwriting gestures should be supported.
+     */
+    private static final int HANDWRITING_GESTURE_LINE_MARGIN = 16;
+
+    /**
      * Minimum velocity to initiate a fling, as measured in dips per second
      */
     private static final int MINIMUM_FLING_VELOCITY = 50;
@@ -338,6 +343,7 @@
     private final int mPagingTouchSlop;
     private final int mDoubleTapSlop;
     private final int mWindowTouchSlop;
+    private final int mHandwritingGestureLineMargin;
     private final float mAmbiguousGestureMultiplier;
     private final int mMaximumDrawingCacheSize;
     private final int mOverscrollDistance;
@@ -381,6 +387,7 @@
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
         mDoubleTapSlop = DOUBLE_TAP_SLOP;
         mWindowTouchSlop = WINDOW_TOUCH_SLOP;
+        mHandwritingGestureLineMargin = HANDWRITING_GESTURE_LINE_MARGIN;
         mAmbiguousGestureMultiplier = AMBIGUOUS_GESTURE_MULTIPLIER;
         //noinspection deprecation
         mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
@@ -490,6 +497,9 @@
 
         mDoubleTapTouchSlop = mTouchSlop;
 
+        mHandwritingGestureLineMargin = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_viewConfigurationHandwritingGestureLineMargin);
+
         mMinimumFlingVelocity = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.config_viewMinFlingVelocity);
         mMaximumFlingVelocity = res.getDimensionPixelSize(
@@ -796,6 +806,14 @@
     }
 
     /**
+     * @return margin in pixels around text line bounds where stylus handwriting gestures should be
+     *     supported.
+     */
+    public int getScaledHandwritingGestureLineMargin() {
+        return mHandwritingGestureLineMargin;
+    }
+
+    /**
      * Interval for dispatching a recurring accessibility event in milliseconds.
      * This interval guarantees that a recurring event will be send at most once
      * during the {@link #getSendRecurringAccessibilityEventsInterval()} time frame.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ce5365a..a2e9faa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9391,12 +9391,8 @@
     /** @hide */
     public int performHandwritingInsertGesture(@NonNull InsertGesture gesture) {
         PointF point = convertFromScreenToContentCoordinates(gesture.getInsertionPoint());
-        int line = mLayout.getLineForVertical((int) point.y);
-        if (point.y < mLayout.getLineTop(line)
-                || point.y > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
-            return handleGestureFailure(gesture);
-        }
-        if (point.x < mLayout.getLineLeft(line) || point.x > mLayout.getLineRight(line)) {
+        int line = getLineForHandwritingGesture(point);
+        if (line == -1) {
             return handleGestureFailure(gesture);
         }
         int offset = mLayout.getOffsetForHorizontal(line, point.x);
@@ -9412,27 +9408,17 @@
         PointF startPoint = convertFromScreenToContentCoordinates(gesture.getStartPoint());
         PointF endPoint = convertFromScreenToContentCoordinates(gesture.getEndPoint());
 
-        // The operation should be applied to the first line of text touched by the line joining
-        // the points.
-        int yMin = (int) Math.min(startPoint.y, endPoint.y);
-        int yMax = (int) Math.max(startPoint.y, endPoint.y);
-        int line = mLayout.getLineForVertical(yMin);
-        if (yMax < mLayout.getLineTop(line)) {
-            // Both points are above the top of the first line.
-            return handleGestureFailure(gesture);
-        }
-        if (yMin > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
-            if (line == mLayout.getLineCount() - 1 || yMax < mLayout.getLineTop(line + 1)) {
-                // The points are below the last line, or they are between two lines.
+        // The operation should be applied to the first line of text containing one of the points.
+        int startPointLine = getLineForHandwritingGesture(startPoint);
+        int endPointLine = getLineForHandwritingGesture(endPoint);
+        int line;
+        if (startPointLine == -1) {
+            if (endPointLine == -1) {
                 return handleGestureFailure(gesture);
-            } else {
-                // Apply the operation to the next line.
-                line++;
             }
-        }
-        if (Math.max(startPoint.x, endPoint.x) < mLayout.getLineLeft(line)
-                || Math.min(startPoint.x, endPoint.x) > mLayout.getLineRight(line)) {
-            return handleGestureFailure(gesture);
+            line = endPointLine;
+        } else {
+            line = (endPointLine == -1) ? startPointLine : Math.min(startPointLine, endPointLine);
         }
 
         // The operation should be applied to all characters touched by the line joining the points.
@@ -9479,12 +9465,8 @@
     public int performHandwritingJoinOrSplitGesture(@NonNull JoinOrSplitGesture gesture) {
         PointF point = convertFromScreenToContentCoordinates(gesture.getJoinOrSplitPoint());
 
-        int line = mLayout.getLineForVertical((int) point.y);
-        if (point.y < mLayout.getLineTop(line)
-                || point.y > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
-            return handleGestureFailure(gesture);
-        }
-        if (point.x < mLayout.getLineLeft(line) || point.x > mLayout.getLineRight(line)) {
+        int line = getLineForHandwritingGesture(point);
+        if (line == -1) {
             return handleGestureFailure(gesture);
         }
 
@@ -9529,6 +9511,37 @@
         return InputConnection.HANDWRITING_GESTURE_RESULT_FAILED;
     }
 
+    /**
+     * Returns the closest line such that the point is either inside the line bounds or within
+     * {@link ViewConfiguration#getScaledHandwritingGestureLineMargin} of the line bounds. Returns
+     * -1 if the point is not within the margin of any line bounds.
+     */
+    private int getLineForHandwritingGesture(PointF point) {
+        int line = mLayout.getLineForVertical((int) point.y);
+        int lineMargin = ViewConfiguration.get(mContext).getScaledHandwritingGestureLineMargin();
+        if (line < mLayout.getLineCount() - 1
+                && point.y > mLayout.getLineBottom(line) - lineMargin
+                && point.y
+                        > (mLayout.getLineBottom(line, false) + mLayout.getLineBottom(line)) / 2f) {
+            // If a point is in the space between line i and line (i + 1), Layout#getLineForVertical
+            // returns i. If the point is within lineMargin of line (i + 1), and closer to line
+            // (i + 1) than line i, then the gesture operation should be applied to line (i + 1).
+            line++;
+        } else if (point.y < mLayout.getLineTop(line) - lineMargin
+                || point.y
+                        > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)
+                                + lineMargin) {
+            // The point is not within lineMargin of a line.
+            return -1;
+        }
+        if (point.x < mLayout.getLineLeft(line) - lineMargin
+                || point.x > mLayout.getLineRight(line) + lineMargin) {
+            // The point is not within lineMargin of a line.
+            return -1;
+        }
+        return line;
+    }
+
     @Nullable
     private Range<Integer> getRangeForRect(@NonNull RectF area, int granularity) {
         SegmentFinder segmentFinder;
diff --git a/core/java/com/android/internal/app/AppLocaleCollector.java b/core/java/com/android/internal/app/AppLocaleCollector.java
new file mode 100644
index 0000000..65e8c64
--- /dev/null
+++ b/core/java/com/android/internal/app/AppLocaleCollector.java
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.app;
+
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
+import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.LocaleList;
+import android.util.Log;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/** The Locale data collector for per-app language. */
+class AppLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
+    private static final String TAG = AppLocaleCollector.class.getSimpleName();
+    private final Context mContext;
+    private final String mAppPackageName;
+    private final LocaleStore.LocaleInfo mAppCurrentLocale;
+
+    AppLocaleCollector(Context context, String appPackageName) {
+        mContext = context;
+        mAppPackageName = appPackageName;
+        mAppCurrentLocale = LocaleStore.getAppCurrentLocaleInfo(
+                mContext, mAppPackageName);
+    }
+
+    @Override
+    public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
+        HashSet<String> langTagsToIgnore = new HashSet<>();
+
+        LocaleList systemLangList = LocaleList.getDefault();
+        for(int i = 0; i < systemLangList.size(); i++) {
+            langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
+        }
+
+        if (mAppCurrentLocale != null) {
+            langTagsToIgnore.add(mAppCurrentLocale.getLocale().toLanguageTag());
+        }
+        return langTagsToIgnore;
+    }
+
+    @Override
+    public Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
+            boolean translatedOnly, boolean isForCountryMode) {
+        AppLocaleStore.AppLocaleResult result =
+                AppLocaleStore.getAppSupportedLocales(mContext, mAppPackageName);
+        Set<String> langTagsToIgnore = getIgnoredLocaleList(translatedOnly);
+        Set<LocaleStore.LocaleInfo> appLocaleList = new HashSet<>();
+        Set<LocaleStore.LocaleInfo> systemLocaleList;
+        boolean shouldShowList =
+                result.mLocaleStatus == GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
+                        || result.mLocaleStatus == GET_SUPPORTED_LANGUAGE_FROM_ASSET;
+
+        // Get system supported locale list
+        if (isForCountryMode) {
+            systemLocaleList = LocaleStore.getLevelLocales(mContext,
+                    langTagsToIgnore, parent, translatedOnly);
+        } else {
+            systemLocaleList = LocaleStore.getLevelLocales(mContext, langTagsToIgnore,
+                    null /* no parent */, translatedOnly);
+        }
+
+        // Add current app locale
+        if (mAppCurrentLocale != null && !isForCountryMode) {
+            appLocaleList.add(mAppCurrentLocale);
+        }
+
+        // Add current system language into suggestion list
+        for(LocaleStore.LocaleInfo localeInfo:
+                LocaleStore.getSystemCurrentLocaleInfo()) {
+            boolean isNotCurrentLocale = mAppCurrentLocale == null
+                    || !localeInfo.getLocale().equals(mAppCurrentLocale.getLocale());
+            if (!isForCountryMode && isNotCurrentLocale) {
+                appLocaleList.add(localeInfo);
+            }
+        }
+
+        // Add the languages that included in system supported locale
+        if (shouldShowList) {
+            appLocaleList.addAll(filterTheLanguagesNotIncludedInSystemLocale(
+                    systemLocaleList, result.mAppSupportedLocales));
+        }
+
+        // Add "system language" option
+        if (!isForCountryMode && shouldShowList) {
+            appLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(
+                    mAppCurrentLocale == null));
+        }
+
+        if (Build.isDebuggable()) {
+            Log.d(TAG, "App locale list: " + appLocaleList);
+        }
+
+        return appLocaleList;
+    }
+
+    @Override
+    public boolean hasSpecificPackageName() {
+        return true;
+    }
+
+    private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotIncludedInSystemLocale(
+            Set<LocaleStore.LocaleInfo> systemLocaleList,
+            HashSet<Locale> appSupportedLocales) {
+        Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
+
+        for(LocaleStore.LocaleInfo li: systemLocaleList) {
+            if (appSupportedLocales.contains(li.getLocale())) {
+                filteredList.add(li);
+            } else {
+                for(Locale l: appSupportedLocales) {
+                    if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
+                        filteredList.add(li);
+                        break;
+                    }
+                }
+            }
+        }
+        return filteredList;
+    }
+}
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 965895f..3efd279 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -16,16 +16,12 @@
 
 package com.android.internal.app;
 
-import static com.android.internal.app.AppLocaleStore.AppLocaleResult.LocaleStatus;
-
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
 import android.app.ListFragment;
 import android.content.Context;
 import android.os.Bundle;
-import android.os.LocaleList;
 import android.text.TextUtils;
-import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -36,7 +32,6 @@
 
 import com.android.internal.R;
 
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Set;
@@ -54,6 +49,7 @@
 
     private SuggestedLocaleAdapter mAdapter;
     private LocaleSelectedListener mListener;
+    private LocaleCollectorBase mLocalePickerCollector;
     private Set<LocaleStore.LocaleInfo> mLocaleList;
     private LocaleStore.LocaleInfo mParentLocale;
     private boolean mTranslatedOnly = false;
@@ -62,7 +58,6 @@
     private boolean mPreviousSearchHadFocus = false;
     private int mFirstVisiblePosition = 0;
     private int mTopDistance = 0;
-    private String mAppPackageName;
     private CharSequence mTitle = null;
     private OnActionExpandListener mOnActionExpandListener;
 
@@ -79,31 +74,50 @@
         void onLocaleSelected(LocaleStore.LocaleInfo locale);
     }
 
-    private static LocalePickerWithRegion createCountryPicker(Context context,
+    /**
+     * The interface which provides the locale list.
+     */
+    interface LocaleCollectorBase {
+        /** Gets the ignored locale list. */
+        HashSet<String> getIgnoredLocaleList(boolean translatedOnly);
+
+        /** Gets the supported locale list. */
+        Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
+                boolean translatedOnly, boolean isForCountryMode);
+
+        /** Indicates if the class work for specific package. */
+        boolean hasSpecificPackageName();
+    }
+
+    private static LocalePickerWithRegion createCountryPicker(
             LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
-            boolean translatedOnly, String appPackageName,
-            OnActionExpandListener onActionExpandListener) {
+            boolean translatedOnly, OnActionExpandListener onActionExpandListener,
+            LocaleCollectorBase localePickerCollector) {
         LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
         localePicker.setOnActionExpandListener(onActionExpandListener);
-        boolean shouldShowTheList = localePicker.setListener(context, listener, parent,
-                translatedOnly, appPackageName);
+        boolean shouldShowTheList = localePicker.setListener(listener, parent,
+                translatedOnly, localePickerCollector);
         return shouldShowTheList ? localePicker : null;
     }
 
     public static LocalePickerWithRegion createLanguagePicker(Context context,
             LocaleSelectedListener listener, boolean translatedOnly) {
-        LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
-        localePicker.setListener(context, listener, /* parent */ null, translatedOnly, null);
-        return localePicker;
+        return createLanguagePicker(context, listener, translatedOnly, null, null);
     }
 
     public static LocalePickerWithRegion createLanguagePicker(Context context,
             LocaleSelectedListener listener, boolean translatedOnly, String appPackageName,
             OnActionExpandListener onActionExpandListener) {
+        LocaleCollectorBase localePickerController;
+        if (TextUtils.isEmpty(appPackageName)) {
+            localePickerController = new SystemLocaleCollector(context);
+        } else {
+            localePickerController = new AppLocaleCollector(context, appPackageName);
+        }
         LocalePickerWithRegion localePicker = new LocalePickerWithRegion();
         localePicker.setOnActionExpandListener(onActionExpandListener);
-        localePicker.setListener(
-                context, listener, /* parent */ null, translatedOnly, appPackageName);
+        localePicker.setListener(listener, /* parent */ null, translatedOnly,
+                localePickerController);
         return localePicker;
     }
 
@@ -120,109 +134,23 @@
      * In this case we don't even show the list, we call the listener with that locale,
      * "pretending" it was selected, and return false.</p>
      */
-    private boolean setListener(Context context, LocaleSelectedListener listener,
-            LocaleStore.LocaleInfo parent, boolean translatedOnly, String appPackageName) {
+    private boolean setListener(LocaleSelectedListener listener, LocaleStore.LocaleInfo parent,
+            boolean translatedOnly, LocaleCollectorBase localePickerController) {
         this.mParentLocale = parent;
         this.mListener = listener;
         this.mTranslatedOnly = translatedOnly;
-        this.mAppPackageName = appPackageName;
+        this.mLocalePickerCollector = localePickerController;
         setRetainInstance(true);
 
-        final HashSet<String> langTagsToIgnore = new HashSet<>();
-        LocaleStore.LocaleInfo appCurrentLocale =
-                LocaleStore.getAppCurrentLocaleInfo(context, appPackageName);
-        boolean isForCountryMode = parent != null;
+        mLocaleList = localePickerController.getSupportedLocaleList(
+                parent, translatedOnly, parent != null);
 
-        if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) {
-            // Filter current system locale to add them into suggestion
-            LocaleList systemLangList = LocaleList.getDefault();
-            for(int i = 0; i < systemLangList.size(); i++) {
-                langTagsToIgnore.add(systemLangList.get(i).toLanguageTag());
-            }
-
-            if (appCurrentLocale != null) {
-                Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag());
-                langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag());
-            } else {
-                Log.d(TAG, "appCurrentLocale is null");
-            }
-        } else if (!translatedOnly) {
-            final LocaleList userLocales = LocalePicker.getLocales();
-            final String[] langTags = userLocales.toLanguageTags().split(",");
-            Collections.addAll(langTagsToIgnore, langTags);
-        }
-
-        if (isForCountryMode) {
-            mLocaleList = LocaleStore.getLevelLocales(context,
-                    langTagsToIgnore, parent, translatedOnly);
-            if (mLocaleList.size() <= 1) {
-                if (listener != null && (mLocaleList.size() == 1)) {
-                    listener.onLocaleSelected(mLocaleList.iterator().next());
-                }
-                return false;
-            }
+        if (parent != null && listener != null && mLocaleList.size() == 1) {
+            listener.onLocaleSelected(mLocaleList.iterator().next());
+            return false;
         } else {
-            mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore,
-                    null /* no parent */, translatedOnly);
+            return true;
         }
-        Log.d(TAG, "mLocaleList size:  " + mLocaleList.size());
-
-        // Adding current locale and system default option into suggestion list
-        if(!TextUtils.isEmpty(appPackageName)) {
-            if (appCurrentLocale != null && !isForCountryMode) {
-                mLocaleList.add(appCurrentLocale);
-            }
-
-            AppLocaleStore.AppLocaleResult result =
-                    AppLocaleStore.getAppSupportedLocales(context, appPackageName);
-            boolean shouldShowList =
-                    result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG
-                    || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET;
-
-            // Add current system language into suggestion list
-            for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) {
-                boolean isNotCurrentLocale = appCurrentLocale == null
-                        || !localeInfo.getLocale().equals(appCurrentLocale.getLocale());
-                if (!isForCountryMode && isNotCurrentLocale) {
-                    mLocaleList.add(localeInfo);
-                }
-            }
-
-            // Filter the language not support in app
-            mLocaleList = filterTheLanguagesNotSupportedInApp(
-                    shouldShowList, result.mAppSupportedLocales);
-
-            Log.d(TAG, "mLocaleList after app-supported filter:  " + mLocaleList.size());
-
-            // Add "system language"
-            if (!isForCountryMode && shouldShowList) {
-                mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null));
-            }
-        }
-        return true;
-    }
-
-    private Set<LocaleStore.LocaleInfo> filterTheLanguagesNotSupportedInApp(
-            boolean shouldShowList, HashSet<Locale> supportedLocales) {
-        Set<LocaleStore.LocaleInfo> filteredList = new HashSet<>();
-        if (!shouldShowList) {
-            return filteredList;
-        }
-
-        for(LocaleStore.LocaleInfo li: mLocaleList) {
-            if (supportedLocales.contains(li.getLocale())) {
-                filteredList.add(li);
-            } else {
-                for(Locale l: supportedLocales) {
-                    if(LocaleList.matchesLanguageAndScript(li.getLocale(), l)) {
-                        filteredList.add(li);
-                        break;
-                    }
-                }
-            }
-        }
-
-        return filteredList;
     }
 
     private void returnToParentFrame() {
@@ -246,7 +174,9 @@
         mTitle = getActivity().getTitle();
         final boolean countryMode = mParentLocale != null;
         final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault();
-        mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, mAppPackageName);
+        final boolean hasSpecificPackageName =
+                mLocalePickerCollector != null && mLocalePickerCollector.hasSpecificPackageName();
+        mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode, hasSpecificPackageName);
         final LocaleHelper.LocaleInfoComparator comp =
                 new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode);
         mAdapter.sort(comp);
@@ -321,8 +251,8 @@
             returnToParentFrame();
         } else {
             LocalePickerWithRegion selector = LocalePickerWithRegion.createCountryPicker(
-                    getContext(), mListener, locale, mTranslatedOnly /* translate only */,
-                    mAppPackageName, mOnActionExpandListener);
+                    mListener, locale, mTranslatedOnly /* translate only */,
+                    mOnActionExpandListener, this.mLocalePickerCollector);
             if (selector != null) {
                 getFragmentManager().beginTransaction()
                         .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
@@ -340,7 +270,8 @@
             inflater.inflate(R.menu.language_selection_list, menu);
 
             final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
-            if (!TextUtils.isEmpty(mAppPackageName) && mOnActionExpandListener != null) {
+            if (mLocalePickerCollector.hasSpecificPackageName()
+                    && mOnActionExpandListener != null) {
                 searchMenuItem.setOnActionExpandListener(mOnActionExpandListener);
             }
 
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index 8f6bc43..a61a6d7 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -69,17 +69,17 @@
     protected Locale mDisplayLocale = null;
     // used to potentially cache a modified Context that uses mDisplayLocale
     protected Context mContextOverride = null;
-    private String mAppPackageName;
+    private boolean mHasSpecificAppPackageName;
 
     public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) {
-        this(localeOptions, countryMode, null);
+        this(localeOptions, countryMode, false);
     }
 
     public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode,
-            String appPackageName) {
+            boolean hasSpecificAppPackageName) {
         mCountryMode = countryMode;
         mLocaleOptions = new ArrayList<>(localeOptions.size());
-        mAppPackageName = appPackageName;
+        mHasSpecificAppPackageName = hasSpecificAppPackageName;
 
         for (LocaleStore.LocaleInfo li : localeOptions) {
             if (li.isSuggested()) {
@@ -136,7 +136,7 @@
 
     @Override
     public int getViewTypeCount() {
-        if (!TextUtils.isEmpty(mAppPackageName) && showHeaders()) {
+        if (mHasSpecificAppPackageName && showHeaders()) {
             // Two headers, 1 "System language", 1 current locale
             return APP_LANGUAGE_PICKER_TYPE_COUNT;
         } else if (showHeaders()) {
diff --git a/core/java/com/android/internal/app/SystemLocaleCollector.java b/core/java/com/android/internal/app/SystemLocaleCollector.java
new file mode 100644
index 0000000..9a6d4c1
--- /dev/null
+++ b/core/java/com/android/internal/app/SystemLocaleCollector.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.os.LocaleList;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/** The Locale data collector for System language. */
+class SystemLocaleCollector implements LocalePickerWithRegion.LocaleCollectorBase {
+    private final Context mContext;
+
+    SystemLocaleCollector(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public HashSet<String> getIgnoredLocaleList(boolean translatedOnly) {
+        HashSet<String> ignoreList = new HashSet<>();
+        if (!translatedOnly) {
+            final LocaleList userLocales = LocalePicker.getLocales();
+            final String[] langTags = userLocales.toLanguageTags().split(",");
+            Collections.addAll(ignoreList, langTags);
+        }
+        return ignoreList;
+    }
+
+    @Override
+    public Set<LocaleStore.LocaleInfo> getSupportedLocaleList(LocaleStore.LocaleInfo parent,
+            boolean translatedOnly, boolean isForCountryMode) {
+        Set<String> langTagsToIgnore = getIgnoredLocaleList(translatedOnly);
+        Set<LocaleStore.LocaleInfo> localeList;
+
+        if (isForCountryMode) {
+            localeList = LocaleStore.getLevelLocales(mContext,
+                    langTagsToIgnore, parent, translatedOnly);
+        } else {
+            localeList = LocaleStore.getLevelLocales(mContext, langTagsToIgnore,
+                    null /* no parent */, translatedOnly);
+        }
+        return localeList;
+    }
+
+
+    @Override
+    public boolean hasSpecificPackageName() {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java
index f725b37..d5f7ba5 100644
--- a/core/java/com/android/internal/infra/AbstractRemoteService.java
+++ b/core/java/com/android/internal/infra/AbstractRemoteService.java
@@ -20,10 +20,14 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ApplicationExitInfo;
+import android.app.IActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.ParceledListSlice;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
@@ -39,6 +43,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Base class representing a remote service.
@@ -66,6 +71,7 @@
 @Deprecated
 public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>,
         I extends IInterface> implements DeathRecipient {
+    private static final int SERVICE_NOT_EXIST = -1;
     private static final int MSG_BIND = 1;
     private static final int MSG_UNBIND = 2;
 
@@ -96,6 +102,9 @@
 
     // Used just for debugging purposes (on dump)
     private long mNextUnbind;
+    // Used just for debugging purposes (on dump)
+    private int mServiceExitReason;
+    private int mServiceExitSubReason;
 
     /** Requests that have been scheduled, but that are not finished yet */
     private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>();
@@ -126,6 +135,8 @@
         mUserId = userId;
         mHandler = new Handler(handler.getLooper());
         mBindingFlags = bindingFlags;
+        mServiceExitReason = SERVICE_NOT_EXIST;
+        mServiceExitSubReason = SERVICE_NOT_EXIST;
     }
 
     /**
@@ -229,6 +240,7 @@
         if (mService != null) {
             mService.asBinder().unlinkToDeath(this, 0);
         }
+        updateServicelicationExitInfo(mComponentName, mUserId);
         mConnecting = true;
         mService = null;
         mServiceDied = true;
@@ -239,6 +251,33 @@
         handleBindFailure();
     }
 
+    private void updateServicelicationExitInfo(ComponentName componentName, int userId) {
+        IActivityManager am = ActivityManager.getService();
+        String packageName = componentName.getPackageName();
+        ParceledListSlice<ApplicationExitInfo> plistSlice = null;
+        try {
+            plistSlice = am.getHistoricalProcessExitReasons(packageName, 0, 1, userId);
+        } catch (RemoteException e) {
+            // do nothing. The local binder so it can not throw it.
+        }
+        if (plistSlice == null) {
+            return;
+        }
+        List<ApplicationExitInfo> list = plistSlice.getList();
+        if (list.isEmpty()) {
+            return;
+        }
+        ApplicationExitInfo info = list.get(0);
+        mServiceExitReason = info.getReason();
+        mServiceExitSubReason = info.getSubReason();
+        if (mVerbose) {
+            Slog.v(mTag, "updateServicelicationExitInfo: exitReason="
+                    + ApplicationExitInfo.reasonCodeToString(mServiceExitReason)
+                    + " exitSubReason= " + ApplicationExitInfo.subreasonToString(
+                    mServiceExitSubReason));
+        }
+    }
+
     // Note: we are dumping without a lock held so this is a bit racy but
     // adding a lock to a class that offloads to a handler thread would
     // mean adding a lock adding overhead to normal runtime operation.
@@ -272,6 +311,16 @@
             }
         }
         pw.println();
+        if (mServiceExitReason != SERVICE_NOT_EXIST) {
+            pw.append(prefix).append(tab).append("serviceExistReason=")
+                    .append(ApplicationExitInfo.reasonCodeToString(mServiceExitReason));
+            pw.println();
+        }
+        if (mServiceExitSubReason != SERVICE_NOT_EXIST) {
+            pw.append(prefix).append(tab).append("serviceExistSubReason=")
+                    .append(ApplicationExitInfo.subreasonToString(mServiceExitSubReason));
+            pw.println();
+        }
         pw.append(prefix).append("mBindingFlags=").println(mBindingFlags);
         pw.append(prefix).append("idleTimeout=")
             .append(Long.toString(idleTimeout / 1000)).append("s\n");
@@ -498,6 +547,8 @@
                 return;
             }
             mService = getServiceInterface(service);
+            mServiceExitReason = SERVICE_NOT_EXIST;
+            mServiceExitSubReason = SERVICE_NOT_EXIST;
             handleOnConnectedStateChangedInternal(true);
             mServiceDied = false;
         }
diff --git a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java
index a03bac4..90ad34d 100644
--- a/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java
+++ b/core/java/com/android/internal/os/SystemServerClassLoaderFactory.java
@@ -87,6 +87,10 @@
         if (isTestOnly) {
             return true;
         }
+        // If system server is being profiled, it's OK to create class loaders anytime.
+        if (ZygoteInit.shouldProfileSystemServer()) {
+            return true;
+        }
         return false;
     }
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 73fb7fe..076e4e1 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -238,6 +238,21 @@
         Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
     }
 
+    private static boolean isExperimentEnabled(String experiment) {
+        boolean defaultValue = SystemProperties.getBoolean(
+                "dalvik.vm." + experiment,
+                /*def=*/false);
+        // Can't use device_config since we are the zygote, and it's not initialized at this point.
+        return SystemProperties.getBoolean(
+                "persist.device_config." + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
+                        + "." + experiment,
+                defaultValue);
+    }
+
+    /* package-private */ static boolean shouldProfileSystemServer() {
+        return isExperimentEnabled("profilesystemserver");
+    }
+
     /**
      * Performs Zygote process initialization. Loads and initializes commonly used classes.
      *
@@ -341,14 +356,7 @@
             // If we are profiling the boot image, reset the Jit counters after preloading the
             // classes. We want to preload for performance, and we can use method counters to
             // infer what clases are used after calling resetJitCounters, for profile purposes.
-            // Can't use device_config since we are the zygote.
-            String prop = SystemProperties.get(
-                    "persist.device_config.runtime_native_boot.profilebootclasspath", "");
-            // Might be empty if the property is unset since the default is "".
-            if (prop.length() == 0) {
-                prop = SystemProperties.get("dalvik.vm.profilebootclasspath", "");
-            }
-            if ("true".equals(prop)) {
+            if (isExperimentEnabled("profilebootclasspath")) {
                 Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ResetJitCounters");
                 VMRuntime.resetJitCounters();
                 Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
@@ -489,16 +497,6 @@
         ZygoteHooks.gcAndFinalize();
     }
 
-    private static boolean shouldProfileSystemServer() {
-        boolean defaultValue = SystemProperties.getBoolean("dalvik.vm.profilesystemserver",
-                /*default=*/ false);
-        // Can't use DeviceConfig since it's not initialized at this point.
-        return SystemProperties.getBoolean(
-                "persist.device_config." + DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT
-                        + ".profilesystemserver",
-                defaultValue);
-    }
-
     /**
      * Finish remaining work for the newly forked system server process.
      */
@@ -585,6 +583,13 @@
      * in the forked system server process in the zygote SELinux domain.
      */
     private static void prefetchStandaloneSystemServerJars() {
+        if (shouldProfileSystemServer()) {
+            // We don't prefetch AOT artifacts if we are profiling system server, as we are going to
+            // JIT it.
+            // This method only gets called from native and should already be skipped if we profile
+            // system server. Still, be robust and check it again.
+            return;
+        }
         String envStr = Os.getenv("STANDALONE_SYSTEMSERVER_JARS");
         if (TextUtils.isEmpty(envStr)) {
             return;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 550259f..664e964 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -343,6 +343,7 @@
 // Must match values in com.android.internal.os.Zygote.
 enum RuntimeFlags : uint32_t {
     DEBUG_ENABLE_JDWP = 1,
+    PROFILE_SYSTEM_SERVER = 1 << 14,
     PROFILE_FROM_SHELL = 1 << 15,
     MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
     MEMORY_TAG_LEVEL_TBI = 1 << 19,
@@ -1821,9 +1822,11 @@
                                            instruction_set.value().c_str());
     }
 
-    if (is_system_server) {
+    if (is_system_server && !(runtime_flags & RuntimeFlags::PROFILE_SYSTEM_SERVER)) {
         // Prefetch the classloader for the system server. This is done early to
         // allow a tie-down of the proper system server selinux domain.
+        // We don't prefetch when the system server is being profiled to avoid
+        // loading AOT code.
         env->CallStaticObjectMethod(gZygoteInitClass, gGetOrCreateSystemServerClassLoader);
         if (env->ExceptionCheck()) {
             // Be robust here. The Java code will attempt to create the classloader
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8fde9d5..b5cdcff 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2612,6 +2612,9 @@
          movement threshold under which hover is considered "stationary". -->
     <dimen name="config_viewConfigurationHoverSlop">4dp</dimen>
 
+    <!-- Margin around text line bounds where stylus handwriting gestures should be supported. -->
+    <dimen name="config_viewConfigurationHandwritingGestureLineMargin">16dp</dimen>
+
     <!-- Multiplier for gesture thresholds when a MotionEvent classification is ambiguous. -->
     <item name="config_ambiguousGestureMultiplier" format="float" type="dimen">2.0</item>
 
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index 89741ef..d03d206 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -131,6 +131,8 @@
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01ca0000">
+    <!-- @hide @SystemApi -->
+    <public name="config_viewConfigurationHandwritingGestureLineMargin" />
   </staging-public-group>
 
   <staging-public-group type="color" first-id="0x01c90000">
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index 613eddd..88b2de7 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -207,7 +207,7 @@
 
         assertFalse("Must not report change if no public diff",
                 shouldReportChange(currentConfig, newConfig, null /* sizeBuckets */,
-                        0 /* handledConfigChanges */));
+                        0 /* handledConfigChanges */, false /* alwaysReportChange */));
 
         final int[] verticalThresholds = {100, 400};
         final SizeConfigurationBuckets buckets = new SizeConfigurationBuckets(
@@ -221,24 +221,33 @@
 
         assertFalse("Must not report changes if the diff is small and not handled",
                 shouldReportChange(currentConfig, newConfig, buckets,
-                        CONFIG_FONT_SCALE /* handledConfigChanges */));
+                        CONFIG_FONT_SCALE /* handledConfigChanges */,
+                        false /* alwaysReportChange */));
 
         assertTrue("Must report changes if the small diff is handled",
                 shouldReportChange(currentConfig, newConfig, buckets,
-                        CONFIG_SCREEN_SIZE /* handledConfigChanges */));
+                        CONFIG_SCREEN_SIZE /* handledConfigChanges */,
+                        false /* alwaysReportChange */));
+
+        assertTrue("Must report changes if it should, even it is small and not handled",
+                shouldReportChange(currentConfig, newConfig, buckets,
+                        CONFIG_FONT_SCALE /* handledConfigChanges */,
+                        true /* alwaysReportChange */));
 
         currentConfig.fontScale = 0.8f;
         newConfig.fontScale = 1.2f;
 
         assertTrue("Must report handled changes regardless of small unhandled change",
                 shouldReportChange(currentConfig, newConfig, buckets,
-                        CONFIG_FONT_SCALE /* handledConfigChanges */));
+                        CONFIG_FONT_SCALE /* handledConfigChanges */,
+                        false /* alwaysReportChange */));
 
         newConfig.screenHeightDp = 500;
 
         assertFalse("Must not report changes if there's unhandled big changes",
                 shouldReportChange(currentConfig, newConfig, buckets,
-                        CONFIG_FONT_SCALE /* handledConfigChanges */));
+                        CONFIG_FONT_SCALE /* handledConfigChanges */,
+                        false /* alwaysReportChange */));
     }
 
     private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index b1c9730..51b976b 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -289,6 +289,7 @@
     private Integer mFrontendHandle;
     private Tuner mFeOwnerTuner = null;
     private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
+    private Integer mDesiredFrontendId = null;
     private int mUserId;
     private Lnb mLnb;
     private Integer mLnbHandle;
@@ -1326,10 +1327,18 @@
 
     private boolean requestFrontend() {
         int[] feHandle = new int[1];
-        TunerFrontendRequest request = new TunerFrontendRequest();
-        request.clientId = mClientId;
-        request.frontendType = mFrontendType;
-        boolean granted = mTunerResourceManager.requestFrontend(request, feHandle);
+        boolean granted = false;
+        try {
+            TunerFrontendRequest request = new TunerFrontendRequest();
+            request.clientId = mClientId;
+            request.frontendType = mFrontendType;
+            request.desiredId = mDesiredFrontendId == null
+                    ? TunerFrontendRequest.DEFAULT_DESIRED_ID
+                    : mDesiredFrontendId;
+            granted = mTunerResourceManager.requestFrontend(request, feHandle);
+        } finally {
+            mDesiredFrontendId = null;
+        }
         if (granted) {
             mFrontendHandle = feHandle[0];
             mFrontend = nativeOpenFrontendByHandle(mFrontendHandle);
@@ -2367,6 +2376,50 @@
     }
 
     /**
+     * Request a frontend by frontend id.
+     *
+     * <p> This API is used if the applications want to select a desired frontend before
+     * {@link tune} to use a specific satellite or sending SatCR DiSEqC command for {@link tune}.
+     *
+     * @param desiredId the desired fronted Id. It can be retrieved by
+     * {@link getAvailableFrontendInfos}
+     *
+     * @return result status of open operation.
+     * @throws SecurityException if the caller does not have appropriate permissions.
+     */
+    @Result
+    public int requestFrontendById(int desiredId) {
+        mFrontendLock.lock();
+        try {
+            if (mFeOwnerTuner != null) {
+                Log.e(TAG, "Operation connot be done by sharee of tuner");
+                return RESULT_INVALID_STATE;
+            }
+            if (mFrontendHandle != null) {
+                Log.e(TAG, "A frontend has been opened before");
+                return RESULT_INVALID_STATE;
+            }
+            FrontendInfo frontendInfo = getFrontendInfoById(desiredId);
+            if (frontendInfo == null) {
+                Log.e(TAG, "Failed to get a FrontendInfo by frontend id: " + desiredId);
+                return RESULT_UNAVAILABLE;
+            }
+            int frontendType = frontendInfo.getType();
+            if (DEBUG) {
+                Log.d(TAG, "Opening frontend with type " + frontendType + ", id " + desiredId);
+            }
+            mFrontendType = frontendType;
+            mDesiredFrontendId = desiredId;
+            if (!checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, mFrontendLock)) {
+                return RESULT_UNAVAILABLE;
+            }
+            return RESULT_SUCCESS;
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
      * Open a shared filter instance.
      *
      * @param context the context of the caller.
@@ -2445,7 +2498,7 @@
         return granted;
     }
 
-    private boolean checkResource(int resourceType, ReentrantLock localLock)  {
+    private boolean checkResource(int resourceType, ReentrantLock localLock) {
         switch (resourceType) {
             case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: {
                 if (mFrontendHandle == null && !requestResource(resourceType, localLock)) {
@@ -2483,7 +2536,7 @@
     // 3) if no, then first release the held lock and grab the TRMS lock to avoid deadlock
     // 4) grab the local lock again and release the TRMS lock
     // If localLock is null, we'll assume the caller does not want the lock related operations
-    private boolean requestResource(int resourceType, ReentrantLock localLock)  {
+    private boolean requestResource(int resourceType, ReentrantLock localLock) {
         boolean enableLockOperations = localLock != null;
 
         // release the local lock first to avoid deadlock
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
index 144b98c..e0af76d 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl
@@ -140,19 +140,29 @@
     void setLnbInfoList(in int[] lnbIds);
 
     /*
-     * This API is used by the Tuner framework to request an available frontend from the TunerHAL.
+     * This API is used by the Tuner framework to request a frontend from the TunerHAL.
      *
-     * <p>There are three possible scenarios:
+     * <p>There are two cases:
      * <ul>
-     * <li>If there is frontend available, the API would send the id back.
-     *
-     * <li>If no Frontend is available but the current request info can show higher priority than
-     * other uses of Frontend, the API will send
+     * <li>If the desiredId is not {@link TunerFrontendRequest#DEFAULT_DESIRED_ID}
+     * <li><li>If the desired frontend with the given frontendType is available, the API would send
+     * the id back.
+     * <li><li>If the desired frontend with the given frontendType is in use but the current request
+     * info can show higher priority than other uses of Frontend, the API will send
      * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
      * handle the resource reclaim on the holder of lower priority and notify the holder of its
      * resource loss.
+     * <li><li>If no frontend can be granted, the API would return false.
+     * <ul>
      *
-     * <li>If no frontend can be granted, the API would return false.
+     * <li>If the desiredId is {@link TunerFrontendRequest#DEFAULT_DESIRED_ID}
+     * <li><li>If there is frontend available, the API would send the id back.
+     * <li><li>If no Frontend is available but the current request info can show higher priority
+     * than other uses of Frontend, the API will send
+     * {@link IResourcesReclaimListener#onReclaimResources()} to the {@link Tuner}. Tuner would
+     * handle the resource reclaim on the holder of lower priority and notify the holder of its
+     * resource loss.
+     * <li><li>If no frontend can be granted, the API would return false.
      * <ul>
      *
      * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called
diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
index 4d98222..c4598a4 100644
--- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
+++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/TunerFrontendRequest.aidl
@@ -22,7 +22,11 @@
  * @hide
  */
 parcelable TunerFrontendRequest {
+    const int DEFAULT_DESIRED_ID = 0xFFFFFFFF;
+
     int clientId;
 
     int frontendType;
-}
\ No newline at end of file
+
+    int desiredId = DEFAULT_DESIRED_ID;
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
index c575fbc..489cc27 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -16,6 +16,7 @@
 
 package com.android.credentialmanager
 
+import android.app.Activity
 import android.app.slice.Slice
 import android.app.slice.SliceSpec
 import android.content.Context
@@ -23,8 +24,11 @@
 import android.credentials.ui.Entry
 import android.credentials.ui.ProviderData
 import android.credentials.ui.RequestInfo
+import android.credentials.ui.UserSelectionResult
 import android.graphics.drawable.Icon
 import android.os.Binder
+import android.os.Bundle
+import android.os.ResultReceiver
 import com.android.credentialmanager.createflow.CreatePasskeyUiState
 import com.android.credentialmanager.createflow.CreateScreenState
 import com.android.credentialmanager.getflow.GetCredentialUiState
@@ -37,6 +41,8 @@
 ) {
   private val requestInfo: RequestInfo
   private val providerList: List<ProviderData>
+  // TODO: require non-null.
+  val resultReceiver: ResultReceiver?
 
   init {
     requestInfo = intent.extras?.getParcelable(
@@ -52,6 +58,29 @@
       ProviderData.EXTRA_PROVIDER_DATA_LIST,
       ProviderData::class.java
     ) ?: testProviderList()
+
+    resultReceiver = intent.getParcelableExtra(
+      RequestInfo.EXTRA_RESULT_RECEIVER,
+      ResultReceiver::class.java
+    )
+  }
+
+  fun onCancel() {
+    resultReceiver?.send(Activity.RESULT_CANCELED, null)
+  }
+
+  fun onOptionSelected(providerPackageName: String, entryId: Int) {
+    val userSelectionResult = UserSelectionResult(
+      requestInfo.token,
+      providerPackageName,
+      entryId
+    )
+    val resultData = Bundle()
+    resultData.putParcelable(
+      UserSelectionResult.EXTRA_USER_SELECTION_RESULT,
+      userSelectionResult
+    )
+    resultReceiver?.send(Activity.RESULT_OK, resultData)
   }
 
   fun getCredentialInitialUiState(): GetCredentialUiState {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index b538ae7..78edaa9 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -23,9 +23,15 @@
 import androidx.activity.compose.setContent
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.runtime.Composable
+import androidx.lifecycle.Observer
+import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.credentialmanager.common.DialogType
+import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ResultState
 import com.android.credentialmanager.createflow.CreatePasskeyScreen
+import com.android.credentialmanager.createflow.CreatePasskeyViewModel
 import com.android.credentialmanager.getflow.GetCredentialScreen
+import com.android.credentialmanager.getflow.GetCredentialViewModel
 import com.android.credentialmanager.ui.theme.CredentialSelectorTheme
 
 @ExperimentalMaterialApi
@@ -57,10 +63,20 @@
     val dialogType = DialogType.toDialogType(operationType)
     when (dialogType) {
       DialogType.CREATE_PASSKEY -> {
-        CreatePasskeyScreen(cancelActivity = onCancel)
+        val viewModel: CreatePasskeyViewModel = viewModel()
+        viewModel.observeDialogResult().observe(
+          this@CredentialSelectorActivity,
+          onCancel
+        )
+        CreatePasskeyScreen(viewModel = viewModel)
       }
       DialogType.GET_CREDENTIALS -> {
-        GetCredentialScreen(cancelActivity = onCancel)
+        val viewModel: GetCredentialViewModel = viewModel()
+        viewModel.observeDialogResult().observe(
+          this@CredentialSelectorActivity,
+          onCancel
+        )
+        GetCredentialScreen(viewModel = viewModel)
       }
       else -> {
         Log.w("AccountSelector", "Unknown type, not rendering any UI")
@@ -69,7 +85,9 @@
     }
   }
 
-  private val onCancel = {
-    this@CredentialSelectorActivity.finish()
+  private val onCancel = Observer<DialogResult> {
+    if (it.resultState == ResultState.COMPLETE || it.resultState == ResultState.CANCELED) {
+      this@CredentialSelectorActivity.finish()
+    }
   }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index af27ce5..6b503ff 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -35,7 +35,7 @@
         ProviderInfo(
           // TODO: replace to extract from the service data structure when available
           icon = context.getDrawable(R.drawable.ic_passkey)!!,
-          name = it.packageName,
+          name = it.providerId,
           appDomainName = "tribank.us",
           credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
           credentialOptions = toCredentialOptionInfoList(it.credentialEntries, context)
@@ -78,7 +78,7 @@
         com.android.credentialmanager.createflow.ProviderInfo(
           // TODO: replace to extract from the service data structure when available
           icon = context.getDrawable(R.drawable.ic_passkey)!!,
-          name = it.packageName,
+          name = it.providerId,
           appDomainName = "tribank.us",
           credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
           createOptions = toCreationOptionInfoList(it.credentialEntries, context),
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
new file mode 100644
index 0000000..b751663
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package com.android.credentialmanager.common
+
+enum class ResultState {
+  COMPLETE,
+  CANCELED,
+}
+
+data class DialogResult(
+  val resultState: ResultState,
+)
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
index 6489d73..f4d60b5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
@@ -38,7 +38,6 @@
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.core.graphics.drawable.toBitmap
-import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.credentialmanager.R
 import com.android.credentialmanager.ui.theme.Grey100
 import com.android.credentialmanager.ui.theme.Shapes
@@ -50,8 +49,7 @@
 @ExperimentalMaterialApi
 @Composable
 fun CreatePasskeyScreen(
-  viewModel: CreatePasskeyViewModel = viewModel(),
-  cancelActivity: () -> Unit,
+  viewModel: CreatePasskeyViewModel,
 ) {
   val state = rememberModalBottomSheetState(
     initialValue = ModalBottomSheetValue.Expanded,
@@ -64,17 +62,17 @@
       when (uiState.currentScreenState) {
         CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
           onConfirm = {viewModel.onConfirmIntro()},
-          onCancel = cancelActivity,
+          onCancel = {viewModel.onCancel()},
         )
         CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
           providerList = uiState.providers,
-          onCancel = cancelActivity,
+          onCancel = {viewModel.onCancel()},
           onProviderSelected = {viewModel.onProviderSelected(it)}
         )
         CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
           providerInfo = uiState.selectedProvider!!,
           onOptionSelected = {viewModel.onCreateOptionSelected(it)},
-          onCancel = cancelActivity,
+          onCancel = {viewModel.onCancel()},
           multiProvider = uiState.providers.size > 1,
           onMoreOptionsSelected = {viewModel.onMoreOptionsSelected(it)}
         )
@@ -93,7 +91,7 @@
   ) {}
   LaunchedEffect(state.currentValue) {
     if (state.currentValue == ModalBottomSheetValue.Hidden) {
-      cancelActivity()
+      viewModel.onCancel()
     }
   }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
index 85fe31d..3cf81da 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
@@ -20,8 +20,12 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.android.credentialmanager.CredentialManagerRepo
+import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ResultState
 
 data class CreatePasskeyUiState(
   val providers: List<ProviderInfo>,
@@ -36,6 +40,14 @@
   var uiState by mutableStateOf(credManRepo.createPasskeyInitialUiState())
     private set
 
+  val dialogResult: MutableLiveData<DialogResult> by lazy {
+    MutableLiveData<DialogResult>()
+  }
+
+  fun observeDialogResult(): LiveData<DialogResult> {
+    return dialogResult
+  }
+
   fun onConfirmIntro() {
     if (uiState.providers.size > 1) {
       uiState = uiState.copy(
@@ -60,6 +72,13 @@
 
   fun onCreateOptionSelected(createOptionId: Int) {
     Log.d("Account Selector", "Option selected for creation: $createOptionId")
+    CredentialManagerRepo.getInstance().onOptionSelected(
+      uiState.selectedProvider!!.name,
+      createOptionId
+    )
+    dialogResult.value = DialogResult(
+      ResultState.COMPLETE,
+    )
   }
 
   fun getProviderInfoByName(providerName: String): ProviderInfo {
@@ -88,4 +107,9 @@
       selectedProvider = getProviderInfoByName(providerName)
     )
   }
+
+  fun onCancel() {
+    CredentialManagerRepo.getInstance().onCancel()
+    dialogResult.value = DialogResult(ResultState.CANCELED)
+  }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 0b18822..48c67bb 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -44,7 +44,6 @@
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import androidx.core.graphics.drawable.toBitmap
-import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.credentialmanager.R
 import com.android.credentialmanager.createflow.CancelButton
 import com.android.credentialmanager.ui.theme.Grey100
@@ -55,8 +54,7 @@
 @ExperimentalMaterialApi
 @Composable
 fun GetCredentialScreen(
-  viewModel: GetCredentialViewModel = viewModel(),
-  cancelActivity: () -> Unit,
+  viewModel: GetCredentialViewModel,
 ) {
   val state = rememberModalBottomSheetState(
     initialValue = ModalBottomSheetValue.Expanded,
@@ -69,7 +67,7 @@
       when (uiState.currentScreenState) {
         GetScreenState.CREDENTIAL_SELECTION -> CredentialSelectionCard(
           providerInfo = uiState.selectedProvider!!,
-          onCancel = cancelActivity,
+          onCancel = {viewModel.onCancel()},
           onOptionSelected = {viewModel.onCredentailSelected(it)},
           multiProvider = uiState.providers.size > 1,
           onMoreOptionSelected = {viewModel.onMoreOptionSelected()},
@@ -81,7 +79,7 @@
   ) {}
   LaunchedEffect(state.currentValue) {
     if (state.currentValue == ModalBottomSheetValue.Hidden) {
-      cancelActivity()
+      viewModel.onCancel()
     }
   }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
index 0fdd8ec..33858f5 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -20,8 +20,12 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import com.android.credentialmanager.CredentialManagerRepo
+import com.android.credentialmanager.common.DialogResult
+import com.android.credentialmanager.common.ResultState
 
 data class GetCredentialUiState(
   val providers: List<ProviderInfo>,
@@ -36,11 +40,31 @@
   var uiState by mutableStateOf(credManRepo.getCredentialInitialUiState())
       private set
 
+  val dialogResult: MutableLiveData<DialogResult> by lazy {
+    MutableLiveData<DialogResult>()
+  }
+
+  fun observeDialogResult(): LiveData<DialogResult> {
+    return dialogResult
+  }
+
   fun onCredentailSelected(credentialId: Int) {
     Log.d("Account Selector", "credential selected: $credentialId")
+    CredentialManagerRepo.getInstance().onOptionSelected(
+      uiState.selectedProvider!!.name,
+      credentialId
+    )
+    dialogResult.value = DialogResult(
+      ResultState.COMPLETE,
+    )
   }
 
   fun onMoreOptionSelected() {
     Log.d("Account Selector", "More Option selected")
   }
+
+  fun onCancel() {
+    CredentialManagerRepo.getInstance().onCancel()
+    dialogResult.value = DialogResult(ResultState.CANCELED)
+  }
 }
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
index 0f95bf6..7567c6d 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt
@@ -33,8 +33,8 @@
 import com.android.settingslib.spa.framework.common.SettingsPageProvider
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.SettingsSlider
-import com.android.settingslib.spa.widget.SettingsSliderModel
+import com.android.settingslib.spa.widget.preference.SliderPreference
+import com.android.settingslib.spa.widget.preference.SliderPreferenceModel
 import com.android.settingslib.spa.widget.preference.Preference
 import com.android.settingslib.spa.widget.preference.PreferenceModel
 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
@@ -51,7 +51,7 @@
             SettingsEntryBuilder.create("Simple Slider", owner)
                 .setIsAllowSearch(true)
                 .setUiLayoutFn {
-                    SettingsSlider(object : SettingsSliderModel {
+                    SliderPreference(object : SliderPreferenceModel {
                         override val title = "Simple Slider"
                         override val initValue = 40
                     })
@@ -61,7 +61,7 @@
             SettingsEntryBuilder.create("Slider with icon", owner)
                 .setIsAllowSearch(true)
                 .setUiLayoutFn {
-                    SettingsSlider(object : SettingsSliderModel {
+                    SliderPreference(object : SliderPreferenceModel {
                         override val title = "Slider with icon"
                         override val initValue = 30
                         override val onValueChangeFinished = {
@@ -78,7 +78,7 @@
                     val initValue = 0
                     var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) }
                     var sliderPosition by remember { mutableStateOf(initValue) }
-                    SettingsSlider(object : SettingsSliderModel {
+                    SliderPreference(object : SliderPreferenceModel {
                         override val title = "Slider with changeable icon"
                         override val initValue = initValue
                         override val onValueChange = { it: Int ->
@@ -96,7 +96,7 @@
             SettingsEntryBuilder.create("Slider with steps", owner)
                 .setIsAllowSearch(true)
                 .setUiLayoutFn {
-                    SettingsSlider(object : SettingsSliderModel {
+                    SliderPreference(object : SliderPreferenceModel {
                         override val title = "Slider with steps"
                         override val initValue = 2
                         override val valueRange = 1..5
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index a3aeda6..8616b9f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
@@ -17,15 +17,11 @@
 package com.android.settingslib.spa.framework.common
 
 import android.os.Bundle
-import android.widget.Toast
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.ProvidedValue
 import androidx.compose.runtime.compositionLocalOf
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.platform.LocalContext
 import com.android.settingslib.spa.framework.compose.LocalNavController
 
 const val INJECT_ENTRY_NAME = "INJECT"
@@ -126,17 +122,6 @@
 
     @Composable
     fun UiLayout(runtimeArguments: Bundle? = null) {
-        val context = LocalContext.current
-        val controller = LocalNavController.current
-        val highlight = rememberSaveable {
-            mutableStateOf(controller.highlightEntryId == id)
-        }
-        if (highlight.value) {
-            highlight.value = false
-            // TODO: Add highlight entry logic
-            Toast.makeText(context, "entry $id highlighted", Toast.LENGTH_SHORT).show()
-        }
-
         CompositionLocalProvider(provideLocalEntryData()) {
             uiLayoutImpl(fullArgument(runtimeArguments))
         }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt
new file mode 100644
index 0000000..b23f4e0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/TimeMeasurer.kt
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalTime::class)
+
+package com.android.settingslib.spa.framework.compose
+
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import kotlin.time.ExperimentalTime
+import kotlin.time.TimeSource
+
+const val ENABLE_MEASURE_TIME = false
+
+interface TimeMeasurer {
+    fun log(msg: String) {}
+    fun logFirst(msg: String) {}
+
+    companion object {
+        private object EmptyTimeMeasurer : TimeMeasurer
+
+        @Composable
+        fun rememberTimeMeasurer(tag: String): TimeMeasurer = remember {
+            if (ENABLE_MEASURE_TIME) TimeMeasurerImpl(tag) else EmptyTimeMeasurer
+        }
+    }
+}
+
+private class TimeMeasurerImpl(private val tag: String) : TimeMeasurer {
+    private val mark = TimeSource.Monotonic.markNow()
+    private val msgLogged = mutableSetOf<String>()
+
+    override fun log(msg: String) {
+        Log.d(tag, "Timer $msg: ${mark.elapsedNow()}")
+    }
+
+    override fun logFirst(msg: String) {
+        if (msgLogged.add(msg)) {
+            Log.d(tag, "Timer $msg: ${mark.elapsedNow()}")
+        }
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt
new file mode 100644
index 0000000..8e24ce0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/EntryHighlight.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.spa.framework.util
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import com.android.settingslib.spa.framework.common.LocalEntryDataProvider
+
+@Composable
+internal fun EntryHighlight(UiLayoutFn: @Composable () -> Unit) {
+    val entryData = LocalEntryDataProvider.current
+    val isHighlighted = rememberSaveable { entryData.isHighlighted }
+    val backgroundColor =
+        if (isHighlighted) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent
+    Box(modifier = Modifier.background(color = backgroundColor)) {
+        UiLayoutFn()
+    }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index f2fe7ad7..3e04b16 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -28,26 +28,29 @@
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsShape
 import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.EntryHighlight
 
 @Composable
 fun MainSwitchPreference(model: SwitchPreferenceModel) {
-    Surface(
-        modifier = Modifier.padding(SettingsDimension.itemPaddingEnd),
-        color = when (model.checked.value) {
-            true -> MaterialTheme.colorScheme.primaryContainer
-            else -> MaterialTheme.colorScheme.secondaryContainer
-        },
-        shape = SettingsShape.CornerLarge,
-    ) {
-        InternalSwitchPreference(
-            title = model.title,
-            checked = model.checked,
-            changeable = model.changeable,
-            onCheckedChange = model.onCheckedChange,
-            paddingStart = 20.dp,
-            paddingEnd = 20.dp,
-            paddingVertical = 18.dp,
-        )
+    EntryHighlight {
+        Surface(
+            modifier = Modifier.padding(SettingsDimension.itemPaddingEnd),
+            color = when (model.checked.value) {
+                true -> MaterialTheme.colorScheme.primaryContainer
+                else -> MaterialTheme.colorScheme.secondaryContainer
+            },
+            shape = SettingsShape.CornerLarge,
+        ) {
+            InternalSwitchPreference(
+                title = model.title,
+                checked = model.checked,
+                changeable = model.changeable,
+                onCheckedChange = model.onCheckedChange,
+                paddingStart = 20.dp,
+                paddingEnd = 20.dp,
+                paddingVertical = 18.dp,
+            )
+        }
     }
 }
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index d1021e2..7c0116a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -27,6 +27,7 @@
 import com.android.settingslib.spa.framework.compose.navigator
 import com.android.settingslib.spa.framework.compose.stateOf
 import com.android.settingslib.spa.framework.util.WrapOnClickWithLog
+import com.android.settingslib.spa.framework.util.EntryHighlight
 import com.android.settingslib.spa.widget.ui.createSettingsIcon
 
 data class SimplePreferenceMacro(
@@ -115,12 +116,14 @@
             )
         } else Modifier
     }
-    BasePreference(
-        title = model.title,
-        summary = model.summary,
-        singleLineSummary = singleLineSummary,
-        modifier = modifier,
-        icon = model.icon,
-        enabled = model.enabled,
-    )
+    EntryHighlight {
+        BasePreference(
+            title = model.title,
+            summary = model.summary,
+            singleLineSummary = singleLineSummary,
+            modifier = modifier,
+            icon = model.icon,
+            enabled = model.enabled,
+        )
+    }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
similarity index 68%
rename from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt
rename to packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
index 4f77a89..7bca38f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.spa.widget
+package com.android.settingslib.spa.widget.preference
 
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AccessAlarm
 import androidx.compose.material.icons.outlined.MusicNote
 import androidx.compose.material.icons.outlined.MusicOff
 import androidx.compose.material3.Icon
-import androidx.compose.material3.Slider
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -32,32 +31,32 @@
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.tooling.preview.Preview
 import com.android.settingslib.spa.framework.theme.SettingsTheme
-import com.android.settingslib.spa.widget.preference.BaseLayout
-import kotlin.math.roundToInt
+import com.android.settingslib.spa.framework.util.EntryHighlight
+import com.android.settingslib.spa.widget.ui.SettingsSlider
 
 /**
- * The widget model for [SettingsSlider] widget.
+ * The widget model for [SliderPreference] widget.
  */
-interface SettingsSliderModel {
+interface SliderPreferenceModel {
     /**
-     * The title of this [SettingsSlider].
+     * The title of this [SliderPreference].
      */
     val title: String
 
     /**
-     * The initial position of the [SettingsSlider].
+     * The initial position of the [SliderPreference].
      */
     val initValue: Int
 
     /**
-     * The value range for this [SettingsSlider].
+     * The value range for this [SliderPreference].
      */
     val valueRange: IntRange
         get() = 0..100
 
     /**
      * The lambda to be invoked during the value change by dragging or a click. This callback is
-     * used to get the real time value of the [SettingsSlider].
+     * used to get the real time value of the [SliderPreference].
      */
     val onValueChange: ((value: Int) -> Unit)?
         get() = null
@@ -70,7 +69,7 @@
         get() = null
 
     /**
-     * The icon image for [SettingsSlider]. If not specified, the slider hides the icon by default.
+     * The icon image for [SliderPreference]. If not specified, the slider hides the icon by default.
      */
     val icon: ImageVector?
         get() = null
@@ -89,46 +88,44 @@
 /**
  * Settings slider widget.
  *
- * Data is provided through [SettingsSliderModel].
+ * Data is provided through [SliderPreferenceModel].
  */
 @Composable
-fun SettingsSlider(model: SettingsSliderModel) {
-    SettingsSlider(
-        title = model.title,
-        initValue = model.initValue,
-        valueRange = model.valueRange,
-        onValueChange = model.onValueChange,
-        onValueChangeFinished = model.onValueChangeFinished,
-        icon = model.icon,
-        showSteps = model.showSteps,
-    )
+fun SliderPreference(model: SliderPreferenceModel) {
+    EntryHighlight {
+        SliderPreference(
+            title = model.title,
+            initValue = model.initValue,
+            valueRange = model.valueRange,
+            onValueChange = model.onValueChange,
+            onValueChangeFinished = model.onValueChangeFinished,
+            icon = model.icon,
+            showSteps = model.showSteps,
+        )
+    }
 }
 
 @Composable
-internal fun SettingsSlider(
+internal fun SliderPreference(
     title: String,
     initValue: Int,
+    modifier: Modifier = Modifier,
     valueRange: IntRange = 0..100,
     onValueChange: ((value: Int) -> Unit)? = null,
     onValueChangeFinished: (() -> Unit)? = null,
     icon: ImageVector? = null,
     showSteps: Boolean = false,
-    modifier: Modifier = Modifier,
 ) {
-    var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) }
     BaseLayout(
         title = title,
         subTitle = {
-            Slider(
-                value = sliderPosition,
-                onValueChange = {
-                    sliderPosition = it
-                    onValueChange?.invoke(sliderPosition.roundToInt())
-                },
-                modifier = modifier,
-                valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(),
-                steps = if (showSteps) (valueRange.count() - 2) else 0,
-                onValueChangeFinished = onValueChangeFinished,
+            SettingsSlider(
+                initValue,
+                modifier,
+                valueRange,
+                onValueChange,
+                onValueChangeFinished,
+                showSteps
             )
         },
         icon = if (icon != null) ({
@@ -139,11 +136,11 @@
 
 @Preview
 @Composable
-private fun SettingsSliderPreview() {
+private fun SliderPreferencePreview() {
     SettingsTheme {
         val initValue = 30
         var sliderPosition by rememberSaveable { mutableStateOf(initValue) }
-        SettingsSlider(
+        SliderPreference(
             title = "Alarm Volume",
             initValue = 30,
             onValueChange = { sliderPosition = it },
@@ -157,10 +154,10 @@
 
 @Preview
 @Composable
-private fun SettingsSliderIconChangePreview() {
+private fun SliderPreferenceIconChangePreview() {
     SettingsTheme {
         var icon by remember { mutableStateOf(Icons.Outlined.MusicNote) }
-        SettingsSlider(
+        SliderPreference(
             title = "Media Volume",
             initValue = 40,
             onValueChange = { it: Int ->
@@ -173,9 +170,9 @@
 
 @Preview
 @Composable
-private fun SettingsSliderStepsPreview() {
+private fun SliderPreferenceStepsPreview() {
     SettingsTheme {
-        SettingsSlider(
+        SliderPreference(
             title = "Display Text",
             initValue = 2,
             valueRange = 1..5,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
index 992ce9e..592a99f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -32,6 +32,7 @@
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.framework.theme.SettingsTheme
 import com.android.settingslib.spa.framework.util.WrapOnSwitchWithLog
+import com.android.settingslib.spa.framework.util.EntryHighlight
 import com.android.settingslib.spa.widget.ui.SettingsSwitch
 
 /**
@@ -79,13 +80,15 @@
  */
 @Composable
 fun SwitchPreference(model: SwitchPreferenceModel) {
-    InternalSwitchPreference(
-        title = model.title,
-        summary = model.summary,
-        checked = model.checked,
-        changeable = model.changeable,
-        onCheckedChange = model.onCheckedChange,
-    )
+    EntryHighlight {
+        InternalSwitchPreference(
+            title = model.title,
+            summary = model.summary,
+            checked = model.checked,
+            changeable = model.changeable,
+            onCheckedChange = model.onCheckedChange,
+        )
+    }
 }
 
 @Composable
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
index f1541b7..63de2c8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
@@ -17,6 +17,7 @@
 package com.android.settingslib.spa.widget.preference
 
 import androidx.compose.runtime.Composable
+import com.android.settingslib.spa.framework.util.EntryHighlight
 import com.android.settingslib.spa.widget.ui.SettingsSwitch
 
 @Composable
@@ -25,16 +26,18 @@
     icon: @Composable (() -> Unit)? = null,
     onClick: () -> Unit,
 ) {
-    TwoTargetPreference(
-        title = model.title,
-        summary = model.summary,
-        onClick = onClick,
-        icon = icon,
-    ) {
-        SettingsSwitch(
-            checked = model.checked,
-            changeable = model.changeable,
-            onCheckedChange = model.onCheckedChange,
-        )
+    EntryHighlight {
+        TwoTargetPreference(
+            title = model.title,
+            summary = model.summary,
+            onClick = onClick,
+            icon = icon,
+        ) {
+            SettingsSwitch(
+                checked = model.checked,
+                changeable = model.changeable,
+                onCheckedChange = model.onCheckedChange,
+            )
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
new file mode 100644
index 0000000..d8455e4
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package com.android.settingslib.spa.widget.ui
+
+import androidx.compose.material3.Slider
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import kotlin.math.roundToInt
+
+@Composable
+fun SettingsSlider(
+    initValue: Int,
+    modifier: Modifier = Modifier,
+    valueRange: IntRange = 0..100,
+    onValueChange: ((value: Int) -> Unit)? = null,
+    onValueChangeFinished: (() -> Unit)? = null,
+    showSteps: Boolean = false,
+) {
+    var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) }
+    Slider(
+        value = sliderPosition,
+        onValueChange = {
+            sliderPosition = it
+            onValueChange?.invoke(sliderPosition.roundToInt())
+        },
+        modifier = modifier,
+        valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(),
+        steps = if (showSteps) (valueRange.count() - 2) else 0,
+        onValueChangeFinished = onValueChangeFinished,
+    )
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt
similarity index 85%
rename from packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt
rename to packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt
index 1d95e33..7ae1175 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.settingslib.spa.widget
+package com.android.settingslib.spa.widget.preference
 
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
@@ -25,14 +25,14 @@
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
-class SettingsSliderTest {
+class SliderPreferenceTest {
     @get:Rule
     val composeTestRule = createComposeRule()
 
     @Test
     fun title_displayed() {
         composeTestRule.setContent {
-            SettingsSlider(object : SettingsSliderModel {
+            SliderPreference(object : SliderPreferenceModel {
                 override val title = "Slider"
                 override val initValue = 40
             })
@@ -41,5 +41,5 @@
         composeTestRule.onNodeWithText("Slider").assertIsDisplayed()
     }
 
-    // TODO: Add more unit tests for SettingsSlider widget.
+    // TODO: Add more unit tests for SliderPreference widget.
 }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 6318b4e..c5ad181 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -28,6 +28,7 @@
 import androidx.compose.ui.res.stringResource
 import androidx.lifecycle.viewmodel.compose.viewModel
 import com.android.settingslib.spa.framework.compose.LogCompositions
+import com.android.settingslib.spa.framework.compose.TimeMeasurer.Companion.rememberTimeMeasurer
 import com.android.settingslib.spa.framework.compose.toState
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.ui.PlaceholderTitle
@@ -66,7 +67,9 @@
     listModel: AppListModel<T>,
     appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
 ) {
+    val timeMeasurer = rememberTimeMeasurer(TAG)
     appListData.value?.let { (list, option) ->
+        timeMeasurer.logFirst("app list first loaded")
         if (list.isEmpty()) {
             PlaceholderTitle(stringResource(R.string.no_applications))
             return
diff --git a/packages/SettingsLib/search/stub-src/com/android/settingslib/search/SearchIndexableResourcesBase.java b/packages/SettingsLib/search/stub-src/com/android/settingslib/search/SearchIndexableResourcesBase.java
index 4870d45..4063b93 100644
--- a/packages/SettingsLib/search/stub-src/com/android/settingslib/search/SearchIndexableResourcesBase.java
+++ b/packages/SettingsLib/search/stub-src/com/android/settingslib/search/SearchIndexableResourcesBase.java
@@ -24,11 +24,11 @@
 public class SearchIndexableResourcesBase implements SearchIndexableResources {
 
     @Override
-    public Collection<Class> getProviderValues() {
+    public Collection<SearchIndexableData> getProviderValues() {
         throw new RuntimeException("STUB!");
     }
 
-    public void addIndex(Class indexClass) {
+    public void addIndex(SearchIndexableData indexClass) {
         throw new RuntimeException("STUB!");
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 5662ce6..6bc1160 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -356,7 +356,7 @@
      * @return {@code true}, if the device should pair automatically; Otherwise, return
      * {@code false}.
      */
-    public synchronized boolean shouldPairByCsip(BluetoothDevice device, int groupId) {
+    private synchronized boolean shouldPairByCsip(BluetoothDevice device, int groupId) {
         boolean isOngoingSetMemberPair = mOngoingSetMemberPair != null;
         int bondState = device.getBondState();
         if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE
@@ -365,13 +365,47 @@
                     + " , device.getBondState: " + bondState);
             return false;
         }
-
-        Log.d(TAG, "Bond " + device.getName() + " by CSIP");
-        mOngoingSetMemberPair = device;
         return true;
     }
 
     /**
+     * Called when we found a set member of a group. The function will check the {@code groupId} if
+     * it exists and the bond state of the device is BOND_NONE, and if there isn't any ongoing pair
+     * , and then pair the device automatically.
+     *
+     * @param device The found device
+     * @param groupId The group id of the found device
+     */
+    public synchronized void pairDeviceByCsip(BluetoothDevice device, int groupId) {
+        if (!shouldPairByCsip(device, groupId)) {
+            return;
+        }
+        Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " by CSIP");
+        mOngoingSetMemberPair = device;
+        syncConfigFromMainDevice(device, groupId);
+        device.createBond(BluetoothDevice.TRANSPORT_LE);
+    }
+
+    private void syncConfigFromMainDevice(BluetoothDevice device, int groupId) {
+        if (!isOngoingPairByCsip(device)) {
+            return;
+        }
+        CachedBluetoothDevice memberDevice = findDevice(device);
+        CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(memberDevice);
+        if (mainDevice == null) {
+            mainDevice = mCsipDeviceManager.getCachedDevice(groupId);
+        }
+
+        if (mainDevice == null || mainDevice.equals(memberDevice)) {
+            Log.d(TAG, "no mainDevice");
+            return;
+        }
+
+        // The memberDevice set PhonebookAccessPermission
+        device.setPhonebookAccessPermission(mainDevice.getDevice().getPhonebookAccessPermission());
+    }
+
+    /**
      * Called when the bond state change. If the bond state change is related with the
      * ongoing set member pair, the cachedBluetoothDevice will be created but the UI
      * would not be updated. For the other case, return {@code false} to go through the normal
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index d5de3f0..20a6cd8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -101,7 +101,14 @@
         return groupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
     }
 
-    private CachedBluetoothDevice getCachedDevice(int groupId) {
+    /**
+     * To find the device with {@code groupId}.
+     *
+     * @param groupId The group id
+     * @return if we could find a device with this {@code groupId} return this device. Otherwise,
+     * return null.
+     */
+    public CachedBluetoothDevice getCachedDevice(int groupId) {
         log("getCachedDevice: groupId: " + groupId);
         for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
             CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 62552f91..61802a8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -582,4 +582,24 @@
         assertThat(mCachedDeviceManager.isSubDevice(mDevice2)).isTrue();
         assertThat(mCachedDeviceManager.isSubDevice(mDevice3)).isFalse();
     }
+
+    @Test
+    public void pairDeviceByCsip_device2AndCapGroup1_device2StartsPairing() {
+        doReturn(CAP_GROUP1).when(mCsipSetCoordinatorProfile).getGroupUuidMapByDevice(mDevice1);
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice1.getPhonebookAccessPermission()).thenReturn(BluetoothDevice.ACCESS_ALLOWED);
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        int groupId = CAP_GROUP1.keySet().stream().findFirst().orElse(
+                BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+        assertThat(groupId).isNotEqualTo(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+        mCachedDeviceManager.pairDeviceByCsip(mDevice2, groupId);
+
+        verify(mDevice2).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
+        verify(mDevice2).createBond(BluetoothDevice.TRANSPORT_LE);
+    }
 }
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 6d61fd8..77ddc6e 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -83,7 +83,6 @@
 stwu@google.com
 syeonlee@google.com
 sunnygoyal@google.com
-susikp@google.com
 thiruram@google.com
 tracyzhou@google.com
 tsuji@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index a3351e1..5d52056 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -86,30 +86,38 @@
         onUpdate()
     }
 
-    fun onDisplayChanged(newDisplayUniqueId: String?) {
+    fun updateConfiguration(newDisplayUniqueId: String?) {
+        val info = DisplayInfo()
+        context.display?.getDisplayInfo(info)
         val oldMode: Display.Mode? = displayMode
-        val display: Display? = context.display
-        displayMode = display?.mode
+        displayMode = info.mode
 
-        if (displayUniqueId != display?.uniqueId) {
-            displayUniqueId = display?.uniqueId
-            shouldDrawCutout = DisplayCutout.getFillBuiltInDisplayCutout(
-                context.resources, displayUniqueId
-            )
-        }
+        updateDisplayUniqueId(info.uniqueId)
 
         // Skip if display mode or cutout hasn't changed.
         if (!displayModeChanged(oldMode, displayMode) &&
-                display?.cutout == displayInfo.displayCutout) {
+                displayInfo.displayCutout == info.displayCutout &&
+                displayRotation == info.rotation) {
             return
         }
-        if (newDisplayUniqueId == display?.uniqueId) {
+        if (newDisplayUniqueId == info.uniqueId) {
+            displayRotation = info.rotation
             updateCutout()
             updateProtectionBoundingPath()
             onUpdate()
         }
     }
 
+    open fun updateDisplayUniqueId(newDisplayUniqueId: String?) {
+        if (displayUniqueId != newDisplayUniqueId) {
+            displayUniqueId = newDisplayUniqueId
+            shouldDrawCutout = DisplayCutout.getFillBuiltInDisplayCutout(
+                    context.resources, displayUniqueId
+            )
+            invalidate()
+        }
+    }
+
     open fun updateRotation(rotation: Int) {
         displayRotation = rotation
         updateCutout()
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index b5f42a1..11d579d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -456,7 +456,6 @@
                     }
                 }
 
-                boolean needToUpdateProviderViews = false;
                 final String newUniqueId = mDisplayInfo.uniqueId;
                 if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
                     mDisplayUniqueId = newUniqueId;
@@ -474,37 +473,6 @@
                         setupDecorations();
                         return;
                     }
-
-                    if (mScreenDecorHwcLayer != null) {
-                        updateHwLayerRoundedCornerDrawable();
-                        updateHwLayerRoundedCornerExistAndSize();
-                    }
-                    needToUpdateProviderViews = true;
-                }
-
-                final float newRatio = getPhysicalPixelDisplaySizeRatio();
-                if (mRoundedCornerResDelegate.getPhysicalPixelDisplaySizeRatio() != newRatio) {
-                    mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(newRatio);
-                    if (mScreenDecorHwcLayer != null) {
-                        updateHwLayerRoundedCornerExistAndSize();
-                    }
-                    needToUpdateProviderViews = true;
-                }
-
-                if (needToUpdateProviderViews) {
-                    updateOverlayProviderViews(null);
-                } else {
-                    updateOverlayProviderViews(new Integer[] {
-                            mFaceScanningViewId,
-                            R.id.display_cutout,
-                            R.id.display_cutout_left,
-                            R.id.display_cutout_right,
-                            R.id.display_cutout_bottom,
-                    });
-                }
-
-                if (mScreenDecorHwcLayer != null) {
-                    mScreenDecorHwcLayer.onDisplayChanged(newUniqueId);
                 }
             }
         };
@@ -1070,9 +1038,11 @@
                 && (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) {
             mRotation = newRotation;
             mDisplayMode = newMod;
+            mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
+                    getPhysicalPixelDisplaySizeRatio());
             if (mScreenDecorHwcLayer != null) {
                 mScreenDecorHwcLayer.pendingConfigChange = false;
-                mScreenDecorHwcLayer.updateRotation(mRotation);
+                mScreenDecorHwcLayer.updateConfiguration(mDisplayUniqueId);
                 updateHwLayerRoundedCornerExistAndSize();
                 updateHwLayerRoundedCornerDrawable();
             }
@@ -1111,7 +1081,8 @@
                 context.getResources(), context.getDisplay().getUniqueId());
     }
 
-    private void updateOverlayProviderViews(@Nullable Integer[] filterIds) {
+    @VisibleForTesting
+    void updateOverlayProviderViews(@Nullable Integer[] filterIds) {
         if (mOverlays == null) {
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
index 991b54e..ded0fb7 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
@@ -59,7 +59,7 @@
         (view as? DisplayCutoutView)?.let { cutoutView ->
             cutoutView.setColor(tintColor)
             cutoutView.updateRotation(rotation)
-            cutoutView.onDisplayChanged(displayUniqueId)
+            cutoutView.updateConfiguration(displayUniqueId)
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
index ec0013b..5fdd198 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
@@ -124,7 +124,7 @@
             view.layoutParams = it
             (view as? FaceScanningOverlay)?.let { overlay ->
                 overlay.setColor(tintColor)
-                overlay.onDisplayChanged(displayUniqueId)
+                overlay.updateConfiguration(displayUniqueId)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index a252864..8b4aeef 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -78,23 +78,18 @@
         reloadMeasures()
     }
 
-    private fun reloadAll(newReloadToken: Int) {
-        if (reloadToken == newReloadToken) {
-            return
-        }
-        reloadToken = newReloadToken
-        reloadRes()
-        reloadMeasures()
-    }
-
     fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) {
         if (displayUniqueId != newDisplayUniqueId) {
             displayUniqueId = newDisplayUniqueId
             newReloadToken ?.let { reloadToken = it }
             reloadRes()
             reloadMeasures()
-        } else {
-            newReloadToken?.let { reloadAll(it) }
+        } else if (newReloadToken != null) {
+            if (reloadToken == newReloadToken) {
+                return
+            }
+            reloadToken = newReloadToken
+            reloadMeasures()
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 2319f43..181839a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -36,6 +36,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -255,6 +256,7 @@
         });
         mScreenDecorations.mDisplayInfo = mDisplayInfo;
         doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
+        doNothing().when(mScreenDecorations).updateOverlayProviderViews(any());
         reset(mTunerService);
 
         try {
@@ -1005,18 +1007,13 @@
         assertEquals(new Size(3, 3), resDelegate.getTopRoundedSize());
         assertEquals(new Size(4, 4), resDelegate.getBottomRoundedSize());
 
-        setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
-                getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
-                /* roundedTopDrawable */,
-                getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
-                /* roundedBottomDrawable */,
-                0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
+        doReturn(2f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
         mDisplayInfo.rotation = Surface.ROTATION_270;
 
         mScreenDecorations.onConfigurationChanged(null);
 
-        assertEquals(new Size(4, 4), resDelegate.getTopRoundedSize());
-        assertEquals(new Size(5, 5), resDelegate.getBottomRoundedSize());
+        assertEquals(new Size(6, 6), resDelegate.getTopRoundedSize());
+        assertEquals(new Size(8, 8), resDelegate.getBottomRoundedSize());
     }
 
     @Test
@@ -1293,51 +1290,6 @@
     }
 
     @Test
-    public void testOnDisplayChanged_hwcLayer() {
-        setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
-                null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
-                0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
-        final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
-        decorationSupport.format = PixelFormat.R_8;
-        doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
-
-        // top cutout
-        mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
-
-        mScreenDecorations.start();
-
-        final ScreenDecorHwcLayer hwcLayer = mScreenDecorations.mScreenDecorHwcLayer;
-        spyOn(hwcLayer);
-        doReturn(mDisplay).when(hwcLayer).getDisplay();
-
-        mScreenDecorations.mDisplayListener.onDisplayChanged(1);
-
-        verify(hwcLayer, times(1)).onDisplayChanged(any());
-    }
-
-    @Test
-    public void testOnDisplayChanged_nonHwcLayer() {
-        setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
-                null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
-                0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
-
-        // top cutout
-        mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
-
-        mScreenDecorations.start();
-
-        final ScreenDecorations.DisplayCutoutView cutoutView = (ScreenDecorations.DisplayCutoutView)
-                mScreenDecorations.getOverlayView(R.id.display_cutout);
-        assertNotNull(cutoutView);
-        spyOn(cutoutView);
-        doReturn(mDisplay).when(cutoutView).getDisplay();
-
-        mScreenDecorations.mDisplayListener.onDisplayChanged(1);
-
-        verify(cutoutView, times(1)).onDisplayChanged(any());
-    }
-
-    @Test
     public void testHasSameProvidersWithNullOverlays() {
         setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
                 null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index f933361..93a1868 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -24,12 +24,11 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.R as InternalR
 import com.android.systemui.R as SystemUIR
-import com.android.systemui.tests.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.tests.R
 import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Test
-
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
@@ -102,14 +101,11 @@
         assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize)
         assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
 
-        setupResources(radius = 100,
-                roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px),
-                roundedBottomDrawable = getTestsDrawable(R.drawable.rounded5px))
-
+        roundedCornerResDelegate.physicalPixelDisplaySizeRatio = 2f
         roundedCornerResDelegate.updateDisplayUniqueId(null, 1)
 
-        assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize)
-        assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
+        assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
+        assertEquals(Size(8, 8), roundedCornerResDelegate.bottomRoundedSize)
     }
 
     @Test
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3aed167..553146d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -156,6 +156,8 @@
         "android.hardware.health-translate-java",
         "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.1-java",
+        "android.hardware.tv.cec-V1-java",
+        "android.hardware.tv.hdmi-V1-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 5aa3fa4..5c1b33c 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -19,20 +19,25 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.CecMessage;
+import android.hardware.tv.cec.IHdmiCec;
+import android.hardware.tv.cec.IHdmiCecCallback;
 import android.hardware.tv.cec.V1_0.HotplugEvent;
-import android.hardware.tv.cec.V1_0.IHdmiCec;
 import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
-import android.hardware.tv.cec.V1_0.IHdmiCecCallback;
+import android.hardware.tv.cec.V1_0.OptionKey;
 import android.hardware.tv.cec.V1_0.Result;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.hardware.tv.hdmi.IHdmi;
+import android.hardware.tv.hdmi.IHdmiCallback;
 import android.icu.util.IllformedLocaleException;
 import android.icu.util.ULocale;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.IHwBinder;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.stats.hdmi.HdmiStatsEnums;
 import android.util.Slog;
 
@@ -170,8 +175,14 @@
      *         returns {@code null}.
      */
     static HdmiCecController create(HdmiControlService service, HdmiCecAtomWriter atomWriter) {
-        HdmiCecController controller = createWithNativeWrapper(service, new NativeWrapperImpl11(),
-                atomWriter);
+        HdmiCecController controller =
+                createWithNativeWrapper(service, new NativeWrapperImplAidl(), atomWriter);
+        if (controller != null) {
+            return controller;
+        }
+        HdmiLogger.warning("Unable to use CEC and HDMI AIDL HALs");
+
+        controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter);
         if (controller != null) {
             return controller;
         }
@@ -362,16 +373,43 @@
     }
 
     /**
-     * Set an option to CEC HAL.
+     * Configures the TV panel device wakeup behaviour in standby mode when it receives an OTP
+     * (One Touch Play) from a source device.
      *
-     * @param flag key of option
-     * @param enabled whether to enable/disable the given option.
+     * @param value If true, the TV device will wake up when OTP is received and if false, the TV
+     *     device will not wake up for an OTP.
      */
     @ServiceThreadOnly
-    void setOption(int flag, boolean enabled) {
+    void enableWakeupByOtp(boolean enabled) {
         assertRunOnServiceThread();
-        HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
-        mNativeWrapperImpl.nativeSetOption(flag, enabled);
+        HdmiLogger.debug("enableWakeupByOtp: %b", enabled);
+        mNativeWrapperImpl.enableWakeupByOtp(enabled);
+    }
+
+    /**
+     * Switch to enable or disable CEC on the device.
+     *
+     * @param value If true, the device will have all CEC functionalities and if false, the device
+     *     will not perform any CEC functions.
+     */
+    @ServiceThreadOnly
+    void enableCec(boolean enabled) {
+        assertRunOnServiceThread();
+        HdmiLogger.debug("enableCec: %b", enabled);
+        mNativeWrapperImpl.enableCec(enabled);
+    }
+
+    /**
+     * Configures the module that processes CEC messages - the Android framework or the HAL.
+     *
+     * @param value If true, the Android framework will actively process CEC messages and if false,
+     *     only the HAL will process the CEC messages.
+     */
+    @ServiceThreadOnly
+    void enableSystemCecControl(boolean enabled) {
+        assertRunOnServiceThread();
+        HdmiLogger.debug("enableSystemCecControl: %b", enabled);
+        mNativeWrapperImpl.enableSystemCecControl(enabled);
     }
 
     /**
@@ -829,12 +867,233 @@
         int nativeGetVersion();
         int nativeGetVendorId();
         HdmiPortInfo[] nativeGetPortInfos();
-        void nativeSetOption(int flag, boolean enabled);
+
+        void enableWakeupByOtp(boolean enabled);
+
+        void enableCec(boolean enabled);
+
+        void enableSystemCecControl(boolean enabled);
+
         void nativeSetLanguage(String language);
         void nativeEnableAudioReturnChannel(int port, boolean flag);
         boolean nativeIsConnected(int port);
     }
 
+    private static final class NativeWrapperImplAidl
+            implements NativeWrapper, IBinder.DeathRecipient {
+        private IHdmiCec mHdmiCec;
+        private IHdmi mHdmi;
+        @Nullable private HdmiCecCallback mCallback;
+
+        private final Object mLock = new Object();
+
+        @Override
+        public String nativeInit() {
+            return connectToHal() ? mHdmiCec.toString() + " " + mHdmi.toString() : null;
+        }
+
+        boolean connectToHal() {
+            mHdmiCec =
+                    IHdmiCec.Stub.asInterface(
+                            ServiceManager.getService(IHdmiCec.DESCRIPTOR + "/default"));
+            if (mHdmiCec == null) {
+                HdmiLogger.error("Could not initialize HDMI CEC AIDL HAL");
+                return false;
+            }
+            try {
+                mHdmiCec.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Couldn't link to death : ", e);
+            }
+
+            mHdmi =
+                    IHdmi.Stub.asInterface(
+                            ServiceManager.getService(IHdmi.DESCRIPTOR + "/default"));
+            if (mHdmi == null) {
+                HdmiLogger.error("Could not initialize HDMI AIDL HAL");
+                return false;
+            }
+            try {
+                mHdmi.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Couldn't link to death : ", e);
+            }
+            return true;
+        }
+
+        @Override
+        public void binderDied() {
+            // One of the services died, try to reconnect to both.
+            mHdmiCec.asBinder().unlinkToDeath(this, 0);
+            mHdmi.asBinder().unlinkToDeath(this, 0);
+            HdmiLogger.error("HDMI or CEC service died, reconnecting");
+            connectToHal();
+            // Reconnect the callback
+            if (mCallback != null) {
+                setCallback(mCallback);
+            }
+        }
+
+        @Override
+        public void setCallback(HdmiCecCallback callback) {
+            mCallback = callback;
+            try {
+                // Create an AIDL callback that can callback onCecMessage
+                mHdmiCec.setCallback(new HdmiCecCallbackAidl(callback));
+            } catch (RemoteException e) {
+                HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
+            }
+            try {
+                // Create an AIDL callback that can callback onHotplugEvent
+                mHdmi.setCallback(new HdmiCallbackAidl(callback));
+            } catch (RemoteException e) {
+                HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e);
+            }
+        }
+
+        @Override
+        public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
+            CecMessage message = new CecMessage();
+            message.initiator = (byte) (srcAddress & 0xF);
+            message.destination = (byte) (dstAddress & 0xF);
+            message.body = body;
+            try {
+                return mHdmiCec.sendMessage(message);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to send CEC message : ", e);
+                return SendMessageResult.FAIL;
+            }
+        }
+
+        @Override
+        public int nativeAddLogicalAddress(int logicalAddress) {
+            try {
+                return mHdmiCec.addLogicalAddress((byte) logicalAddress);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to add a logical address : ", e);
+                return Result.FAILURE_INVALID_ARGS;
+            }
+        }
+
+        @Override
+        public void nativeClearLogicalAddress() {
+            try {
+                mHdmiCec.clearLogicalAddress();
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to clear logical address : ", e);
+            }
+        }
+
+        @Override
+        public int nativeGetPhysicalAddress() {
+            try {
+                return mHdmiCec.getPhysicalAddress();
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to get physical address : ", e);
+                return INVALID_PHYSICAL_ADDRESS;
+            }
+        }
+
+        @Override
+        public int nativeGetVersion() {
+            try {
+                return mHdmiCec.getCecVersion();
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to get cec version : ", e);
+                return Result.FAILURE_UNKNOWN;
+            }
+        }
+
+        @Override
+        public int nativeGetVendorId() {
+            try {
+                return mHdmiCec.getVendorId();
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to get vendor id : ", e);
+                return Result.FAILURE_UNKNOWN;
+            }
+        }
+
+        @Override
+        public void enableWakeupByOtp(boolean enabled) {
+            try {
+                mHdmiCec.enableWakeupByOtp(enabled);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed call to enableWakeupByOtp : ", e);
+            }
+        }
+
+        @Override
+        public void enableCec(boolean enabled) {
+            try {
+                mHdmiCec.enableCec(enabled);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed call to enableCec : ", e);
+            }
+        }
+
+        @Override
+        public void enableSystemCecControl(boolean enabled) {
+            try {
+                mHdmiCec.enableSystemCecControl(enabled);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed call to enableSystemCecControl : ", e);
+            }
+        }
+
+        @Override
+        public void nativeSetLanguage(String language) {
+            try {
+                mHdmiCec.setLanguage(language);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to set language : ", e);
+            }
+        }
+
+        @Override
+        public void nativeEnableAudioReturnChannel(int port, boolean flag) {
+            try {
+                mHdmiCec.enableAudioReturnChannel(port, flag);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to enable/disable ARC : ", e);
+            }
+        }
+
+        @Override
+        public HdmiPortInfo[] nativeGetPortInfos() {
+            try {
+                android.hardware.tv.hdmi.HdmiPortInfo[] hdmiPortInfos = mHdmi.getPortInfo();
+                HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
+                int i = 0;
+                for (android.hardware.tv.hdmi.HdmiPortInfo portInfo : hdmiPortInfos) {
+                    hdmiPortInfo[i] =
+                            new HdmiPortInfo(
+                                    portInfo.portId,
+                                    portInfo.type,
+                                    portInfo.physicalAddress,
+                                    portInfo.cecSupported,
+                                    false,
+                                    portInfo.arcSupported);
+                    i++;
+                }
+                return hdmiPortInfo;
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to get port information : ", e);
+                return null;
+            }
+        }
+
+        @Override
+        public boolean nativeIsConnected(int port) {
+            try {
+                return mHdmi.isConnected(port);
+            } catch (RemoteException e) {
+                HdmiLogger.error("Failed to get connection info : ", e);
+                return false;
+            }
+        }
+    }
+
     private static final class NativeWrapperImpl11 implements NativeWrapper,
             IHwBinder.DeathRecipient, getPhysicalAddressCallback {
         private android.hardware.tv.cec.V1_1.IHdmiCec mHdmiCec;
@@ -985,8 +1244,7 @@
             }
         }
 
-        @Override
-        public void nativeSetOption(int flag, boolean enabled) {
+        private void nativeSetOption(int flag, boolean enabled) {
             try {
                 mHdmiCec.setOption(flag, enabled);
             } catch (RemoteException e) {
@@ -995,6 +1253,21 @@
         }
 
         @Override
+        public void enableWakeupByOtp(boolean enabled) {
+            nativeSetOption(OptionKey.WAKEUP, enabled);
+        }
+
+        @Override
+        public void enableCec(boolean enabled) {
+            nativeSetOption(OptionKey.ENABLE_CEC, enabled);
+        }
+
+        @Override
+        public void enableSystemCecControl(boolean enabled) {
+            nativeSetOption(OptionKey.SYSTEM_CEC_CONTROL, enabled);
+        }
+
+        @Override
         public void nativeSetLanguage(String language) {
             try {
                 mHdmiCec.setLanguage(language);
@@ -1038,7 +1311,7 @@
 
         boolean connectToHal() {
             try {
-                mHdmiCec = IHdmiCec.getService(true);
+                mHdmiCec = android.hardware.tv.cec.V1_0.IHdmiCec.getService(true);
                 try {
                     mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
                 } catch (RemoteException e) {
@@ -1063,7 +1336,8 @@
 
         @Override
         public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
-            CecMessage message = new CecMessage();
+            android.hardware.tv.cec.V1_0.CecMessage message =
+                    new android.hardware.tv.cec.V1_0.CecMessage();
             message.initiator = srcAddress;
             message.destination = dstAddress;
             message.body = new ArrayList<>(body.length);
@@ -1151,8 +1425,7 @@
             }
         }
 
-        @Override
-        public void nativeSetOption(int flag, boolean enabled) {
+        private void nativeSetOption(int flag, boolean enabled) {
             try {
                 mHdmiCec.setOption(flag, enabled);
             } catch (RemoteException e) {
@@ -1161,6 +1434,21 @@
         }
 
         @Override
+        public void enableWakeupByOtp(boolean enabled) {
+            nativeSetOption(OptionKey.WAKEUP, enabled);
+        }
+
+        @Override
+        public void enableCec(boolean enabled) {
+            nativeSetOption(OptionKey.ENABLE_CEC, enabled);
+        }
+
+        @Override
+        public void enableSystemCecControl(boolean enabled) {
+            nativeSetOption(OptionKey.SYSTEM_CEC_CONTROL, enabled);
+        }
+
+        @Override
         public void nativeSetLanguage(String language) {
             try {
                 mHdmiCec.setLanguage(language);
@@ -1221,7 +1509,8 @@
         }
     }
 
-    private static final class HdmiCecCallback10 extends IHdmiCecCallback.Stub {
+    private static final class HdmiCecCallback10
+            extends android.hardware.tv.cec.V1_0.IHdmiCecCallback.Stub {
         private final HdmiCecCallback mHdmiCecCallback;
 
         HdmiCecCallback10(HdmiCecCallback hdmiCecCallback) {
@@ -1229,7 +1518,8 @@
         }
 
         @Override
-        public void onCecMessage(CecMessage message) throws RemoteException {
+        public void onCecMessage(android.hardware.tv.cec.V1_0.CecMessage message)
+                throws RemoteException {
             byte[] body = new byte[message.body.size()];
             for (int i = 0; i < message.body.size(); i++) {
                 body[i] = message.body.get(i);
@@ -1262,7 +1552,8 @@
         }
 
         @Override
-        public void onCecMessage(CecMessage message) throws RemoteException {
+        public void onCecMessage(android.hardware.tv.cec.V1_0.CecMessage message)
+                throws RemoteException {
             byte[] body = new byte[message.body.size()];
             for (int i = 0; i < message.body.size(); i++) {
                 body[i] = message.body.get(i);
@@ -1276,6 +1567,52 @@
         }
     }
 
+    private static final class HdmiCecCallbackAidl extends IHdmiCecCallback.Stub {
+        private final HdmiCecCallback mHdmiCecCallback;
+
+        HdmiCecCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+            mHdmiCecCallback = hdmiCecCallback;
+        }
+
+        @Override
+        public void onCecMessage(CecMessage message) throws RemoteException {
+            mHdmiCecCallback.onCecMessage(message.initiator, message.destination, message.body);
+        }
+
+        @Override
+        public synchronized String getInterfaceHash() throws android.os.RemoteException {
+            return IHdmiCecCallback.Stub.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() throws android.os.RemoteException {
+            return IHdmiCecCallback.Stub.VERSION;
+        }
+    }
+
+    private static final class HdmiCallbackAidl extends IHdmiCallback.Stub {
+        private final HdmiCecCallback mHdmiCecCallback;
+
+        HdmiCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+            mHdmiCecCallback = hdmiCecCallback;
+        }
+
+        @Override
+        public void onHotplugEvent(boolean connected, int portId) throws RemoteException {
+            mHdmiCecCallback.onHotplugEvent(portId, connected);
+        }
+
+        @Override
+        public synchronized String getInterfaceHash() throws android.os.RemoteException {
+            return IHdmiCallback.Stub.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() throws android.os.RemoteException {
+            return IHdmiCallback.Stub.VERSION;
+        }
+    }
+
     public abstract static class Dumpable {
         protected final long mTime;
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3ee3503..1ae1b5b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -56,7 +56,6 @@
 import android.hardware.hdmi.IHdmiRecordListener;
 import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
 import android.hardware.hdmi.IHdmiVendorCommandListener;
-import android.hardware.tv.cec.V1_0.OptionKey;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
@@ -656,7 +655,7 @@
         if (mHdmiControlEnabled == HdmiControlManager.HDMI_CEC_CONTROL_ENABLED) {
             initializeCec(INITIATED_BY_BOOT_UP);
         } else {
-            mCecController.setOption(OptionKey.ENABLE_CEC, false);
+            mCecController.enableCec(false);
         }
         mMhlDevices = Collections.emptyList();
 
@@ -730,10 +729,11 @@
                     @Override
                     public void onChange(String setting) {
                         if (isTvDeviceEnabled()) {
-                            setCecOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+                            mCecController.enableWakeupByOtp(tv().getAutoWakeup());
                         }
                     }
-                }, mServiceThreadExecutor);
+                },
+                mServiceThreadExecutor);
     }
 
     /** Returns true if the device screen is off */
@@ -854,7 +854,7 @@
         mWakeUpMessageReceived = false;
 
         if (isTvDeviceEnabled()) {
-            mCecController.setOption(OptionKey.WAKEUP, tv().getAutoWakeup());
+            mCecController.enableWakeupByOtp(tv().getAutoWakeup());
         }
         int reason = -1;
         switch (initiatedBy) {
@@ -988,7 +988,7 @@
         mCecVersion = Math.max(HdmiControlManager.HDMI_CEC_VERSION_1_4_B,
                 Math.min(settingsCecVersion, supportedCecVersion));
 
-        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
+        mCecController.enableSystemCecControl(true);
         mCecController.setLanguage(mMenuLanguage);
         initializeLocalDevices(initiatedBy);
     }
@@ -3424,7 +3424,7 @@
                 device.onStandby(mStandbyMessageReceived, standbyAction);
             }
             if (!isAudioSystemDevice()) {
-                mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
+                mCecController.enableSystemCecControl(false);
                 mMhlController.setOption(OPTION_MHL_SERVICE_CONTROL, DISABLED);
             }
         }
@@ -3573,12 +3573,6 @@
     }
 
     @ServiceThreadOnly
-    void setCecOption(int key, boolean value) {
-        assertRunOnServiceThread();
-        mCecController.setOption(key, value);
-    }
-
-    @ServiceThreadOnly
     void setControlEnabled(@HdmiControlManager.HdmiCecControl int enabled) {
         assertRunOnServiceThread();
 
@@ -3612,8 +3606,8 @@
 
     @ServiceThreadOnly
     private void enableHdmiControlService() {
-        mCecController.setOption(OptionKey.ENABLE_CEC, true);
-        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true);
+        mCecController.enableCec(true);
+        mCecController.enableSystemCecControl(true);
         mMhlController.setOption(OPTION_MHL_ENABLE, ENABLED);
 
         initializeCec(INITIATED_BY_ENABLE_CEC);
@@ -3621,21 +3615,23 @@
 
     @ServiceThreadOnly
     private void disableHdmiControlService() {
-        disableDevices(new PendingActionClearedCallback() {
-            @Override
-            public void onCleared(HdmiCecLocalDevice device) {
-                assertRunOnServiceThread();
-                mCecController.flush(new Runnable() {
+        disableDevices(
+                new PendingActionClearedCallback() {
                     @Override
-                    public void run() {
-                        mCecController.setOption(OptionKey.ENABLE_CEC, false);
-                        mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, false);
-                        mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
-                        clearLocalDevices();
+                    public void onCleared(HdmiCecLocalDevice device) {
+                        assertRunOnServiceThread();
+                        mCecController.flush(
+                                new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        mCecController.enableCec(false);
+                                        mCecController.enableSystemCecControl(false);
+                                        mMhlController.setOption(OPTION_MHL_ENABLE, DISABLED);
+                                        clearLocalDevices();
+                                    }
+                                });
                     }
                 });
-            }
-        });
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index fc7be7f..364f6db 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -25,6 +25,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.ILocaleManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -38,6 +39,8 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -357,17 +360,20 @@
                 false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL,
                 "getApplicationLocales", /* callerPackage= */ null);
 
-        // This function handles three types of query operations:
+        // This function handles four types of query operations:
         // 1.) A normal, non-privileged app querying its own locale.
-        // 2.) The installer of the given app querying locales of a package installed
-        // by said installer.
-        // 3.) A privileged system service querying locales of another package.
-        // The least privileged case is a normal app performing a query, so check that first and
-        // get locales if the package name is owned by the app. Next check if the calling app
-        // is the installer of the given app and get locales. If neither conditions matched,
-        // check if the caller has the necessary permission and fetch locales.
+        // 2.) The installer of the given app querying locales of a package installed by said
+        // installer.
+        // 3.) The current input method querying locales of another package.
+        // 4.) A privileged system service querying locales of another package.
+        // The least privileged case is a normal app performing a query, so check that first and get
+        // locales if the package name is owned by the app. Next check if the calling app is the
+        // installer of the given app and get locales. Finally check if the calling app is the
+        // current input method. If neither conditions matched, check if the caller has the
+        // necessary permission and fetch locales.
         if (!isPackageOwnedByCaller(appPackageName, userId)
-                && !isCallerInstaller(appPackageName, userId)) {
+                && !isCallerInstaller(appPackageName, userId)
+                && !isCallerFromCurrentInputMethod(userId)) {
             enforceReadAppSpecificLocalesPermission();
         }
         final long token = Binder.clearCallingIdentity();
@@ -412,6 +418,26 @@
         return false;
     }
 
+    /**
+     * Checks if the calling app is the current input method.
+     */
+    private boolean isCallerFromCurrentInputMethod(int userId) {
+        String currentInputMethod = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.DEFAULT_INPUT_METHOD,
+                userId);
+        if (!TextUtils.isEmpty(currentInputMethod)) {
+            String inputMethodPkgName = ComponentName
+                    .unflattenFromString(currentInputMethod)
+                    .getPackageName();
+            int inputMethodUid = getPackageUid(inputMethodPkgName, userId);
+            return inputMethodUid >= 0 && UserHandle.isSameApp(Binder.getCallingUid(),
+                    inputMethodUid);
+        }
+
+        return false;
+    }
+
     private void enforceReadAppSpecificLocalesPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.READ_APP_SPECIFIC_LOCALES,
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 07e9fe6..6c4c829 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -115,6 +115,16 @@
         if (request.getIntervalMillis() == GnssMeasurementRequest.PASSIVE_INTERVAL) {
             return true;
         }
+        // The HAL doc does not specify if consecutive start() calls will be allowed.
+        // Some vendors may ignore the 2nd start() call if stop() is not called.
+        // Thus, here we always call stop() before calling start() to avoid being ignored.
+        if (mGnssNative.stopMeasurementCollection()) {
+            if (D) {
+                Log.d(TAG, "stopping gnss measurements");
+            }
+        } else {
+            Log.e(TAG, "error stopping gnss measurements");
+        }
         if (mGnssNative.startMeasurementCollection(request.isFullTracking(),
                 request.isCorrelationVectorOutputsEnabled(),
                 request.getIntervalMillis())) {
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index edd1ef3..ad1ff72 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -946,8 +946,12 @@
         int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         // Priority max value is 1000
         int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
+        // If the desired frontend id was specified, we only need to check the frontend.
+        boolean hasDesiredFrontend = request.desiredId != TunerFrontendRequest.DEFAULT_DESIRED_ID;
         for (FrontendResource fr : getFrontendResources().values()) {
-            if (fr.getType() == request.frontendType) {
+            int frontendId = getResourceIdFromHandle(fr.getHandle());
+            if (fr.getType() == request.frontendType
+                    && (!hasDesiredFrontend || frontendId == request.desiredId)) {
                 if (!fr.isInUse()) {
                     // Unused resource cannot be acquired if the max is already reached, but
                     // TRM still has to look for the reclaim candidate
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 559a2c0..29eccd4 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -118,7 +118,13 @@
     }
 
     @Override
-    public void nativeSetOption(int flag, boolean enabled) {}
+    public void enableWakeupByOtp(boolean enabled) {}
+
+    @Override
+    public void enableCec(boolean enabled) {}
+
+    @Override
+    public void enableSystemCecControl(boolean enabled) {}
 
     @Override
     public void nativeSetLanguage(String language) {}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
index 1dcdbac..dbcd38c 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.locales;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.fail;
@@ -35,13 +37,16 @@
 
 import android.Manifest;
 import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.InstallSourceInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.LocaleList;
+import android.provider.Settings;
 
+import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.content.PackageMonitor;
@@ -111,6 +116,8 @@
         doReturn(DEFAULT_USER_ID).when(mMockActivityManager)
                 .handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
                         anyString(), anyString());
+        doReturn(InstrumentationRegistry.getContext().getContentResolver())
+                .when(mMockContext).getContentResolver();
 
         mMockBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
         mLocaleManagerService = new LocaleManagerService(mMockContext, mMockActivityTaskManager,
@@ -299,6 +306,25 @@
         assertEquals(DEFAULT_LOCALES, locales);
     }
 
+    @Test
+    public void testGetApplicationLocales_callerIsCurrentInputMethod_returnsLocales()
+            throws Exception {
+        doReturn(DEFAULT_UID).when(mMockPackageManager)
+                .getPackageUidAsUser(anyString(), any(), anyInt());
+        doReturn(new PackageConfig(/* nightMode = */ 0, DEFAULT_LOCALES))
+                .when(mMockActivityTaskManager).getApplicationConfig(anyString(), anyInt());
+        String imPkgName = getCurrentInputMethodPackageName();
+        doReturn(Binder.getCallingUid()).when(mMockPackageManager)
+                .getPackageUidAsUser(eq(imPkgName), any(), anyInt());
+
+        LocaleList locales =
+                mLocaleManagerService.getApplicationLocales(
+                        DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+        verify(mMockContext, never()).enforceCallingOrSelfPermission(any(), any());
+        assertEquals(DEFAULT_LOCALES, locales);
+    }
+
     private static void assertNoLocalesStored(LocaleList locales) {
         assertNull(locales);
     }
@@ -311,4 +337,13 @@
     private void setUpPassingPermissionCheckFor(String permission) {
         doNothing().when(mMockContext).enforceCallingOrSelfPermission(eq(permission), any());
     }
+
+    private String getCurrentInputMethodPackageName() {
+        String im = Settings.Secure.getString(
+                InstrumentationRegistry.getContext().getContentResolver(),
+                Settings.Secure.DEFAULT_INPUT_METHOD);
+        ComponentName cn = ComponentName.unflattenFromString(im);
+        assertThat(cn).isNotNull();
+        return cn.getPackageName();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
index 808b74e..853eea1 100644
--- a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -47,6 +47,8 @@
 import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.XmlUtils;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -124,6 +126,8 @@
         doReturn(DEFAULT_INSTALL_SOURCE_INFO).when(mMockPackageManager)
                 .getInstallSourceInfo(anyString());
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+        doReturn(InstrumentationRegistry.getContext().getContentResolver())
+                .when(mMockContext).getContentResolver();
 
         mStoragefile = new AtomicFile(new File(
                 Environment.getExternalStorageDirectory(), "systemUpdateUnitTests.xml"));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 7abe369..d535677 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -88,7 +88,7 @@
 
     @After
     public void tearDown() {
-        mPositioner = null;
+        TaskPositioner.setFactory(null);
     }
 
     @Test
diff --git a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
index 35859fe..29d87a2 100644
--- a/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
+++ b/tests/RollbackTest/MultiUserRollbackTest/src/com/android/tests/rollback/host/MultiUserRollbackTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
 
 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -35,6 +36,7 @@
  */
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class MultiUserRollbackTest extends BaseHostJUnit4Test {
+    private boolean mSupportMultiUsers;
     // The user that was running originally when the test starts.
     private int mOriginalUserId;
     private int mSecondaryUserId = -1;
@@ -46,14 +48,20 @@
 
     @After
     public void tearDown() throws Exception {
-        removeSecondaryUserIfNecessary();
-        runPhaseForUsers("cleanUp", mOriginalUserId);
-        uninstallPackage("com.android.cts.install.lib.testapp.A");
-        uninstallPackage("com.android.cts.install.lib.testapp.B");
+        if (mSupportMultiUsers) {
+            removeSecondaryUserIfNecessary();
+            runPhaseForUsers("cleanUp", mOriginalUserId);
+            uninstallPackage("com.android.cts.install.lib.testapp.A");
+            uninstallPackage("com.android.cts.install.lib.testapp.B");
+        }
     }
 
     @Before
     public void setup() throws Exception {
+        assumeTrue("Device does not support multiple users",
+                getDevice().isMultiUserSupported());
+
+        mSupportMultiUsers = true;
         mOriginalUserId = getDevice().getCurrentUser();
         createAndStartSecondaryUser();
         installPackage("RollbackTest.apk", "--user all");