Merge "Import translations. DO NOT MERGE ANYWHERE"
diff --git a/core/api/current.txt b/core/api/current.txt
index 5f3b1f3..dd3d6eb 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -125,6 +125,18 @@
     field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS";
     field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL";
     field public static final String MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL = "android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL";
+    field public static final String MANAGE_DEVICE_POLICY_APPS_CONTROL = "android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL";
+    field public static final String MANAGE_DEVICE_POLICY_APP_RESTRICTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS";
+    field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
+    field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS";
+    field public static final String MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES = "android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES";
+    field public static final String MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES = "android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES";
+    field public static final String MANAGE_DEVICE_POLICY_LOCK_TASK = "android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK";
+    field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS";
+    field public static final String MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY = "android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY";
+    field public static final String MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS = "android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS";
+    field public static final String MANAGE_DEVICE_POLICY_SAFE_BOOT = "android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT";
+    field public static final String MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE = "android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE";
     field public static final String MANAGE_DEVICE_POLICY_TIME = "android.permission.MANAGE_DEVICE_POLICY_TIME";
     field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
@@ -7535,6 +7547,7 @@
     method @Nullable public android.app.admin.PackagePolicy getManagedProfileCallerIdAccessPolicy();
     method @Nullable public android.app.admin.PackagePolicy getManagedProfileContactsAccessPolicy();
     method public long getManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName);
+    method @NonNull public android.app.admin.ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy();
     method public int getMaximumFailedPasswordsForWipe(@Nullable android.content.ComponentName);
     method public long getMaximumTimeToLock(@Nullable android.content.ComponentName);
     method @NonNull public java.util.List<java.lang.String> getMeteredDataDisabledPackages(@NonNull android.content.ComponentName);
@@ -7685,6 +7698,7 @@
     method public void setManagedProfileCallerIdAccessPolicy(@Nullable android.app.admin.PackagePolicy);
     method public void setManagedProfileContactsAccessPolicy(@Nullable android.app.admin.PackagePolicy);
     method public void setManagedProfileMaximumTimeOff(@NonNull android.content.ComponentName, long);
+    method public void setManagedSubscriptionsPolicy(@Nullable android.app.admin.ManagedSubscriptionsPolicy);
     method public void setMasterVolumeMuted(@NonNull android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(@NonNull android.content.ComponentName, int);
     method public void setMaximumTimeToLock(@NonNull android.content.ComponentName, long);
@@ -7987,6 +8001,16 @@
     method public java.time.MonthDay getStart();
   }
 
+  public final class ManagedSubscriptionsPolicy implements android.os.Parcelable {
+    ctor public ManagedSubscriptionsPolicy(int);
+    method public int describeContents();
+    method public int getPolicyType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedSubscriptionsPolicy> CREATOR;
+    field public static final int TYPE_ALL_MANAGED_SUBSCRIPTIONS = 1; // 0x1
+    field public static final int TYPE_ALL_PERSONAL_SUBSCRIPTIONS = 0; // 0x0
+  }
+
   public abstract class NetworkEvent implements android.os.Parcelable {
     method public int describeContents();
     method public long getId();
@@ -23773,6 +23797,8 @@
     method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence);
     method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle);
     method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri);
+    method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityPublic();
+    method @NonNull public android.media.MediaRoute2Info.Builder setVisibilityRestricted(@NonNull java.util.Set<java.lang.String>);
     method @NonNull public android.media.MediaRoute2Info.Builder setVolume(int);
     method @NonNull public android.media.MediaRoute2Info.Builder setVolumeHandling(int);
     method @NonNull public android.media.MediaRoute2Info.Builder setVolumeMax(int);
@@ -24314,15 +24340,19 @@
 
   public final class RouteListingPreference implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.content.ComponentName getInAppOnlyItemRoutingReceiver();
     method @NonNull public java.util.List<android.media.RouteListingPreference.Item> getItems();
     method public boolean getUseSystemOrdering();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference> CREATOR;
+    field public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
   }
 
   public static final class RouteListingPreference.Builder {
     ctor public RouteListingPreference.Builder();
     method @NonNull public android.media.RouteListingPreference build();
+    method @NonNull public android.media.RouteListingPreference.Builder setInAppOnlyItemRoutingReceiver(@Nullable android.content.ComponentName);
     method @NonNull public android.media.RouteListingPreference.Builder setItems(@NonNull java.util.List<android.media.RouteListingPreference.Item>);
     method @NonNull public android.media.RouteListingPreference.Builder setUseSystemOrdering(boolean);
   }
@@ -24337,6 +24367,7 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteListingPreference.Item> CREATOR;
     field public static final int DISABLE_REASON_AD = 3; // 0x3
     field public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2; // 0x2
+    field public static final int DISABLE_REASON_IN_APP_ONLY = 4; // 0x4
     field public static final int DISABLE_REASON_NONE = 0; // 0x0
     field public static final int DISABLE_REASON_SUBSCRIPTION_REQUIRED = 1; // 0x1
     field public static final int FLAG_ONGOING_SESSION = 1; // 0x1
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index de03ba4..484ef4f 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -10997,6 +10997,51 @@
     }
 
     /**
+     * Called by a profile owner of an organization-owned device to specify {@link
+     * ManagedSubscriptionsPolicy}
+     *
+     * <p>Managed subscriptions policy controls how SIMs would be associated with the
+     * managed profile. For example a policy of type
+     * {@link ManagedSubscriptionsPolicy#TYPE_ALL_MANAGED_SUBSCRIPTIONS} assigns all
+     * SIM-based subscriptions to the managed profile. In this case OEM default
+     * dialer and messages app are automatically installed in the managed profile
+     * and all incoming and outgoing calls and text messages are handled by them.
+     * <p>This API can only be called during device setup.
+     *
+     * @param policy {@link ManagedSubscriptionsPolicy} policy, passing null for this resets the
+     *               policy to be the default.
+     * @throws SecurityException     if the caller is not a profile owner on an organization-owned
+     *                               managed profile.
+     * @throws IllegalStateException if called after the device setup has been completed.
+     * @see ManagedSubscriptionsPolicy
+     */
+    public void setManagedSubscriptionsPolicy(@Nullable ManagedSubscriptionsPolicy policy) {
+        throwIfParentInstance("setManagedSubscriptionsPolicy");
+        try {
+            mService.setManagedSubscriptionsPolicy(policy);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current {@link ManagedSubscriptionsPolicy}.
+     * If the policy has not been set, it will return a default policy of Type {@link
+     * ManagedSubscriptionsPolicy#TYPE_ALL_PERSONAL_SUBSCRIPTIONS}.
+     *
+     * @see #setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy)
+     */
+    @NonNull
+    public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
+        throwIfParentInstance("getManagedSubscriptionsPolicy");
+        try {
+            return mService.getManagedSubscriptionsPolicy();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Similar to {@link #logoutUser(ComponentName)}, except:
      *
      * <ul>
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 26d7667..20695ca 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -34,6 +34,7 @@
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
+import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.WifiSsidPolicy;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -583,4 +584,7 @@
 
     void setMtePolicy(int flag);
     int getMtePolicy();
+
+    void setManagedSubscriptionsPolicy(in ManagedSubscriptionsPolicy policy);
+    ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy();
 }
diff --git a/core/java/android/app/timedetector/TimePoint.aidl b/core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
similarity index 74%
rename from core/java/android/app/timedetector/TimePoint.aidl
rename to core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
index 80d4bc1..f634f11 100644
--- a/core/java/android/app/timedetector/TimePoint.aidl
+++ b/core/java/android/app/admin/ManagedSubscriptionsPolicy.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2022, The Android Open Source Project
+ * 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
+ *      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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.app.timedetector;
+package android.app.admin;
 
-parcelable TimePoint;
+parcelable ManagedSubscriptionsPolicy;
\ No newline at end of file
diff --git a/core/java/android/app/admin/ManagedSubscriptionsPolicy.java b/core/java/android/app/admin/ManagedSubscriptionsPolicy.java
new file mode 100644
index 0000000..1098c38
--- /dev/null
+++ b/core/java/android/app/admin/ManagedSubscriptionsPolicy.java
@@ -0,0 +1,156 @@
+/*
+ * 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 android.app.admin;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * A policy class that describes how managed SIM subscriptions should behave on the device.
+ */
+public final class ManagedSubscriptionsPolicy implements Parcelable {
+    private static final String TAG = "ManagedSubscriptionsPolicy";
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"TYPE_"}, value = {TYPE_ALL_PERSONAL_SUBSCRIPTIONS,
+            TYPE_ALL_MANAGED_SUBSCRIPTIONS})
+    @interface ManagedSubscriptionsPolicyType {
+    }
+
+    /**
+     * Represents default policy to not have any managed subscriptions on the device.
+     */
+    public static final int TYPE_ALL_PERSONAL_SUBSCRIPTIONS = 0;
+
+    /**
+     * Represents policy to have only managed subscriptions on the device, any existing and
+     * future subscriptions on the device are exclusively associated with the managed profile.
+     *
+     * <p>When a subscription is associated with the managed profile, incoming/outgoing calls and
+     * text message using that subscription would only work via apps on managed profile.
+     * Also, Call logs and messages would be accessible only from the managed profile.
+     */
+    public static final int TYPE_ALL_MANAGED_SUBSCRIPTIONS = 1;
+
+    @ManagedSubscriptionsPolicyType
+    private final int mPolicyType;
+
+    private static final String KEY_POLICY_TYPE = "policy_type";
+
+    public ManagedSubscriptionsPolicy(@ManagedSubscriptionsPolicyType int policyType) {
+        if (policyType != TYPE_ALL_PERSONAL_SUBSCRIPTIONS
+                && policyType != TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+            throw new IllegalArgumentException("Invalid policy type");
+        }
+        mPolicyType = policyType;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<ManagedSubscriptionsPolicy> CREATOR =
+            new Parcelable.Creator<ManagedSubscriptionsPolicy>() {
+                public ManagedSubscriptionsPolicy createFromParcel(Parcel in) {
+                    ManagedSubscriptionsPolicy policy = new ManagedSubscriptionsPolicy(
+                            in.readInt());
+                    return policy;
+                }
+
+                @Override
+                public ManagedSubscriptionsPolicy[] newArray(int size) {
+                    return new ManagedSubscriptionsPolicy[size];
+                }
+            };
+
+    /**
+     * Returns the type of managed subscriptions policy, or {@link #TYPE_ALL_PERSONAL_SUBSCRIPTIONS}
+     * if no policy has been set.
+     *
+     * @return The policy type.
+     */
+    @ManagedSubscriptionsPolicyType
+    public int getPolicyType() {
+        return mPolicyType;
+    }
+
+    @Override
+    public String toString() {
+        return TextUtils.formatSimple("ManagedSubscriptionsPolicy (type: %d)", mPolicyType);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mPolicyType);
+    }
+
+    @Override
+    public boolean equals(Object thatObject) {
+        if (this == thatObject) {
+            return true;
+        }
+        if (!(thatObject instanceof ManagedSubscriptionsPolicy)) {
+            return false;
+        }
+        ManagedSubscriptionsPolicy that = (ManagedSubscriptionsPolicy) thatObject;
+        return mPolicyType == that.mPolicyType;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPolicyType);
+    }
+
+    /** @hide */
+    @Nullable
+    public static ManagedSubscriptionsPolicy readFromXml(@NonNull TypedXmlPullParser parser) {
+        try {
+            ManagedSubscriptionsPolicy policy = new ManagedSubscriptionsPolicy(
+                    parser.getAttributeInt(null, KEY_POLICY_TYPE, -1));
+
+            return policy;
+        } catch (IllegalArgumentException e) {
+            // Fail through
+            Log.w(TAG, "Load xml failed", e);
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    public void saveToXml(TypedXmlSerializer out) throws IOException {
+        out.attributeInt(null, KEY_POLICY_TYPE, mPolicyType);
+    }
+}
diff --git a/core/java/android/app/timedetector/ITimeDetectorService.aidl b/core/java/android/app/timedetector/ITimeDetectorService.aidl
index a0c898e..96eddd2 100644
--- a/core/java/android/app/timedetector/ITimeDetectorService.aidl
+++ b/core/java/android/app/timedetector/ITimeDetectorService.aidl
@@ -24,7 +24,6 @@
 import android.app.time.UnixEpochTime;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
 
 /**
  * Binder APIs to communicate with the time detector service.
@@ -55,5 +54,5 @@
   boolean suggestManualTime(in ManualTimeSuggestion timeSuggestion);
   void suggestTelephonyTime(in TelephonyTimeSuggestion timeSuggestion);
 
-  TimePoint latestNetworkTime();
+  UnixEpochTime latestNetworkTime();
 }
diff --git a/core/java/android/app/timedetector/TimePoint.java b/core/java/android/app/timedetector/TimePoint.java
deleted file mode 100644
index aa079a9..0000000
--- a/core/java/android/app/timedetector/TimePoint.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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 android.app.timedetector;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * Data class for passing a Unix epoch time anchored to the elapsed realtime clock.
- *
- * @hide
- */
-public final class TimePoint implements Parcelable {
-
-    private final long mUnixEpochTimeMillis;
-    private final long mElapsedRealtimeMillis;
-
-    public TimePoint(long unixEpochTimeMillis, long elapsedRealtimeMillis) {
-        mUnixEpochTimeMillis = unixEpochTimeMillis;
-        mElapsedRealtimeMillis = elapsedRealtimeMillis;
-    }
-
-    /**
-     * The current Unix epoch time, according to the external source.
-     */
-    public long getUnixEpochTimeMillis() {
-        return mUnixEpochTimeMillis;
-    }
-
-    /**
-     * The elapsed millis since boot when {@link #getUnixEpochTimeMillis} was computed.
-     */
-    public long getElapsedRealtimeMillis() {
-        return mElapsedRealtimeMillis;
-    }
-
-    @Override
-    public void writeToParcel(Parcel out, int flags) {
-        out.writeLong(mUnixEpochTimeMillis);
-        out.writeLong(mElapsedRealtimeMillis);
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof TimePoint)) {
-            return false;
-        }
-        TimePoint timePoint = (TimePoint) o;
-        return mUnixEpochTimeMillis == timePoint.mUnixEpochTimeMillis
-                && mElapsedRealtimeMillis == timePoint.mElapsedRealtimeMillis;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mUnixEpochTimeMillis, mElapsedRealtimeMillis);
-    }
-
-    @Override
-    public String toString() {
-        return "TimePoint{"
-                + "mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
-                + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
-                + '}';
-    }
-
-    public static final @NonNull Creator<TimePoint> CREATOR =
-            new Creator<TimePoint>() {
-                public TimePoint createFromParcel(Parcel in) {
-                    long unixEpochTime = in.readLong();
-                    long elapsedRealtimeMillis = in.readLong();
-                    return new TimePoint(unixEpochTime, elapsedRealtimeMillis);
-                }
-
-                public TimePoint[] newArray(int size) {
-                    return new TimePoint[size];
-                }
-            };
-}
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 2b75a23..831ca86 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.app.IAlarmManager;
+import android.app.time.UnixEpochTime;
 import android.app.timedetector.ITimeDetectorService;
-import android.app.timedetector.TimePoint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.location.ILocationManager;
@@ -300,7 +300,7 @@
         ITimeDetectorService timeDetectorService = ITimeDetectorService.Stub
                 .asInterface(ServiceManager.getService(Context.TIME_DETECTOR_SERVICE));
         if (timeDetectorService != null) {
-            TimePoint time;
+            UnixEpochTime time;
             try {
                 time = timeDetectorService.latestNetworkTime();
             } catch (ParcelableException e) {
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 92ee393..fc02524 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -599,8 +599,11 @@
     /**
      * Specifies if a user is disallowed from transferring files over USB.
      *
-     * <p>This restriction can only be set by a device owner, a profile owner on the primary
-     * user or a profile owner of an organization-owned managed profile on the parent profile.
+     * <p>This restriction can only be set by a <a href="https://developers.google.com/android/work/terminology#device_owner_do">
+     * device owner</a> or a <a href="https://developers.google.com/android/work/terminology#profile_owner_po">
+     * profile owner</a> on the primary user's profile or a profile owner of an organization-owned
+     * <a href="https://developers.google.com/android/work/terminology#managed_profile">
+     * managed profile</a> on the parent profile.
      * When it is set by a device owner, it applies globally. When it is set by a profile owner
      * on the primary user or by a profile owner of an organization-owned managed profile on
      * the parent profile, it disables the primary user from transferring files over USB. No other
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 943d64a..fc47e69 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -353,4 +353,6 @@
      * Returns whether this window needs to cancel draw and retry later.
      */
     boolean cancelDraw(IWindow window);
+
+    boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow);
 }
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 50ce7b6..134625e 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -194,7 +194,7 @@
          * is more akin to a PopupWindow in that the size is user specified
          * independent of configuration width and height.
          *
-         * In order to receive the configuration change via 
+         * In order to receive the configuration change via
          * {@link View#onConfigurationChanged}, the context used with the
          * SurfaceControlViewHost and it's embedded view hierarchy must
          * be a WindowContext obtained from {@link Context#createWindowContext}.
@@ -460,5 +460,26 @@
                 (WindowManagerImpl) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE);
         attrs.token = wm.getDefaultToken();
     }
-}
 
+    /**
+     * Transfer the currently in progress touch gesture to the parent
+     * (if any) of this SurfaceControlViewHost. This requires that the
+     * SurfaceControlViewHost was created with an associated hostInputToken.
+     *
+     * @return Whether the touch stream was transferred.
+     * @hide
+     */
+    public boolean transferTouchGestureToHost() {
+        if (mViewRoot == null) {
+            return false;
+        }
+
+        final IWindowSession realWm = WindowManagerGlobal.getWindowSession();
+        try {
+            return realWm.transferEmbeddedTouchFocusToHost(mViewRoot.mWindow);
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index edf33f1..ecb5557 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -561,4 +561,11 @@
     public boolean cancelDraw(IWindow window) {
         return false;
     }
+
+    @Override
+    public boolean transferEmbeddedTouchFocusToHost(IWindow window) {
+        Log.e(TAG, "Received request to transferEmbeddedTouch focus on WindowlessWindowManager" +
+            " we shouldn't get here!");
+        return false;
+    }
 }
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 40c0fee..f53737a 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -74,6 +74,9 @@
      * and animation should seek to its end state. Exact end value may vary depending on
      * screen size.
      * </ol>
+     * <li> After the gesture finishes in cancel state, this method keeps getting invoked until the
+     * progress value animates back to 0.
+     * </ol>
      * In-between locations are linearly interpolated based on horizontal distance from the starting
      * edge and smooth clamped to 1 when the distance exceeds a system-wide threshold.
      */
diff --git a/core/java/android/window/BackProgressAnimator.java b/core/java/android/window/BackProgressAnimator.java
index 14a57e0..618670a 100644
--- a/core/java/android/window/BackProgressAnimator.java
+++ b/core/java/android/window/BackProgressAnimator.java
@@ -16,8 +16,10 @@
 
 package android.window;
 
+import android.annotation.NonNull;
 import android.util.FloatProperty;
 
+import com.android.internal.dynamicanimation.animation.DynamicAnimation;
 import com.android.internal.dynamicanimation.animation.SpringAnimation;
 import com.android.internal.dynamicanimation.animation.SpringForce;
 
@@ -126,6 +128,27 @@
         mProgress = 0;
     }
 
+    /**
+     * Animate the back progress animation from current progress to start position.
+     * This should be called when back is cancelled.
+     *
+     * @param finishCallback the callback to be invoked when the progress is reach to 0.
+     */
+    public void onBackCancelled(@NonNull Runnable finishCallback) {
+        final DynamicAnimation.OnAnimationEndListener listener =
+                new DynamicAnimation.OnAnimationEndListener() {
+            @Override
+            public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value,
+                    float velocity) {
+                mSpring.removeEndListener(this);
+                finishCallback.run();
+                reset();
+            }
+        };
+        mSpring.addEndListener(listener);
+        mSpring.animateToFinalPosition(0);
+    }
+
     private void updateProgressValue(float progress) {
         if (mLastBackEvent == null || mCallback == null || !mStarted) {
             return;
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 64992b9..7a5510c 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -266,11 +266,12 @@
         @Override
         public void onBackCancelled() {
             Handler.getMain().post(() -> {
-                mProgressAnimator.reset();
-                final OnBackAnimationCallback callback = getBackAnimationCallback();
-                if (callback != null) {
-                    callback.onBackCancelled();
-                }
+                mProgressAnimator.onBackCancelled(() -> {
+                    final OnBackAnimationCallback callback = getBackAnimationCallback();
+                    if (callback != null) {
+                        callback.onBackCancelled();
+                    }
+                });
             });
         }
 
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e0b0110..4f97d21 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -91,6 +91,10 @@
     public static final ComponentName ACCESSIBILITY_BUTTON_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "AccessibilityButton");
 
+    // The component name for the sub setting of Hearing aids in Accessibility settings
+    public static final ComponentName ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "HearingAids");
+
     public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "ColorInversionTile");
     public static final ComponentName DALTONIZER_TILE_COMPONENT_NAME =
@@ -104,7 +108,7 @@
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
             .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
             .build();
-    private static Map<ComponentName, ToggleableFrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
+    private static Map<ComponentName, FrameworkFeatureInfo> sFrameworkShortcutFeaturesMap;
 
     private final Context mContext;
     private final Handler mHandler;
@@ -133,10 +137,10 @@
      * @return An immutable map from placeholder component names to feature
      *         info for toggling a framework feature
      */
-    public static Map<ComponentName, ToggleableFrameworkFeatureInfo>
+    public static Map<ComponentName, FrameworkFeatureInfo>
         getFrameworkShortcutFeaturesMap() {
         if (sFrameworkShortcutFeaturesMap == null) {
-            Map<ComponentName, ToggleableFrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
+            Map<ComponentName, FrameworkFeatureInfo> featuresMap = new ArrayMap<>(4);
             featuresMap.put(COLOR_INVERSION_COMPONENT_NAME,
                     new ToggleableFrameworkFeatureInfo(
                             Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
@@ -157,6 +161,8 @@
                             Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                             "1" /* Value to enable */, "0" /* Value to disable */,
                             R.string.reduce_bright_colors_feature_name));
+            featuresMap.put(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
+                    new LaunchableFrameworkFeatureInfo(R.string.hearing_aids_feature_name));
             sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap);
         }
         return sFrameworkShortcutFeaturesMap;
@@ -391,7 +397,7 @@
         if (targetComponentName == null) {
             return null;
         }
-        final ToggleableFrameworkFeatureInfo frameworkFeatureInfo =
+        final FrameworkFeatureInfo frameworkFeatureInfo =
                 getFrameworkShortcutFeaturesMap().get(targetComponentName);
         if (frameworkFeatureInfo != null) {
             return frameworkFeatureInfo.getLabel(mContext);
@@ -670,15 +676,13 @@
     /**
      * Immutable class to hold info about framework features that can be controlled by shortcut
      */
-    public static class ToggleableFrameworkFeatureInfo {
+    public abstract static class FrameworkFeatureInfo {
         private final String mSettingKey;
         private final String mSettingOnValue;
         private final String mSettingOffValue;
         private final int mLabelStringResourceId;
-        // These go to the settings wrapper
-        private int mIconDrawableId;
 
-        ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+        FrameworkFeatureInfo(String settingKey, String settingOnValue,
                 String settingOffValue, int labelStringResourceId) {
             mSettingKey = settingKey;
             mSettingOnValue = settingOnValue;
@@ -696,7 +700,7 @@
         /**
          * @return The value to write to settings to turn the feature on
          */
-        public String getSettingOnValue() {
+        public String getSettingOnValue()  {
             return mSettingOnValue;
         }
 
@@ -711,6 +715,29 @@
             return context.getString(mLabelStringResourceId);
         }
     }
+    /**
+     * Immutable class to hold framework features that have on/off state settings key and can be
+     * controlled by shortcut.
+     */
+    public static class ToggleableFrameworkFeatureInfo extends FrameworkFeatureInfo {
+
+        ToggleableFrameworkFeatureInfo(String settingKey, String settingOnValue,
+                String settingOffValue, int labelStringResourceId) {
+            super(settingKey, settingOnValue, settingOffValue, labelStringResourceId);
+        }
+    }
+
+    /**
+     * Immutable class to hold framework features that don't have settings key and can be controlled
+     * by shortcut.
+     */
+    public static class LaunchableFrameworkFeatureInfo extends FrameworkFeatureInfo {
+
+        LaunchableFrameworkFeatureInfo(int labelStringResourceId) {
+            super(/* settingKey= */ null, /* settingOnValue= */ null, /* settingOffValue= */ null,
+                    labelStringResourceId);
+        }
+    }
 
     // Class to allow mocking of static framework calls
     public static class FrameworkObjectProvider {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index fc2c8cc..b5455f2 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -18,6 +18,7 @@
 
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
@@ -250,11 +251,21 @@
                         context.getDrawable(R.drawable.ic_accessibility_reduce_bright_colors),
                         Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED);
 
+        final InvisibleToggleAllowListingFeatureTarget hearingAids =
+                new InvisibleToggleAllowListingFeatureTarget(context,
+                        shortcutType,
+                        isShortcutContained(context, shortcutType,
+                                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()),
+                        ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString(),
+                        context.getString(R.string.hearing_aids_feature_name),
+                        context.getDrawable(R.drawable.ic_accessibility_hearing_aid),
+                        /* key= */ null);
         targets.add(magnification);
         targets.add(daltonizer);
         targets.add(colorInversion);
         targets.add(oneHandedMode);
         targets.add(reduceBrightColors);
+        targets.add(hearingAids);
 
         return targets;
     }
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
index 91cc4b9..d64a474 100644
--- a/core/java/com/android/internal/util/FunctionalUtils.java
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -248,6 +248,19 @@
     }
 
     /**
+     * A {@link Supplier} that allows the caller to specify a custom checked {@link Exception} that
+     * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
+     * different classes.
+     *
+     * @param <Output> Method return type
+     * @param <ExceptionType> Checked exception type
+     */
+    @FunctionalInterface
+    public interface ThrowingCheckedSupplier<Output, ExceptionType extends Exception> {
+        Output get() throws ExceptionType;
+    }
+
+    /**
      * A {@link Consumer} that allows the caller to specify a custom checked {@link Exception} that
      * can be thrown by the implementer. This is usually used when proxying/wrapping calls between
      * different classes.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bfa5301..76fbffd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3119,12 +3119,99 @@
 
     <!-- @SystemApi @hide Allows an application to exempt apps from platform restrictions.-->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS"
-                android:protectionLevel="signature|role" />
+                android:protectionLevel="internal|role" />
 
-    <!-- Allows an application to manage date and time device policy. -->
+    <!-- Allows an application to manage device policy relating to time.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.-->
     <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME"
                 android:protectionLevel="internal|role" />
 
+    <!-- Allows an application to set the grant state of runtime permissions on packages.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage the identity of the managing organization.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to set support messages for when a user action is affected by an
+        active policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage backup service policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage lock task policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage policy regarding modifying applications.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage installing from unknown sources policy.
+        <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected
+        by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage application restrictions.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage calling policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage debugging features policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage policy preventing users from modifying users.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS"
+                android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to manage safe boot policy.
+        <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call
+        APIs protected by this permission on users different to the calling user.
+    -->
+    <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT"
+                android:protectionLevel="internal|role" />
+
     <!-- Allows an application to set device policies outside the current user
         that are critical for securing data within the current user.
         <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_*
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid.xml b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
new file mode 100644
index 0000000..e5ffeb0
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
@@ -0,0 +1,24 @@
+<!--
+    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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/accessibility_feature_background" />
+    <foreground>
+        <inset
+            android:drawable="@drawable/ic_accessibility_hearing_aid_foreground"
+            android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+    </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml
new file mode 100644
index 0000000..7ced795
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_foreground.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="18dp"
+    android:height="18dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 08c5d9c..7fe373f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -323,7 +323,7 @@
     <string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"uskutečňování a spravování telefonních hovorů"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Tělesné senzory"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"přístup k datům ze snímačů vašich životních funkcí"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"přístup k datům ze senzorů vašich životních funkcí"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"Oznámení"</string>
     <string name="permgroupdesc_notifications" msgid="4608679556801506580">"zobrazovat oznámení"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Načítat obsah oken"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index b659de8..6733faa 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1682,13 +1682,13 @@
     <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Verknüpfung für Bedienungshilfen aktivieren?"</string>
     <string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfen. Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nAktuelle Funktionen:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nDu kannst ausgewählte Funktionen unter \"Einstellungen\" &gt; \"Bedienungshilfen\" ändern."</string>
     <string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
-    <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Verknüpfung für <xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
+    <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Kurzbefehl für <xliff:g id="SERVICE">%1$s</xliff:g> aktivieren?"</string>
     <string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Wenn du beide Lautstärketasten einige Sekunden lang gedrückt hältst, aktivierst du die Bedienungshilfe \"<xliff:g id="SERVICE">%1$s</xliff:g>\". Dadurch kann sich die Funktionsweise deines Geräts ändern.\n\nUnter \"Einstellungen &gt; \"Bedienungshilfen\" kannst du dieser Verknüpfung eine andere Funktion zuweisen."</string>
     <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Aktivieren"</string>
     <string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nicht aktivieren"</string>
     <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"AN"</string>
     <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"AUS"</string>
-    <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> die vollständige Kontrolle über dein Gerät geben?"</string>
+    <string name="accessibility_enable_service_title" msgid="3931558336268541484">"„<xliff:g id="SERVICE">%1$s</xliff:g>“ die vollständige Kontrolle über dein Gerät geben?"</string>
     <string name="accessibility_service_warning_description" msgid="291674995220940133">"Die vollständige Kontrolle sollte nur für Apps aktiviert werden, die dir Zugang zu App-Funktionen erleichtern. Das ist in der Regel nur ein kleiner Teil der Apps."</string>
     <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Bildschirm aufrufen und steuern"</string>
     <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Die Funktion kann alle Inhalte auf dem Bildschirm lesen und diese Inhalte über andere Apps anzeigen."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 0791c38..43f4172 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -295,11 +295,11 @@
     <string name="user_owner_label" msgid="8628726904184471211">"Vaihda henkilökohtaiseen profiiliin"</string>
     <string name="managed_profile_label" msgid="7316778766973512382">"Vaihda työprofiiliin"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"Yhteystiedot"</string>
-    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"käyttää yhteystietoja"</string>
+    <string name="permgroupdesc_contacts" msgid="9163927941244182567">"pääsy yhteystietoihin"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"Sijainti"</string>
-    <string name="permgroupdesc_location" msgid="1995955142118450685">"käyttää laitteen sijaintia"</string>
+    <string name="permgroupdesc_location" msgid="1995955142118450685">"pääsy laitteen sijaintiin"</string>
     <string name="permgrouplab_calendar" msgid="6426860926123033230">"Kalenteri"</string>
-    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"käyttää kalenteria"</string>
+    <string name="permgroupdesc_calendar" msgid="6762751063361489379">"pääsy kalenteriin"</string>
     <string name="permgrouplab_sms" msgid="795737735126084874">"Tekstiviestit"</string>
     <string name="permgroupdesc_sms" msgid="5726462398070064542">"lähettää ja tarkastella tekstiviestejä"</string>
     <string name="permgrouplab_storage" msgid="17339216290379241">"Tiedostot"</string>
@@ -309,7 +309,7 @@
     <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"Valokuvat ja videot"</string>
     <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"pääsy laitteen kuviin ja videoihin"</string>
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"Mikrofoni"</string>
-    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"tallentaa ääntä"</string>
+    <string name="permgroupdesc_microphone" msgid="1047786732792487722">"tallentaa audiota"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Liikkuminen"</string>
     <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"nähdä liikkumistietosi"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"Kamera"</string>
@@ -321,7 +321,7 @@
     <string name="permgrouplab_phone" msgid="570318944091926620">"Puhelin"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"soittaa ja hallinnoida puheluita"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"Kehon anturit"</string>
-    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"käyttää anturitietoja elintoiminnoistasi"</string>
+    <string name="permgroupdesc_sensors" msgid="2610631290633747752">"pääsy anturidataan elintoiminnoistasi"</string>
     <string name="permgrouplab_notifications" msgid="5472972361980668884">"Ilmoitukset"</string>
     <string name="permgroupdesc_notifications" msgid="4608679556801506580">"näyttää ilmoituksia"</string>
     <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Noutaa ikkunan sisältöä"</string>
@@ -476,7 +476,7 @@
     <string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Tällä sovelluksella on pääsy sijaintitietoihin milloin tahansa, myös silloin kun sovellusta ei käytetä."</string>
     <string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"muuta ääniasetuksia"</string>
     <string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Antaa sovelluksen muokata yleisiä ääniasetuksia, kuten äänenvoimakkuutta ja käytettävää kaiutinta."</string>
-    <string name="permlab_recordAudio" msgid="1208457423054219147">"tallentaa ääntä"</string>
+    <string name="permlab_recordAudio" msgid="1208457423054219147">"tallentaa audiota"</string>
     <string name="permdesc_recordAudio" msgid="5857246765327514062">"Tämä sovellus voi tallentaa mikrofonilla audiota, kun sovellusta käytetään."</string>
     <string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"tallentaa audiota taustalla"</string>
     <string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Tämä sovellus voi tallentaa mikrofonilla audiota koska tahansa."</string>
@@ -1170,7 +1170,7 @@
     <string name="not_selected" msgid="410652016565864475">"ei valittu"</string>
     <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{1/{max} tähteä}other{#/{max} tähteä}}"</string>
     <string name="in_progress" msgid="2149208189184319441">"käynnissä"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen sovellusta"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Tee toiminto käyttäen:"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Suorita sovelluksella %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Suorita toiminto"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Avaa sovelluksessa"</string>
@@ -1975,7 +1975,7 @@
     <string name="pin_specific_target" msgid="7824671240625957415">"Kiinnitä <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="unpin_target" msgid="3963318576590204447">"Irrota"</string>
     <string name="unpin_specific_target" msgid="3859828252160908146">"Irrota <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="app_info" msgid="6113278084877079851">"Sovelluksen tiedot"</string>
+    <string name="app_info" msgid="6113278084877079851">"Sovellustiedot"</string>
     <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="demo_starting_message" msgid="6577581216125805905">"Aloitetaan esittelyä…"</string>
     <string name="demo_restarting_message" msgid="1160053183701746766">"Palautetaan asetuksia…"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index e93a05e..4a21b76 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1170,7 +1170,7 @@
     <string name="not_selected" msgid="410652016565864475">"inte valt"</string>
     <string name="rating_label" msgid="1837085249662154601">"{rating,plural, =1{En stjärna av {max}}other{# stjärnor av {max}}}"</string>
     <string name="in_progress" msgid="2149208189184319441">"pågår"</string>
-    <string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd genom att använda"</string>
+    <string name="whichApplication" msgid="5432266899591255759">"Slutför åtgärd med"</string>
     <string name="whichApplicationNamed" msgid="6969946041713975681">"Slutför åtgärden med %1$s"</string>
     <string name="whichApplicationLabel" msgid="7852182961472531728">"Slutför åtgärd"</string>
     <string name="whichViewApplication" msgid="5733194231473132945">"Öppna med"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 8a221a4..b31f920 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -311,7 +311,7 @@
     <string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
     <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ఆడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"ఫిజికల్ యాక్టివిటీ"</string>
-    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"భౌతిక యాక్టివిటీని యాక్సెస్ చేయండి"</string>
+    <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"ఫిజికల్ యాక్టివిటీని యాక్సెస్ చేయండి"</string>
     <string name="permgrouplab_camera" msgid="9090413408963547706">"కెమెరా"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"చిత్రాలను తీయడానికి మరియు వీడియోను రికార్డ్ చేయడానికి"</string>
     <string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"సమీపంలోని పరికరాలు"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 388bc95..b754440 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4715,6 +4715,9 @@
     <!-- Title of Reduce Brightness feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
     <string name="reduce_bright_colors_feature_name">Extra dim</string>
 
+    <!-- Title of hearing aids feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
+    <string name="hearing_aids_feature_name">Hearing devices</string>
+
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cd39e59..9b04c6b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3516,12 +3516,13 @@
 
   <java-symbol type="drawable" name="ic_accessibility_color_inversion" />
   <java-symbol type="drawable" name="ic_accessibility_color_correction" />
+  <java-symbol type="drawable" name="ic_accessibility_hearing_aid" />
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
   <java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
   <java-symbol type="drawable" name="ic_accessibility_one_handed" />
 
+  <java-symbol type="string" name="hearing_aids_feature_name" />
   <java-symbol type="string" name="reduce_bright_colors_feature_name" />
-
   <java-symbol type="string" name="one_handed_mode_feature_name" />
 
   <!-- com.android.internal.widget.RecyclerView -->
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index 6baf305..a68c392 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -21,6 +21,12 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ONE_HANDED_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -90,7 +96,6 @@
 import java.util.Map;
 import java.util.Set;
 
-
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityShortcutControllerTest {
     private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
@@ -429,7 +434,7 @@
 
     @Test
     public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() {
-        Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo>
+        Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo>
                 frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0);
@@ -443,6 +448,19 @@
     }
 
     @Test
+    public void getFrameworkFeatureMap_containsExpectedKey() {
+        Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo>
+                frameworkFeatureMap =
+                AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
+
+        assertTrue(frameworkFeatureMap.containsKey(COLOR_INVERSION_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(DALTONIZER_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(ONE_HANDED_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(REDUCE_BRIGHT_COLORS_COMPONENT_NAME));
+        assertTrue(frameworkFeatureMap.containsKey(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME));
+    }
+
+    @Test
     public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash()
             throws Exception {
         configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
index e36e16c..5d38494 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityAnimation.java
@@ -171,6 +171,7 @@
         mInitialTouchPos.set(0, 0);
         mEnteringWindowShow = false;
         mEnteringMargin = 0;
+        mEnteringAnimator = null;
 
         if (mFinishCallback != null) {
             try {
@@ -276,7 +277,7 @@
         }
 
         // End the fade in animation.
-        if (mEnteringAnimator.isRunning()) {
+        if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
             mEnteringAnimator.cancel();
         }
 
@@ -329,12 +330,10 @@
         @Override
         public void onBackCancelled() {
             // End the fade in animation.
-            if (mEnteringAnimator.isRunning()) {
+            if (mEnteringAnimator != null && mEnteringAnimator.isRunning()) {
                 mEnteringAnimator.cancel();
             }
-            // TODO (b259608500): Let BackProgressAnimator could play cancel animation.
-            mProgressAnimator.reset();
-            finishAnimation();
+            mProgressAnimator.onBackCancelled(CrossActivityAnimation.this::finishAnimation);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
index 676e259..99a434a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java
@@ -329,8 +329,7 @@
 
         @Override
         public void onBackCancelled() {
-            mProgressAnimator.reset();
-            finishAnimation();
+            mProgressAnimator.onBackCancelled(CrossTaskBackAnimation.this::finishAnimation);
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
new file mode 100644
index 0000000..3608474
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.wm.shell.back;
+
+import static android.window.BackEvent.EDGE_LEFT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.window.BackEvent;
+import android.window.BackMotionEvent;
+import android.window.BackProgressAnimator;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@TestableLooper.RunWithLooper
+@RunWith(AndroidTestingRunner.class)
+public class BackProgressAnimatorTest {
+    private BackProgressAnimator mProgressAnimator;
+    private BackEvent mReceivedBackEvent;
+    private float mTargetProgress = 0.5f;
+    private CountDownLatch mTargetProgressCalled = new CountDownLatch(1);
+    private Handler mMainThreadHandler;
+
+    @Before
+    public void setUp() throws Exception {
+        mMainThreadHandler = new Handler(Looper.getMainLooper());
+        final BackMotionEvent backEvent = new BackMotionEvent(
+                0, 0,
+                0, EDGE_LEFT, null);
+        mMainThreadHandler.post(
+                () -> {
+                    mProgressAnimator = new BackProgressAnimator();
+                    mProgressAnimator.onBackStarted(backEvent, this::onGestureProgress);
+                });
+    }
+
+    @Test
+    public void testBackProgressed() throws InterruptedException {
+        final BackMotionEvent backEvent = new BackMotionEvent(
+                100, 0,
+                mTargetProgress, EDGE_LEFT, null);
+        mMainThreadHandler.post(
+                () -> mProgressAnimator.onBackProgressed(backEvent));
+
+        mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+
+        assertNotNull(mReceivedBackEvent);
+        assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+    }
+
+    @Test
+    public void testBackCancelled() throws InterruptedException {
+        // Give the animator some progress.
+        final BackMotionEvent backEvent = new BackMotionEvent(
+                100, 0,
+                mTargetProgress, EDGE_LEFT, null);
+        mMainThreadHandler.post(
+                () -> mProgressAnimator.onBackProgressed(backEvent));
+        mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+        assertNotNull(mReceivedBackEvent);
+
+        // Trigger animation cancel, the target progress should be 0.
+        mTargetProgress = 0;
+        mTargetProgressCalled = new CountDownLatch(1);
+        CountDownLatch cancelCallbackCalled = new CountDownLatch(1);
+        mMainThreadHandler.post(
+                () -> mProgressAnimator.onBackCancelled(() -> cancelCallbackCalled.countDown()));
+        cancelCallbackCalled.await(1, TimeUnit.SECONDS);
+        mTargetProgressCalled.await(1, TimeUnit.SECONDS);
+        assertNotNull(mReceivedBackEvent);
+        assertEquals(mReceivedBackEvent.getProgress(), mTargetProgress, 0 /* delta */);
+    }
+
+    private void onGestureProgress(BackEvent backEvent) {
+        if (mTargetProgress == backEvent.getProgress()) {
+            mReceivedBackEvent = backEvent;
+            mTargetProgressCalled.countDown();
+        }
+    }
+}
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index e8648cc..1022d93 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -353,6 +353,8 @@
     final Set<String> mDeduplicationIds;
     final Bundle mExtras;
     final String mProviderId;
+    final boolean mIsVisibilityRestricted;
+    final Set<String> mAllowedPackages;
 
     MediaRoute2Info(@NonNull Builder builder) {
         mId = builder.mId;
@@ -372,6 +374,8 @@
         mDeduplicationIds = builder.mDeduplicationIds;
         mExtras = builder.mExtras;
         mProviderId = builder.mProviderId;
+        mIsVisibilityRestricted = builder.mIsVisibilityRestricted;
+        mAllowedPackages = builder.mAllowedPackages;
     }
 
     MediaRoute2Info(@NonNull Parcel in) {
@@ -393,6 +397,8 @@
         mDeduplicationIds = Set.of(in.readStringArray());
         mExtras = in.readBundle();
         mProviderId = in.readString();
+        mIsVisibilityRestricted = in.readBoolean();
+        mAllowedPackages = Set.of(in.createString8Array());
     }
 
     /**
@@ -628,6 +634,15 @@
     }
 
     /**
+     * Returns whether this route is visible to the package with the given name.
+     * @hide
+     */
+    public boolean isVisibleTo(String packageName) {
+        return !mIsVisibilityRestricted || getPackageName().equals(packageName)
+                || mAllowedPackages.contains(packageName);
+    }
+
+    /**
      * Dumps the current state of the object to the given {@code pw} as a human-readable string.
      *
      * <p> Used in the context of dumpsys. </p>
@@ -655,6 +670,8 @@
         pw.println(indent + "mDeduplicationIds=" + mDeduplicationIds);
         pw.println(indent + "mExtras=" + mExtras);
         pw.println(indent + "mProviderId=" + mProviderId);
+        pw.println(indent + "mIsVisibilityRestricted=" + mIsVisibilityRestricted);
+        pw.println(indent + "mAllowedPackages=" + mAllowedPackages);
     }
 
     private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
@@ -705,7 +722,9 @@
                 && (mVolume == other.mVolume)
                 && Objects.equals(mAddress, other.mAddress)
                 && Objects.equals(mDeduplicationIds, other.mDeduplicationIds)
-                && Objects.equals(mProviderId, other.mProviderId);
+                && Objects.equals(mProviderId, other.mProviderId)
+                && (mIsVisibilityRestricted == other.mIsVisibilityRestricted)
+                && Objects.equals(mAllowedPackages, other.mAllowedPackages);
     }
 
     @Override
@@ -713,7 +732,8 @@
         // Note: mExtras is not included.
         return Objects.hash(mId, mName, mFeatures, mType, mIsSystem, mIconUri, mDescription,
                 mConnectionState, mClientPackageName, mPackageName, mVolumeHandling, mVolumeMax,
-                mVolume, mAddress, mDeduplicationIds, mProviderId);
+                mVolume, mAddress, mDeduplicationIds, mProviderId, mIsVisibilityRestricted,
+                mAllowedPackages);
     }
 
     @Override
@@ -733,6 +753,8 @@
                 .append(", volume=").append(getVolume())
                 .append(", deduplicationIds=").append(String.join(",", getDeduplicationIds()))
                 .append(", providerId=").append(getProviderId())
+                .append(", isVisibilityRestricted=").append(mIsVisibilityRestricted)
+                .append(", allowedPackages=").append(String.join(",", mAllowedPackages))
                 .append(" }");
         return result.toString();
     }
@@ -761,6 +783,8 @@
         dest.writeStringArray(mDeduplicationIds.toArray(new String[mDeduplicationIds.size()]));
         dest.writeBundle(mExtras);
         dest.writeString(mProviderId);
+        dest.writeBoolean(mIsVisibilityRestricted);
+        dest.writeString8Array(mAllowedPackages.toArray(new String[0]));
     }
 
     /**
@@ -787,6 +811,8 @@
         Set<String> mDeduplicationIds;
         Bundle mExtras;
         String mProviderId;
+        boolean mIsVisibilityRestricted;
+        Set<String> mAllowedPackages;
 
         /**
          * Constructor for builder to create {@link MediaRoute2Info}.
@@ -809,6 +835,7 @@
             mName = name;
             mFeatures = new ArrayList<>();
             mDeduplicationIds = Set.of();
+            mAllowedPackages = Set.of();
         }
 
         /**
@@ -854,6 +881,8 @@
                 mExtras = new Bundle(routeInfo.mExtras);
             }
             mProviderId = routeInfo.mProviderId;
+            mIsVisibilityRestricted = routeInfo.mIsVisibilityRestricted;
+            mAllowedPackages = routeInfo.mAllowedPackages;
         }
 
         /**
@@ -1057,6 +1086,29 @@
         }
 
         /**
+         * Sets the visibility of this route to public. This is the default
+         * visibility for routes that are public to all other apps.
+         */
+        @NonNull
+        public Builder setVisibilityPublic() {
+            mIsVisibilityRestricted = false;
+            mAllowedPackages = Set.of();
+            return this;
+        }
+
+        /**
+         * Sets the visibility of this route to restricted. This means that the
+         * route is only visible to a set of package name.
+         * @param allowedPackages set of package names which are allowed to see this route.
+         */
+        @NonNull
+        public Builder setVisibilityRestricted(@NonNull Set<String> allowedPackages) {
+            mIsVisibilityRestricted = true;
+            mAllowedPackages = Set.copyOf(allowedPackages);
+            return this;
+        }
+
+        /**
          * Builds the {@link MediaRoute2Info media route info}.
          *
          * @throws IllegalArgumentException if no features are added.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index aea6bcb..3abfc629 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -247,7 +247,6 @@
         return getTransferableRoutes(sessions.get(sessions.size() - 1));
     }
 
-
     /**
      * Gets available routes for the given routing session.
      * The returned routes can be passed to
@@ -313,9 +312,15 @@
                 mDiscoveryPreferenceMap.getOrDefault(packageName, RouteDiscoveryPreference.EMPTY);
 
         for (MediaRoute2Info route : getSortedRoutes(discoveryPreference)) {
-            if (sessionInfo.getTransferableRoutes().contains(route.getId())
-                    || (includeSelectedRoutes
-                    && sessionInfo.getSelectedRoutes().contains(route.getId()))) {
+            if (!route.isVisibleTo(packageName)) {
+                continue;
+            }
+            boolean transferableRoutesContainRoute =
+                    sessionInfo.getTransferableRoutes().contains(route.getId());
+            boolean selectedRoutesContainRoute =
+                    sessionInfo.getSelectedRoutes().contains(route.getId());
+            if (transferableRoutesContainRoute
+                    || (includeSelectedRoutes && selectedRoutesContainRoute)) {
                 routes.add(route);
                 continue;
             }
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index b1d74d4..b03653c0 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -19,6 +19,9 @@
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -40,6 +43,18 @@
  */
 public final class RouteListingPreference implements Parcelable {
 
+    /**
+     * {@link Intent} action for apps to take the user to a screen for transferring media playback
+     * to the route with the id provided by the extra with key {@link #EXTRA_ROUTE_ID}.
+     */
+    public static final String ACTION_TRANSFER_MEDIA = "android.media.action.TRANSFER_MEDIA";
+
+    /**
+     * {@link Intent} string extra key that contains the {@link Item#getRouteId() id} of the route
+     * to transfer to, as part of an {@link #ACTION_TRANSFER_MEDIA} intent.
+     */
+    public static final String EXTRA_ROUTE_ID = "android.media.extra.ROUTE_ID";
+
     @NonNull
     public static final Creator<RouteListingPreference> CREATOR =
             new Creator<>() {
@@ -56,10 +71,12 @@
 
     @NonNull private final List<Item> mItems;
     private final boolean mUseSystemOrdering;
+    @Nullable private final ComponentName mInAppOnlyItemRoutingReceiver;
 
     private RouteListingPreference(Builder builder) {
         mItems = builder.mItems;
         mUseSystemOrdering = builder.mUseSystemOrdering;
+        mInAppOnlyItemRoutingReceiver = builder.mInAppOnlyItemRoutingReceiver;
     }
 
     private RouteListingPreference(Parcel in) {
@@ -67,6 +84,7 @@
                 in.readParcelableList(new ArrayList<>(), Item.class.getClassLoader(), Item.class);
         mItems = List.copyOf(items);
         mUseSystemOrdering = in.readBoolean();
+        mInAppOnlyItemRoutingReceiver = ComponentName.readFromParcel(in);
     }
 
     /**
@@ -90,6 +108,21 @@
         return mUseSystemOrdering;
     }
 
+    /**
+     * Returns a {@link ComponentName} for handling routes disabled via {@link
+     * Item#DISABLE_REASON_IN_APP_ONLY}, or null if the user needs to manually navigate to the app
+     * in order to route to select the corresponding routes.
+     *
+     * <p>If the user selects an {@link Item} disabled via {@link Item#DISABLE_REASON_IN_APP_ONLY},
+     * and this method returns a non-null {@link ComponentName}, the system takes the user back to
+     * the app by launching an intent to the returned {@link ComponentName}, using action {@link
+     * #ACTION_TRANSFER_MEDIA}, with the extra {@link #EXTRA_ROUTE_ID}.
+     */
+    @Nullable
+    public ComponentName getInAppOnlyItemRoutingReceiver() {
+        return mInAppOnlyItemRoutingReceiver;
+    }
+
     // RouteListingPreference Parcelable implementation.
 
     @Override
@@ -101,6 +134,7 @@
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeParcelableList(mItems, flags);
         dest.writeBoolean(mUseSystemOrdering);
+        ComponentName.writeToParcel(mInAppOnlyItemRoutingReceiver, dest);
     }
 
     // Equals and hashCode.
@@ -114,12 +148,15 @@
             return false;
         }
         RouteListingPreference that = (RouteListingPreference) other;
-        return mItems.equals(that.mItems) && mUseSystemOrdering == that.mUseSystemOrdering;
+        return mItems.equals(that.mItems)
+                && mUseSystemOrdering == that.mUseSystemOrdering
+                && Objects.equals(
+                        mInAppOnlyItemRoutingReceiver, that.mInAppOnlyItemRoutingReceiver);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mItems, mUseSystemOrdering);
+        return Objects.hash(mItems, mUseSystemOrdering, mInAppOnlyItemRoutingReceiver);
     }
 
     /** Builder for {@link RouteListingPreference}. */
@@ -127,6 +164,7 @@
 
         private List<Item> mItems;
         private boolean mUseSystemOrdering;
+        private ComponentName mInAppOnlyItemRoutingReceiver;
 
         /** Creates a new instance with default values (documented in the setters). */
         public Builder() {
@@ -159,6 +197,18 @@
         }
 
         /**
+         * See {@link #getInAppOnlyItemRoutingReceiver()}.
+         *
+         * <p>The default value is {@code null}.
+         */
+        @NonNull
+        public Builder setInAppOnlyItemRoutingReceiver(
+                @Nullable ComponentName inAppOnlyItemRoutingReceiver) {
+            mInAppOnlyItemRoutingReceiver = inAppOnlyItemRoutingReceiver;
+            return this;
+        }
+
+        /**
          * Creates and returns a new {@link RouteListingPreference} instance with the given
          * parameters.
          */
@@ -203,7 +253,8 @@
                     DISABLE_REASON_NONE,
                     DISABLE_REASON_SUBSCRIPTION_REQUIRED,
                     DISABLE_REASON_DOWNLOADED_CONTENT,
-                    DISABLE_REASON_AD
+                    DISABLE_REASON_AD,
+                    DISABLE_REASON_IN_APP_ONLY
                 })
         public @interface DisableReason {}
 
@@ -221,6 +272,14 @@
         public static final int DISABLE_REASON_DOWNLOADED_CONTENT = 2;
         /** The corresponding route is not available because an ad is in progress. */
         public static final int DISABLE_REASON_AD = 3;
+        /**
+         * The corresponding route is only available for routing from within the app.
+         *
+         * <p>The user may still select the corresponding route if the app provides an {@link
+         * #getInAppOnlyItemRoutingReceiver() in-app routing receiver}, in which case the system
+         * will take the user to the app.
+         */
+        public static final int DISABLE_REASON_IN_APP_ONLY = 4;
 
         @NonNull
         public static final Creator<Item> CREATOR =
@@ -257,7 +316,11 @@
             Preconditions.checkArgument(mSessionParticipantCount >= 0);
         }
 
-        /** Returns the id of the route that corresponds to this route listing preference item. */
+        /**
+         * Returns the id of the route that corresponds to this route listing preference item.
+         *
+         * @see MediaRoute2Info#getId()
+         */
         @NonNull
         public String getRouteId() {
             return mRouteId;
@@ -282,6 +345,7 @@
          * @see #DISABLE_REASON_SUBSCRIPTION_REQUIRED
          * @see #DISABLE_REASON_DOWNLOADED_CONTENT
          * @see #DISABLE_REASON_AD
+         * @see #DISABLE_REASON_IN_APP_ONLY
          */
         @DisableReason
         public int getDisableReason() {
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index c410d64..5bd15a9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -158,7 +158,7 @@
     <!-- Apps permission will be granted of corresponding profile [CHAR LIMIT=30] -->
     <string name="permission_app_streaming">Apps</string>
 
-    <!-- Nearby_device_streaming permission will be granted to the corresponding profile [CHAR LIMIT=30] -->
+    <!-- Nearby_device_streaming permission will be granted to the corresponding profile [CHAR LIMIT=45] -->
     <string name="permission_nearby_device_streaming">Nearby Device Streaming</string>
 
     <!-- Description of phone permission of corresponding profile [CHAR LIMIT=NONE] -->
diff --git a/packages/CredentialManager/res/values-ar/strings.xml b/packages/CredentialManager/res/values-ar/strings.xml
index e99e357..c0ff69d 100644
--- a/packages/CredentialManager/res/values-ar/strings.xml
+++ b/packages/CredentialManager/res/values-ar/strings.xml
@@ -10,44 +10,32 @@
     <string name="string_use_another_device" msgid="8754514926121520445">"استخدام جهاز آخر"</string>
     <string name="string_save_to_another_device" msgid="1959562542075194458">"الحفظ على جهاز آخر"</string>
     <string name="passkey_creation_intro_title" msgid="4251037543787718844">"توفير المزيد من الأمان باستخدام مفاتيح المرور"</string>
-    <!-- no translation found for passkey_creation_intro_body_password (8825872426579958200) -->
-    <skip />
-    <!-- no translation found for passkey_creation_intro_body_fingerprint (7331338631826254055) -->
-    <skip />
-    <!-- no translation found for passkey_creation_intro_body_device (1203796455762131631) -->
-    <skip />
+    <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"باستخدام مفاتيح المرور، لا حاجة لإنشاء كلمات مرور معقدة أو تذكّرها."</string>
+    <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"مفاتيح المرور هي مفاتيح رقمية مشفّرة يمكنك إنشاؤها باستخدام بصمة الإصبع أو التعرّف على الوجه أو قفل الشاشة."</string>
+    <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"يتم حفظها في مدير كلمات مرور، حتى تتمكن من تسجيل الدخول على أجهزة أخرى."</string>
     <string name="choose_provider_title" msgid="7245243990139698508">"اختيار مكان <xliff:g id="CREATETYPES">%1$s</xliff:g>"</string>
     <string name="create_your_passkeys" msgid="8901224153607590596">"إنشاء مفاتيح مرورك"</string>
     <string name="save_your_password" msgid="6597736507991704307">"حفظ كلمة المرور"</string>
     <string name="save_your_sign_in_info" msgid="7213978049817076882">"حفظ معلومات تسجيل الدخول"</string>
-    <!-- no translation found for choose_provider_body (4384188171872005547) -->
-    <skip />
-    <!-- no translation found for choose_create_option_passkey_title (5220979185879006862) -->
-    <skip />
-    <!-- no translation found for choose_create_option_password_title (7097275038523578687) -->
-    <skip />
-    <!-- no translation found for choose_create_option_sign_in_title (4124872317613421249) -->
-    <skip />
-    <!-- no translation found for choose_create_option_description (5531335144879100664) -->
-    <skip />
+    <string name="choose_provider_body" msgid="4384188171872005547">"اختَر مدير كلمة مرور لحفظ معلوماتك وتسجيل الدخول بشكل أسرع في المرة القادمة."</string>
+    <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"إنشاء مفتاح مرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\""</string>
+    <string name="choose_create_option_password_title" msgid="7097275038523578687">"هل تريد حفظ كلمة المرور لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
+    <string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"هل تريد حفظ معلومات تسجيل الدخول لتطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\"؟"</string>
+    <string name="choose_create_option_description" msgid="5531335144879100664">"يمكنك استخدام <xliff:g id="CREDENTIALTYPES">%2$s</xliff:g> لتطبيق \"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g>\" على أي جهاز. يتم حفظه في \"<xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>\" للحساب \"<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g>\"."</string>
     <string name="passkey" msgid="632353688396759522">"مفتاح مرور"</string>
     <string name="password" msgid="6738570945182936667">"كلمة المرور"</string>
     <string name="sign_ins" msgid="4710739369149469208">"عمليات تسجيل الدخول"</string>
-    <!-- no translation found for sign_in_info (2627704710674232328) -->
-    <skip />
-    <!-- no translation found for save_credential_to_title (3172811692275634301) -->
-    <skip />
+    <string name="sign_in_info" msgid="2627704710674232328">"معلومات تسجيل الدخول"</string>
+    <string name="save_credential_to_title" msgid="3172811692275634301">"حفظ <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> في"</string>
     <string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"هل تريد إنشاء مفتاح مرور في جهاز آخر؟"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"هل تريد استخدام \"<xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g>\" لكل عمليات تسجيل الدخول؟"</string>
     <string name="use_provider_for_all_description" msgid="6560593199974037820">"ستخزِّن خدمة إدارة كلمات المرور هذه كلمات المرور ومفاتيح المرور لمساعدتك في تسجيل الدخول بسهولة."</string>
     <string name="set_as_default" msgid="4415328591568654603">"ضبط الخيار كتلقائي"</string>
     <string name="use_once" msgid="9027366575315399714">"الاستخدام مرة واحدة"</string>
-    <!-- no translation found for more_options_usage_passwords_passkeys (3470113942332934279) -->
-    <skip />
+    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> كلمة مرور • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> مفتاح مرور"</string>
     <string name="more_options_usage_passwords" msgid="1632047277723187813">"عدد كلمات المرور: <xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>"</string>
     <string name="more_options_usage_passkeys" msgid="5390320437243042237">"عدد مفاتيح المرور: <xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>"</string>
-    <!-- no translation found for more_options_usage_credentials (1785697001787193984) -->
-    <skip />
+    <string name="more_options_usage_credentials" msgid="1785697001787193984">"بيانات الاعتماد: <xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g>"</string>
     <string name="passkey_before_subtitle" msgid="2448119456208647444">"مفتاح مرور"</string>
     <string name="another_device" msgid="5147276802037801217">"جهاز آخر"</string>
     <string name="other_password_manager" msgid="565790221427004141">"خدمات مدراء كلمات المرور الأخرى"</string>
@@ -58,8 +46,7 @@
     <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"هل تريد استخدام بيانات اعتماد تسجيل الدخول المحفوظة لتطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"؟"</string>
     <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"اختيار بيانات اعتماد تسجيل دخول محفوظة لـ \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"تسجيل الدخول بطريقة أخرى"</string>
-    <!-- no translation found for snackbar_action (37373514216505085) -->
-    <skip />
+    <string name="snackbar_action" msgid="37373514216505085">"عرض الخيارات"</string>
     <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"متابعة"</string>
     <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"خيارات تسجيل الدخول"</string>
     <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"معلومات تسجيل دخول \"<xliff:g id="USERNAME">%1$s</xliff:g>\""</string>
diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml
index f2a91f5..18681fe 100644
--- a/packages/CredentialManager/res/values-ja/strings.xml
+++ b/packages/CredentialManager/res/values-ja/strings.xml
@@ -10,44 +10,32 @@
     <string name="string_use_another_device" msgid="8754514926121520445">"別のデバイスを使用"</string>
     <string name="string_save_to_another_device" msgid="1959562542075194458">"他のデバイスに保存"</string>
     <string name="passkey_creation_intro_title" msgid="4251037543787718844">"パスキーでより安全に"</string>
-    <!-- no translation found for passkey_creation_intro_body_password (8825872426579958200) -->
-    <skip />
-    <!-- no translation found for passkey_creation_intro_body_fingerprint (7331338631826254055) -->
-    <skip />
-    <!-- no translation found for passkey_creation_intro_body_device (1203796455762131631) -->
-    <skip />
+    <string name="passkey_creation_intro_body_password" msgid="8825872426579958200">"パスキーがあれば、複雑なパスワードを作成したり覚えたりする必要はありません"</string>
+    <string name="passkey_creation_intro_body_fingerprint" msgid="7331338631826254055">"パスキーは、指紋認証、顔認証、または画面ロックを使って作成される暗号化されたデジタルキーです"</string>
+    <string name="passkey_creation_intro_body_device" msgid="1203796455762131631">"パスワード マネージャーに保存され、他のデバイスでもログインできます"</string>
     <string name="choose_provider_title" msgid="7245243990139698508">"<xliff:g id="CREATETYPES">%1$s</xliff:g> の保存場所の選択"</string>
     <string name="create_your_passkeys" msgid="8901224153607590596">"パスキーの作成"</string>
     <string name="save_your_password" msgid="6597736507991704307">"パスワードを保存"</string>
     <string name="save_your_sign_in_info" msgid="7213978049817076882">"ログイン情報を保存"</string>
-    <!-- no translation found for choose_provider_body (4384188171872005547) -->
-    <skip />
-    <!-- no translation found for choose_create_option_passkey_title (5220979185879006862) -->
-    <skip />
-    <!-- no translation found for choose_create_option_password_title (7097275038523578687) -->
-    <skip />
-    <!-- no translation found for choose_create_option_sign_in_title (4124872317613421249) -->
-    <skip />
-    <!-- no translation found for choose_create_option_description (5531335144879100664) -->
-    <skip />
+    <string name="choose_provider_body" msgid="4384188171872005547">"パスワード マネージャーを選択して情報を保存しておくと、次回からすばやくログインできます。"</string>
+    <string name="choose_create_option_passkey_title" msgid="5220979185879006862">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスキーを作成しますか?"</string>
+    <string name="choose_create_option_password_title" msgid="7097275038523578687">"<xliff:g id="APPNAME">%1$s</xliff:g> のパスワードを保存しますか?"</string>
+    <string name="choose_create_option_sign_in_title" msgid="4124872317613421249">"<xliff:g id="APPNAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string>
+    <string name="choose_create_option_description" msgid="5531335144879100664">"<xliff:g id="APPDOMAINNAME">%1$s</xliff:g> の<xliff:g id="CREDENTIALTYPES">%2$s</xliff:g>はどのデバイスでも使用できます。<xliff:g id="CREATEINFODISPLAYNAME">%4$s</xliff:g> の <xliff:g id="PROVIDERINFODISPLAYNAME">%3$s</xliff:g>に保存されます。"</string>
     <string name="passkey" msgid="632353688396759522">"パスキー"</string>
     <string name="password" msgid="6738570945182936667">"パスワード"</string>
     <string name="sign_ins" msgid="4710739369149469208">"ログイン"</string>
-    <!-- no translation found for sign_in_info (2627704710674232328) -->
-    <skip />
-    <!-- no translation found for save_credential_to_title (3172811692275634301) -->
-    <skip />
+    <string name="sign_in_info" msgid="2627704710674232328">"ログイン情報"</string>
+    <string name="save_credential_to_title" msgid="3172811692275634301">"<xliff:g id="CREDENTIALTYPES">%1$s</xliff:g>の保存先"</string>
     <string name="create_passkey_in_other_device_title" msgid="6372952459932674632">"他のデバイスでパスキーを作成しますか?"</string>
     <string name="use_provider_for_all_title" msgid="4201020195058980757">"ログインのたびに <xliff:g id="PROVIDERINFODISPLAYNAME">%1$s</xliff:g> を使用しますか?"</string>
     <string name="use_provider_for_all_description" msgid="6560593199974037820">"このパスワード マネージャーに、パスワードやパスキーが保存され、簡単にログインできるようになります。"</string>
     <string name="set_as_default" msgid="4415328591568654603">"デフォルトに設定"</string>
     <string name="use_once" msgid="9027366575315399714">"1 回使用"</string>
-    <!-- no translation found for more_options_usage_passwords_passkeys (3470113942332934279) -->
-    <skip />
+    <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> 件のパスキー"</string>
     <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> 件のパスワード"</string>
     <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> 件のパスキー"</string>
-    <!-- no translation found for more_options_usage_credentials (1785697001787193984) -->
-    <skip />
+    <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> 件の認証情報"</string>
     <string name="passkey_before_subtitle" msgid="2448119456208647444">"パスキー"</string>
     <string name="another_device" msgid="5147276802037801217">"別のデバイス"</string>
     <string name="other_password_manager" msgid="565790221427004141">"他のパスワード マネージャー"</string>
@@ -58,8 +46,7 @@
     <string name="get_dialog_title_use_sign_in_for" msgid="5283099528915572980">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報を使用しますか?"</string>
     <string name="get_dialog_title_choose_sign_in_for" msgid="1361715440877613701">"<xliff:g id="APP_NAME">%1$s</xliff:g> の保存したログイン情報の選択"</string>
     <string name="get_dialog_use_saved_passkey_for" msgid="4618100798664888512">"別の方法でログイン"</string>
-    <!-- no translation found for snackbar_action (37373514216505085) -->
-    <skip />
+    <string name="snackbar_action" msgid="37373514216505085">"オプションを表示"</string>
     <string name="get_dialog_button_label_continue" msgid="6446201694794283870">"続行"</string>
     <string name="get_dialog_title_sign_in_options" msgid="2092876443114893618">"ログイン オプション"</string>
     <string name="get_dialog_heading_for_username" msgid="3456868514554204776">"<xliff:g id="USERNAME">%1$s</xliff:g> 用"</string>
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
index 5d8502d..0f5e935 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/ProgressBar.kt
@@ -30,9 +30,11 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.drawscope.DrawScope
+import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 
 /**
@@ -62,13 +64,15 @@
 }
 
 private fun DrawScope.drawLinearBar(
-    endFraction: Float,
+    progress: Float,
     color: Color,
     roundedCorner: Boolean
 ) {
-    val width = endFraction * size.width
+    val isLtr = layoutDirection == LayoutDirection.Ltr
+    val width = progress * size.width
     drawRoundRect(
         color = color,
+        topLeft = if (isLtr) Offset.Zero else Offset((1 - progress) * size.width, 0f),
         size = Size(width, size.height),
         cornerRadius = if (roundedCorner) CornerRadius(
             size.height / 2,
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 63dbc7c..a2c59ff 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -564,10 +564,8 @@
     <string name="user_add_user_title" msgid="5457079143694924885">"新しいユーザーを追加しますか?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"追加ユーザーを作成して、このデバイスを他のユーザーと共有できます。各ユーザーは各自のスペースを所有して、アプリや壁紙などのカスタマイズを行うことができます。Wi-Fi など、すべてのユーザーに影響するデバイス設定を変更することもできます。\n\n新しく追加したユーザーは各自でスペースをセットアップする必要があります。\n\nすべてのユーザーがアプリを更新でき、その影響は他のユーザーにも及びます。ユーザー補助機能の設定とサービスは新しいユーザーに適用されないことがあります。"</string>
     <string name="user_add_user_message_short" msgid="3295959985795716166">"新しく追加したユーザーは各自でスペースをセットアップする必要があります。\n\nすべてのユーザーがアプリを更新でき、その影響は他のユーザーにも及びます。"</string>
-    <!-- no translation found for user_grant_admin_title (5565796912475193314) -->
-    <skip />
-    <!-- no translation found for user_grant_admin_message (7925257971286380976) -->
-    <skip />
+    <string name="user_grant_admin_title" msgid="5565796912475193314">"このユーザーに管理者権限を付与しますか?"</string>
+    <string name="user_grant_admin_message" msgid="7925257971286380976">"管理者は、他のユーザーの管理、デバイスの設定の変更、デバイスの出荷時設定へのリセットを行えます。"</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"ユーザーを今すぐセットアップ"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"ユーザーがデバイスを使って各自のスペースをセットアップできるようにします"</string>
     <string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"プロファイルを今すぐセットアップしますか?"</string>
@@ -599,10 +597,8 @@
     <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"新しいゲスト セッションが開始し、現在のセッションのすべてのアプリとデータが削除されます"</string>
     <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ゲストモードを終了しますか?"</string>
     <string name="guest_exit_dialog_message" msgid="1743218864242719783">"現在のゲスト セッションからすべてのアプリとデータが削除されます"</string>
-    <!-- no translation found for grant_admin (4273077214151417783) -->
-    <skip />
-    <!-- no translation found for not_grant_admin (6985027675930546850) -->
-    <skip />
+    <string name="grant_admin" msgid="4273077214151417783">"このユーザーに管理者権限を付与します"</string>
+    <string name="not_grant_admin" msgid="6985027675930546850">"ユーザーに管理者権限を付与しません"</string>
     <string name="guest_exit_dialog_button" msgid="1736401897067442044">"終了"</string>
     <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ゲストアクティビティの保存"</string>
     <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"現在のセッションのアクティビティの保存や、すべてのアプリとデータの削除を行えます"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index c9b1f33..81c2a00 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -564,7 +564,7 @@
     <string name="user_add_user_title" msgid="5457079143694924885">"要新增使用者嗎?"</string>
     <string name="user_add_user_message_long" msgid="1527434966294733380">"你可以建立其他使用者,藉此與他人共用這個裝置。每位使用者都有自己的專屬空間,並可使用應用程式、桌布等項目自訂個人空間。此外,使用者也可以調整會影響所有人的裝置設定,例如 Wi‑Fi 設定。\n\n新增的使用者需要自行設定個人空間。\n\n任何使用者都可以為所有其他使用者更新應用程式。無障礙設定和服務可能無法轉移到新的使用者。"</string>
     <string name="user_add_user_message_short" msgid="3295959985795716166">"新增的使用者需要自行設定個人空間。\n\n任何使用者皆可為其他所有使用者更新應用程式。"</string>
-    <string name="user_grant_admin_title" msgid="5565796912475193314">"要授予這位使用者管理員權限嗎?"</string>
+    <string name="user_grant_admin_title" msgid="5565796912475193314">"要將管理員權限授予此使用者嗎?"</string>
     <string name="user_grant_admin_message" msgid="7925257971286380976">"取得管理員權限後,就能管理其他使用者、修改裝置設定,以及將裝置恢復原廠設定。"</string>
     <string name="user_setup_dialog_title" msgid="8037342066381939995">"立即設定使用者?"</string>
     <string name="user_setup_dialog_message" msgid="269931619868102841">"請確保對方可以使用裝置並設定自己的空間"</string>
@@ -597,8 +597,8 @@
     <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"如果重設,系統會開始新的訪客工作階段,並刪除目前工作階段中的所有應用程式和資料"</string>
     <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string>
     <string name="guest_exit_dialog_message" msgid="1743218864242719783">"如果結束,系統會刪除目前訪客工作階段中的所有應用程式和資料"</string>
-    <string name="grant_admin" msgid="4273077214151417783">"授予這位使用者管理員權限"</string>
-    <string name="not_grant_admin" msgid="6985027675930546850">"不授予這位使用者管理員權限"</string>
+    <string name="grant_admin" msgid="4273077214151417783">"將管理員權限授予這位使用者"</string>
+    <string name="not_grant_admin" msgid="6985027675930546850">"不要將管理員權限授予使用者"</string>
     <string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string>
     <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string>
     <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"你可以儲存目前工作階段中的活動,也可以刪除所有應用程式和資料"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 3903404..cd6609e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -39,7 +39,7 @@
 
     BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
             MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
-        super(context, routerManager, info, packageName);
+        super(context, routerManager, info, packageName, null);
         mCachedDevice = device;
         mAudioManager = context.getSystemService(AudioManager.class);
         initDeviceRecord();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 1b5ce8fe..6fb5555 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -25,6 +25,7 @@
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -40,11 +41,16 @@
     private static final String TAG = "InfoMediaDevice";
 
     InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
-            String packageName) {
-        super(context, routerManager, info, packageName);
+            String packageName, RouteListingPreference.Item item) {
+        super(context, routerManager, info, packageName, item);
         initDeviceRecord();
     }
 
+    InfoMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+            String packageName) {
+        this(context, routerManager, info, packageName, null);
+    }
+
     @Override
     public String getName() {
         return mRouteInfo.getName().toString();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index c7bfe12..2bdbb16 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -58,7 +58,9 @@
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.stream.Collectors;
@@ -82,6 +84,8 @@
 
     private MediaDevice mCurrentConnectedDevice;
     private LocalBluetoothManager mBluetoothManager;
+    private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
+            new ConcurrentHashMap<>();
 
     public InfoMediaManager(Context context, String packageName, Notification notification,
             LocalBluetoothManager localBluetoothManager) {
@@ -248,7 +252,7 @@
         if (info != null) {
             for (MediaRoute2Info route : mRouterManager.getSelectableRoutes(info)) {
                 deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
-                        route, mPackageName));
+                        route, mPackageName, mPreferenceItemMap.get(route.getId())));
             }
             return deviceList;
         }
@@ -275,7 +279,7 @@
         if (info != null) {
             for (MediaRoute2Info route : mRouterManager.getDeselectableRoutes(info)) {
                 deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
-                        route, mPackageName));
+                        route, mPackageName, mPreferenceItemMap.get(route.getId())));
                 Log.d(TAG, route.getName() + " is deselectable for " + mPackageName);
             }
             return deviceList;
@@ -302,7 +306,7 @@
         if (info != null) {
             for (MediaRoute2Info route : mRouterManager.getSelectedRoutes(info)) {
                 deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
-                        route, mPackageName));
+                        route, mPackageName, mPreferenceItemMap.get(route.getId())));
             }
             return deviceList;
         }
@@ -510,7 +514,7 @@
             case TYPE_GROUP:
                 //TODO(b/148765806): use correct device type once api is ready.
                 mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
-                        mPackageName);
+                        mPackageName, mPreferenceItemMap.get(route.getId()));
                 if (!TextUtils.isEmpty(mPackageName)
                         && getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
                     mediaDevice.setState(STATE_SELECTED);
@@ -601,6 +605,16 @@
         public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
             refreshDevices();
         }
+
+        @Override
+        public void onRouteListingPreferenceUpdated(
+                String packageName,
+                RouteListingPreference routeListingPreference) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+                Api34Impl.onRouteListingPreferenceUpdated(packageName, routeListingPreference,
+                        mPreferenceItemMap);
+            }
+        }
     }
 
     @RequiresApi(34)
@@ -666,5 +680,18 @@
             return routeListingPreference != null
                     && !routeListingPreference.getUseSystemOrdering();
         }
+
+        @DoNotInline
+        static void onRouteListingPreferenceUpdated(
+                String packageName,
+                RouteListingPreference routeListingPreference,
+                Map<String, RouteListingPreference.Item> preferenceItemMap) {
+            preferenceItemMap.clear();
+            if (routeListingPreference != null) {
+                routeListingPreference.getItems().forEach((item) -> {
+                    preferenceItemMap.put(item.getRouteId(), item);
+                });
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 5c97f37..aec1767 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -92,8 +92,6 @@
     @VisibleForTesting
     List<MediaDevice> mDisconnectedMediaDevices = new CopyOnWriteArrayList<>();
     @VisibleForTesting
-    MediaDevice mPhoneDevice;
-    @VisibleForTesting
     MediaDevice mCurrentConnectedDevice;
     @VisibleForTesting
     DeviceAttributeChangeCallback mDeviceAttributeChangeCallback =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 3ba51d2..b73e7a3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -30,6 +30,7 @@
 import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE;
 
 import static com.android.settingslib.media.LocalMediaManager.MediaDeviceState.STATE_SELECTED;
 
@@ -39,10 +40,14 @@
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.media.NearbyDevice;
+import android.media.RouteListingPreference;
+import android.os.Build;
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.DoNotInline;
 import androidx.annotation.IntDef;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -87,14 +92,16 @@
     protected final Context mContext;
     protected final MediaRoute2Info mRouteInfo;
     protected final MediaRouter2Manager mRouterManager;
+    protected final RouteListingPreference.Item mItem;
     protected final String mPackageName;
 
     MediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
-            String packageName) {
+            String packageName, RouteListingPreference.Item item) {
         mContext = context;
         mRouteInfo = info;
         mRouterManager = routerManager;
         mPackageName = packageName;
+        mItem = item;
         setType(info);
     }
 
@@ -180,10 +187,21 @@
 
     /**
      * Get unique ID that represent MediaDevice
+     *
      * @return unique id of MediaDevice
      */
     public abstract String getId();
 
+    /**
+     * Checks if device is suggested device from application
+     *
+     * @return true if device is suggested device
+     */
+    public boolean isSuggestedDevice() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+                && Api34Impl.isSuggestedDevice(mItem);
+    }
+
     void setConnectedRecord() {
         mConnectedRecord++;
         ConnectionRecordManager.getInstance().setConnectionRecord(mContext, getId(),
@@ -466,4 +484,12 @@
         final MediaDevice otherDevice = (MediaDevice) obj;
         return otherDevice.getId().equals(getId());
     }
+
+    @RequiresApi(34)
+    private static class Api34Impl {
+        @DoNotInline
+        static boolean isSuggestedDevice(RouteListingPreference.Item item) {
+            return item != null && item.getFlags() == FLAG_SUGGESTED_ROUTE;
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 921c245..de16d4a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -51,7 +51,7 @@
 
     PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
             String packageName) {
-        super(context, routerManager, info, packageName);
+        super(context, routerManager, info, packageName, null);
         mDeviceIconUtil = new DeviceIconUtil();
         initDeviceRecord();
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 9260013..2c8aa26 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -257,14 +257,17 @@
                 Build.VERSION_CODES.UPSIDE_DOWN_CAKE);
         final List<RouteListingPreference.Item> preferenceItemList = new ArrayList<>();
         RouteListingPreference.Item item1 = new RouteListingPreference.Item.Builder(
-                TEST_ID_4).build();
+                TEST_ID_4).setFlags(RouteListingPreference.Item.FLAG_SUGGESTED_ROUTE).build();
         RouteListingPreference.Item item2 = new RouteListingPreference.Item.Builder(
                 TEST_ID_3).build();
         preferenceItemList.add(item1);
         preferenceItemList.add(item2);
+
+        RouteListingPreference routeListingPreference =
+                new RouteListingPreference.Builder().setItems(
+                        preferenceItemList).setUseSystemOrdering(false).build();
         when(mRouterManager.getRouteListingPreference(TEST_PACKAGE_NAME))
-                .thenReturn(new RouteListingPreference.Builder().setItems(
-                        preferenceItemList).setUseSystemOrdering(false).build());
+                .thenReturn(routeListingPreference);
 
         final List<MediaRoute2Info> selectedRoutes = new ArrayList<>();
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
@@ -284,11 +287,13 @@
         setTransferableRoutesList();
 
         mInfoMediaManager.mRouterManager = mRouterManager;
-
+        mInfoMediaManager.mMediaRouterCallback.onRouteListingPreferenceUpdated(TEST_PACKAGE_NAME,
+                routeListingPreference);
         mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
 
         assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
         assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
+        assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue();
         assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
         assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
     }
@@ -620,6 +625,7 @@
         mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
         mShadowRouter2Manager.setDeselectableRoutes(mediaRoute2Infos);
         when(mediaRoute2Info.getName()).thenReturn(TEST_NAME);
+        when(mediaRoute2Info.getId()).thenReturn(TEST_ID);
 
         final List<MediaDevice> mediaDevices = mInfoMediaManager.getDeselectableMediaDevice();
 
@@ -873,21 +879,25 @@
         final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
 
         when(route2Info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
+        when(route2Info.getId()).thenReturn(TEST_ID);
         mInfoMediaManager.addMediaDevice(route2Info);
         assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof InfoMediaDevice).isTrue();
 
         when(route2Info.getType()).thenReturn(TYPE_USB_DEVICE);
+        when(route2Info.getId()).thenReturn(TEST_ID);
         mInfoMediaManager.mMediaDevices.clear();
         mInfoMediaManager.addMediaDevice(route2Info);
         assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
 
         when(route2Info.getType()).thenReturn(TYPE_WIRED_HEADSET);
+        when(route2Info.getId()).thenReturn(TEST_ID);
         mInfoMediaManager.mMediaDevices.clear();
         mInfoMediaManager.addMediaDevice(route2Info);
         assertThat(mInfoMediaManager.mMediaDevices.get(0) instanceof PhoneMediaDevice).isTrue();
 
         when(route2Info.getType()).thenReturn(TYPE_BLUETOOTH_A2DP);
         when(route2Info.getAddress()).thenReturn("00:00:00:00:00:00");
+        when(route2Info.getId()).thenReturn(TEST_ID);
         when(mLocalBluetoothManager.getCachedDeviceManager())
                 .thenReturn(cachedBluetoothDeviceManager);
         when(cachedBluetoothDeviceManager.findDevice(any(BluetoothDevice.class)))
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index c8d186f..d87fc54 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -266,18 +266,15 @@
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
         final MediaDevice device3 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
         devices.add(device2);
         mLocalMediaManager.mMediaDevices.add(device3);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
 
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
         when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
-        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
@@ -290,14 +287,12 @@
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
         devices.add(device2);
         mLocalMediaManager.mMediaDevices.add(device1);
         mLocalMediaManager.mMediaDevices.add(device2);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback
                 .onDeviceListRemoved(mLocalMediaManager.mMediaDevices);
@@ -312,19 +307,17 @@
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
         final MediaDevice device3 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
         devices.add(device3);
         mLocalMediaManager.mMediaDevices.add(device1);
         mLocalMediaManager.mMediaDevices.add(device2);
         mLocalMediaManager.mMediaDevices.add(device3);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(4);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
@@ -452,9 +445,7 @@
     public void onDeviceListAdded_haveMutingExpectedDevice_addMutingExpectedDevice() {
         final List<MediaDevice> devices = new ArrayList<>();
         final MediaDevice device1 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
-        devices.add(mLocalMediaManager.mPhoneDevice);
 
         final List<LocalBluetoothProfile> profiles = new ArrayList<>();
         final A2dpProfile a2dpProfile = mock(A2dpProfile.class);
@@ -483,13 +474,12 @@
 
         when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
         when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE);
-        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
 
         assertThat(mLocalMediaManager.mMediaDevices).hasSize(0);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
         verify(mCallback).onDeviceListUpdate(any());
     }
 
@@ -536,17 +526,13 @@
         final MediaDevice device1 = mock(MediaDevice.class);
         final MediaDevice device2 = mock(MediaDevice.class);
         final MediaDevice device3 = mock(MediaDevice.class);
-        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
         devices.add(device1);
         devices.add(device2);
         mLocalMediaManager.mMediaDevices.add(device3);
-        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
 
         mShadowBluetoothAdapter = null;
 
-        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
-
-        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(1);
         mLocalMediaManager.registerCallback(mCallback);
         mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
 
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index 6d8847c..a3c0554 100644
--- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml
+++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
@@ -37,6 +37,7 @@
 
         <Button
             android:id="@+id/magnifier_edit_button"
+            android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/accessibility_magnifier_edit"
@@ -57,21 +58,36 @@
             android:layout_height="56dp"
             android:scaleType="center"
             android:layout_weight="1"
-            android:layout_marginStart="12dp"/>
+            android:layout_marginStart="12dp"
+            android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+            android:padding="@dimen/magnification_switch_button_padding"
+            android:src="@drawable/ic_magnification_menu_small"
+            android:tint="@color/accessibility_magnifier_icon_color"
+            android:tintMode="src_atop" />
 
         <ImageButton
             android:id="@+id/magnifier_medium_button"
             android:layout_width="0dp"
             android:layout_height="56dp"
             android:scaleType="center"
-            android:layout_weight="1"/>
+            android:layout_weight="1"
+            android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+            android:padding="@dimen/magnification_switch_button_padding"
+            android:src="@drawable/ic_magnification_menu_medium"
+            android:tint="@color/accessibility_magnifier_icon_color"
+            android:tintMode="src_atop" />
 
         <ImageButton
             android:id="@+id/magnifier_large_button"
             android:layout_width="0dp"
             android:layout_height="56dp"
             android:scaleType="center"
-            android:layout_weight="1"/>
+            android:layout_weight="1"
+            android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+            android:padding="@dimen/magnification_switch_button_padding"
+            android:src="@drawable/ic_magnification_menu_large"
+            android:tint="@color/accessibility_magnifier_icon_color"
+            android:tintMode="src_atop" />
 
         <ImageButton
             android:id="@+id/magnifier_full_button"
@@ -79,7 +95,12 @@
             android:layout_height="56dp"
             android:scaleType="center"
             android:layout_weight="1"
-            android:layout_marginEnd="12dp"/>
+            android:layout_marginEnd="12dp"
+            android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
+            android:padding="@dimen/magnification_switch_button_padding"
+            android:src="@drawable/ic_open_in_new_fullscreen"
+            android:tint="@color/accessibility_magnifier_icon_color"
+            android:tintMode="src_atop" />
     </LinearLayout>
 
     <LinearLayout
@@ -141,6 +162,7 @@
 
     <Button
         android:id="@+id/magnifier_close_button"
+        android:background="@drawable/accessibility_magnification_setting_view_btn_bg"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="@string/accessibility_magnification_close"
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index ec15d1a..a3dbaad 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -440,6 +440,7 @@
         final int configDiff = newConfig.diff(mConfiguration);
         mConfiguration.setTo(newConfig);
         onConfigurationChanged(configDiff);
+        mWindowMagnificationSettings.onConfigurationChanged(configDiff);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 069c0f6..9f857a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -27,8 +27,6 @@
 import android.content.pm.ActivityInfo;
 import android.graphics.Insets;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -105,14 +103,14 @@
     /** Denotes the Magnification size type. */
     public @interface MagnificationSize {
         int NONE = 0;
-        int SMALL  = 1;
+        int SMALL = 1;
         int MEDIUM = 2;
         int LARGE = 3;
     }
 
     @VisibleForTesting
     WindowMagnificationSettings(Context context, WindowMagnificationSettingsCallback callback,
-                           SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
+            SfVsyncFrameCallbackProvider sfVsyncFrameProvider) {
         mContext = context;
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mWindowManager = mContext.getSystemService(WindowManager.class);
@@ -123,66 +121,10 @@
                 Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, 0,
                 UserHandle.USER_CURRENT) == 1;
 
-        float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0,
-                UserHandle.USER_CURRENT);
-
-        mSettingView = (LinearLayout) View.inflate(context,
-                R.layout.window_magnification_settings_view, null);
-
-        mSettingView.setClickable(true);
-        mSettingView.setFocusable(true);
-        mSettingView.setOnTouchListener(this::onTouch);
-
-        mPanelView = mSettingView.findViewById(R.id.magnifier_panel_view);
-        mSmallButton = mSettingView.findViewById(R.id.magnifier_small_button);
-        mMediumButton = mSettingView.findViewById(R.id.magnifier_medium_button);
-        mLargeButton = mSettingView.findViewById(R.id.magnifier_large_button);
-        mCloseButton = mSettingView.findViewById(R.id.magnifier_close_button);
-        mEditButton = mSettingView.findViewById(R.id.magnifier_edit_button);
-        mChangeModeButton = mSettingView.findViewById(R.id.magnifier_full_button);
-
-        mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_seekbar);
-        mZoomSeekbar.setOnSeekBarChangeListener(new ZoomSeekbarChangeListener());
-        setSeekbarProgress(scale);
-        mAllowDiagonalScrollingSwitch =
-                (Switch) mSettingView.findViewById(R.id.magnifier_horizontal_lock_switch);
-        mAllowDiagonalScrollingSwitch.setChecked(mAllowDiagonalScrolling);
-        mAllowDiagonalScrollingSwitch.setOnCheckedChangeListener((view, checked) -> {
-            toggleDiagonalScrolling();
-        });
+        inflateView();
 
         mParams = createLayoutParams(context);
-        applyResourcesValues();
-
-        mSmallButton.setAccessibilityDelegate(mButtonDelegate);
-        mSmallButton.setOnClickListener(mButtonClickListener);
-
-        mMediumButton.setAccessibilityDelegate(mButtonDelegate);
-        mMediumButton.setOnClickListener(mButtonClickListener);
-
-        mLargeButton.setAccessibilityDelegate(mButtonDelegate);
-        mLargeButton.setOnClickListener(mButtonClickListener);
-
-        mCloseButton.setAccessibilityDelegate(mButtonDelegate);
-        mCloseButton.setOnClickListener(mButtonClickListener);
-
-        mChangeModeButton.setAccessibilityDelegate(mButtonDelegate);
-        mChangeModeButton.setOnClickListener(mButtonClickListener);
-
-        mEditButton.setAccessibilityDelegate(mButtonDelegate);
-        mEditButton.setOnClickListener(mButtonClickListener);
-
         mWindowInsetChangeRunnable = this::onWindowInsetChanged;
-        mSettingView.setOnApplyWindowInsetsListener((v, insets) -> {
-            // Adds a pending post check to avoiding redundant calculation because this callback
-            // is sent frequently when the switch icon window dragged by the users.
-            if (!mSettingView.getHandler().hasCallbacks(mWindowInsetChangeRunnable)) {
-                mSettingView.getHandler().post(mWindowInsetChangeRunnable);
-            }
-            return v.onApplyWindowInsets(insets);
-        });
-
         mGestureDetector = new MagnificationGestureDetector(context,
                 context.getMainThreadHandler(), this);
     }
@@ -230,43 +172,6 @@
         }
     }
 
-    private void applyResourcesValues() {
-        final int padding = mContext.getResources().getDimensionPixelSize(
-                R.dimen.magnification_switch_button_padding);
-
-        PorterDuffColorFilter filter = new PorterDuffColorFilter(mContext.getColor(
-                R.color.accessibility_magnifier_icon_color), PorterDuff.Mode.SRC_ATOP);
-
-        mSmallButton.setImageResource(R.drawable.ic_magnification_menu_small);
-        mSmallButton.setColorFilter(filter);
-        mSmallButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-        mSmallButton.setPadding(padding, padding, padding, padding);
-
-        mMediumButton.setImageResource(R.drawable.ic_magnification_menu_medium);
-        mMediumButton.setColorFilter(filter);
-        mMediumButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-        mMediumButton.setPadding(padding, padding, padding, padding);
-
-        mLargeButton.setImageResource(R.drawable.ic_magnification_menu_large);
-        mLargeButton.setColorFilter(filter);
-        mLargeButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-        mLargeButton.setPadding(padding, padding, padding, padding);
-
-        mChangeModeButton.setImageResource(R.drawable.ic_open_in_new_fullscreen);
-        mChangeModeButton.setColorFilter(filter);
-        mChangeModeButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-        mChangeModeButton.setPadding(padding, padding, padding, padding);
-
-        mCloseButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-        mEditButton.setBackgroundResource(
-                R.drawable.accessibility_magnification_setting_view_btn_bg);
-    }
-
     private final AccessibilityDelegate mButtonDelegate = new AccessibilityDelegate() {
         @Override
         public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -394,6 +299,10 @@
     }
 
     public void hideSettingPanel() {
+        hideSettingPanel(true);
+    }
+
+    public void hideSettingPanel(boolean resetPosition) {
         if (!mIsVisible) {
             return;
         }
@@ -401,8 +310,10 @@
         // Reset button status.
         mWindowManager.removeView(mSettingView);
         mIsVisible = false;
-        mParams.x = 0;
-        mParams.y = 0;
+        if (resetPosition) {
+            mParams.x = 0;
+            mParams.y = 0;
+        }
 
         mContext.unregisterReceiver(mScreenOffReceiver);
     }
@@ -458,9 +369,72 @@
         mZoomSeekbar.setProgress(index);
     }
 
+    void inflateView() {
+        mSettingView = (LinearLayout) View.inflate(mContext,
+                R.layout.window_magnification_settings_view, null);
+
+        mSettingView.setClickable(true);
+        mSettingView.setFocusable(true);
+        mSettingView.setOnTouchListener(this::onTouch);
+
+        mPanelView = mSettingView.findViewById(R.id.magnifier_panel_view);
+        mSmallButton = mSettingView.findViewById(R.id.magnifier_small_button);
+        mMediumButton = mSettingView.findViewById(R.id.magnifier_medium_button);
+        mLargeButton = mSettingView.findViewById(R.id.magnifier_large_button);
+        mCloseButton = mSettingView.findViewById(R.id.magnifier_close_button);
+        mEditButton = mSettingView.findViewById(R.id.magnifier_edit_button);
+        mChangeModeButton = mSettingView.findViewById(R.id.magnifier_full_button);
+
+        mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_seekbar);
+        mZoomSeekbar.setOnSeekBarChangeListener(new ZoomSeekbarChangeListener());
+
+        float scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0,
+                UserHandle.USER_CURRENT);
+        setSeekbarProgress(scale);
+        mAllowDiagonalScrollingSwitch =
+                (Switch) mSettingView.findViewById(R.id.magnifier_horizontal_lock_switch);
+        mAllowDiagonalScrollingSwitch.setChecked(mAllowDiagonalScrolling);
+        mAllowDiagonalScrollingSwitch.setOnCheckedChangeListener((view, checked) -> {
+            toggleDiagonalScrolling();
+        });
+
+        mSmallButton.setAccessibilityDelegate(mButtonDelegate);
+        mSmallButton.setOnClickListener(mButtonClickListener);
+
+        mMediumButton.setAccessibilityDelegate(mButtonDelegate);
+        mMediumButton.setOnClickListener(mButtonClickListener);
+
+        mLargeButton.setAccessibilityDelegate(mButtonDelegate);
+        mLargeButton.setOnClickListener(mButtonClickListener);
+
+        mCloseButton.setAccessibilityDelegate(mButtonDelegate);
+        mCloseButton.setOnClickListener(mButtonClickListener);
+
+        mChangeModeButton.setAccessibilityDelegate(mButtonDelegate);
+        mChangeModeButton.setOnClickListener(mButtonClickListener);
+
+        mEditButton.setAccessibilityDelegate(mButtonDelegate);
+        mEditButton.setOnClickListener(mButtonClickListener);
+
+        mSettingView.setOnApplyWindowInsetsListener((v, insets) -> {
+            // Adds a pending post check to avoiding redundant calculation because this callback
+            // is sent frequently when the switch icon window dragged by the users.
+            if (!mSettingView.getHandler().hasCallbacks(mWindowInsetChangeRunnable)) {
+                mSettingView.getHandler().post(mWindowInsetChangeRunnable);
+            }
+            return v.onApplyWindowInsets(insets);
+        });
+    }
+
     void onConfigurationChanged(int configDiff) {
         if ((configDiff & ActivityInfo.CONFIG_UI_MODE) != 0) {
-            applyResourcesValues();
+            boolean showSettingPanelAfterThemeChange = mIsVisible;
+            hideSettingPanel(/* resetPosition= */ false);
+            inflateView();
+            if (showSettingPanelAfterThemeChange) {
+                showSettingPanel(/* resetPosition= */ false);
+            }
             return;
         }
         if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d85ab8e..3d4347e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1459,16 +1459,16 @@
     public void maybeHandlePendingLock() {
         if (mPendingLock) {
 
-            // The screen off animation is playing, so if we lock now, the foreground app will
-            // vanish and the keyguard will jump-cut in. Delay it, until either:
+            // The screen off animation is playing or is about to be, so if we lock now, the
+            // foreground app will vanish and the keyguard will jump-cut in. Delay it, until either:
             //   - The screen off animation ends. We will call maybeHandlePendingLock from
             //     the end action in UnlockedScreenOffAnimationController#animateInKeyguard.
             //   - The screen off animation is cancelled by the device waking back up. We will call
             //     maybeHandlePendingLock from KeyguardViewMediator#onStartedWakingUp.
-            if (mScreenOffAnimationController.isKeyguardShowDelayed()) {
+            if (mScreenOffAnimationController.shouldDelayKeyguardShow()) {
                 if (DEBUG) {
                     Log.d(TAG, "#maybeHandlePendingLock: not handling because the screen off "
-                            + "animation's isKeyguardShowDelayed() returned true. This should be "
+                            + "animation's shouldDelayKeyguardShow() returned true. This should be "
                             + "handled soon by #onStartedWakingUp, or by the end actions of the "
                             + "screen off animation.");
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 1fdbc99..d5558b2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@
 
     private val configListener =
         object : ConfigurationController.ConfigurationListener {
+            var lastOrientation = -1
 
             override fun onDensityOrFontScaleChanged() {
                 // System font changes should only happen when UMO is offscreen or a flicker may
@@ -200,7 +201,13 @@
             override fun onConfigChanged(newConfig: Configuration?) {
                 if (newConfig == null) return
                 isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
-                updatePlayers(recreateMedia = true)
+                val newOrientation = newConfig.orientation
+                if (lastOrientation != newOrientation) {
+                    // The players actually depend on the orientation possibly, so we have to
+                    // recreate them (at least on large screen devices)
+                    lastOrientation = newOrientation
+                    updatePlayers(recreateMedia = true)
+                }
             }
 
             override fun onUiModeChanged() {
@@ -717,6 +724,9 @@
     private fun updatePlayers(recreateMedia: Boolean) {
         pageIndicator.tintList =
             ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
+        val previousVisibleKey =
+            MediaPlayerData.visiblePlayerKeys()
+                .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
 
         MediaPlayerData.mediaData().forEach { (key, data, isSsMediaRec) ->
             if (isSsMediaRec) {
@@ -741,6 +751,9 @@
                     isSsReactivated = isSsReactivated
                 )
             }
+            if (recreateMedia) {
+                reorderAllPlayers(previousVisibleKey)
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 5196f49..9b0d8db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -444,6 +444,45 @@
         TestableLooper.get(this).processAllMessages();
     }
 
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testKeyguardDelayedOnGoingToSleep_ifScreenOffAnimationWillPlayButIsntPlayingYet() {
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        mViewMediator.setShowingLocked(false);
+        TestableLooper.get(this).processAllMessages();
+
+        mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mScreenOffAnimationController.shouldDelayKeyguardShow()).thenReturn(true);
+        when(mScreenOffAnimationController.isKeyguardShowDelayed()).thenReturn(false);
+        mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
+        TestableLooper.get(this).processAllMessages();
+
+        assertFalse(mViewMediator.isShowingAndNotOccluded());
+    }
+
+    @Test
+    @TestableLooper.RunWithLooper(setAsMainLooper = true)
+    public void testKeyguardNotDelayedOnGoingToSleep_ifScreenOffAnimationWillNotPlay() {
+        mViewMediator.onSystemReady();
+        TestableLooper.get(this).processAllMessages();
+
+        mViewMediator.setShowingLocked(false);
+        TestableLooper.get(this).processAllMessages();
+
+        mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_USER);
+        TestableLooper.get(this).processAllMessages();
+
+        when(mScreenOffAnimationController.shouldDelayKeyguardShow()).thenReturn(false);
+        mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
+        TestableLooper.get(this).processAllMessages();
+
+        assertTrue(mViewMediator.isShowingAndNotOccluded());
+    }
+
     private void createAndStartViewMediator() {
         mViewMediator = new KeyguardViewMediator(
                 mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
index 6ca34e0..039dd4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaCarouselControllerTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media.controls.ui
 
 import android.app.PendingIntent
+import android.content.res.Configuration
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
@@ -86,6 +87,9 @@
     @Mock lateinit var mediaViewController: MediaViewController
     @Mock lateinit var smartspaceMediaData: SmartspaceMediaData
     @Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
+    @Captor
+    lateinit var configListener: ArgumentCaptor<ConfigurationController.ConfigurationListener>
+    @Captor lateinit var newConfig: ArgumentCaptor<Configuration>
     @Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
 
     private val clock = FakeSystemClock()
@@ -111,6 +115,7 @@
                 logger,
                 debugLogger
             )
+        verify(configurationController).addCallback(capture(configListener))
         verify(mediaDataManager).addListener(capture(listener))
         verify(visualStabilityProvider)
             .addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
@@ -662,4 +667,39 @@
         mediaCarouselController.updatePageIndicatorAlpha()
         assertEquals(mediaCarouselController.pageIndicator.alpha, 1.0F, delta)
     }
+
+    @Ignore("b/253229241")
+    @Test
+    fun testOnConfigChanged_playersAreAddedBack() {
+        listener.value.onMediaDataLoaded(
+            "playing local",
+            null,
+            DATA.copy(
+                active = true,
+                isPlaying = true,
+                playbackLocation = MediaData.PLAYBACK_LOCAL,
+                resumption = false
+            )
+        )
+        listener.value.onMediaDataLoaded(
+            "paused local",
+            null,
+            DATA.copy(
+                active = true,
+                isPlaying = false,
+                playbackLocation = MediaData.PLAYBACK_LOCAL,
+                resumption = false
+            )
+        )
+
+        val playersSize = MediaPlayerData.players().size
+
+        configListener.value.onConfigChanged(capture(newConfig))
+
+        assertEquals(playersSize, MediaPlayerData.players().size)
+        assertEquals(
+            MediaPlayerData.getMediaPlayerIndex("playing local"),
+            mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
+        )
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c5598cb..5e3e97d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -33,6 +33,7 @@
 import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
 import static android.view.accessibility.AccessibilityManager.ShortcutType;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
@@ -131,7 +132,8 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
-import com.android.internal.accessibility.AccessibilityShortcutController.ToggleableFrameworkFeatureInfo;
+import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkFeatureInfo;
+import com.android.internal.accessibility.AccessibilityShortcutController.LaunchableFrameworkFeatureInfo;
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
 import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity;
 import com.android.internal.annotations.GuardedBy;
@@ -1810,6 +1812,18 @@
         }
     }
 
+    private void launchAccessibilitySubSettings(int displayId, ComponentName name) {
+        final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
+        final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.putExtra(Intent.EXTRA_COMPONENT_NAME, name.flattenToString());
+        try {
+            mContext.startActivityAsUser(intent, bundle, UserHandle.of(mCurrentUserId));
+        } catch (ActivityNotFoundException ignore) {
+            // ignore the exception
+        }
+    }
+
     private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
         mIsAccessibilityButtonShown = available;
@@ -3245,7 +3259,7 @@
             return;
         }
         // In case user assigned an accessibility framework feature to the given shortcut.
-        if (performAccessibilityFrameworkFeature(targetComponentName, shortcutType)) {
+        if (performAccessibilityFrameworkFeature(displayId, targetComponentName, shortcutType)) {
             return;
         }
         // In case user assigned an accessibility shortcut target to the given shortcut.
@@ -3260,17 +3274,24 @@
         }
     }
 
-    private boolean performAccessibilityFrameworkFeature(ComponentName assignedTarget,
-            @ShortcutType int shortcutType) {
-        final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
+    private boolean performAccessibilityFrameworkFeature(int displayId,
+            ComponentName assignedTarget, @ShortcutType int shortcutType) {
+        final Map<ComponentName, FrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         if (!frameworkFeatureMap.containsKey(assignedTarget)) {
             return false;
         }
-        // Toggle the requested framework feature
-        final ToggleableFrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
+        final FrameworkFeatureInfo featureInfo = frameworkFeatureMap.get(assignedTarget);
         final SettingStringHelper setting = new SettingStringHelper(mContext.getContentResolver(),
                 featureInfo.getSettingKey(), mCurrentUserId);
+
+        if (featureInfo instanceof LaunchableFrameworkFeatureInfo) {
+            logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
+                    /* serviceEnabled= */ true);
+            launchAccessibilityFrameworkFeature(displayId, assignedTarget);
+            return true;
+        }
+
         // Assuming that the default state will be to have the feature off
         if (!TextUtils.equals(featureInfo.getSettingOnValue(), setting.read())) {
             logAccessibilityShortcutActivated(mContext, assignedTarget, shortcutType,
@@ -3374,6 +3395,12 @@
         }
     }
 
+    private void launchAccessibilityFrameworkFeature(int displayId, ComponentName assignedTarget) {
+        if (assignedTarget.equals(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME)) {
+            launchAccessibilitySubSettings(displayId, ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME);
+        }
+    }
+
     @Override
     public List<String> getAccessibilityShortcutTargets(@ShortcutType int shortcutType) {
         if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index cc485ba..cde4ea9 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -31,6 +31,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -285,6 +286,15 @@
     public void setRouteListingPreference(
             @NonNull IMediaRouter2 router,
             @Nullable RouteListingPreference routeListingPreference) {
+        ComponentName inAppOnlyItemRoutingReceiver =
+                routeListingPreference != null
+                        ? routeListingPreference.getInAppOnlyItemRoutingReceiver()
+                        : null;
+        if (inAppOnlyItemRoutingReceiver != null) {
+            MediaServerUtils.enforcePackageName(
+                    inAppOnlyItemRoutingReceiver.getPackageName(), Binder.getCallingUid());
+        }
+
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
@@ -787,6 +797,12 @@
                 obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers,
                         routerRecord.mUserRecord.mHandler,
                         routerRecord.mPackageName, null));
+        routerRecord.mUserRecord.mHandler.sendMessage(
+                obtainMessage(
+                        UserHandler::notifyRouteListingPreferenceChangeToManagers,
+                        routerRecord.mUserRecord.mHandler,
+                        routerRecord.mPackageName,
+                        /* routeListingPreference= */ null));
         userRecord.mHandler.sendMessage(
                 obtainMessage(UserHandler::updateDiscoveryPreferenceOnHandler,
                         userRecord.mHandler));
@@ -1626,7 +1642,7 @@
          * <p>This list contains all routes exposed by route providers. This includes routes from
          * both system route providers and user route providers.
          *
-         * <p>See {@link #getRouters(boolean hasModifyAudioRoutingPermission)}.
+         * <p>See {@link #getRouterRecords(boolean hasModifyAudioRoutingPermission)}.
          */
         private final Map<String, MediaRoute2Info> mLastNotifiedRoutesToPrivilegedRouters =
                 new ArrayMap<>();
@@ -1896,9 +1912,10 @@
             if (!hasAddedOrModifiedRoutes && !hasRemovedRoutes) {
                 return;
             }
-
-            List<IMediaRouter2> routersWithModifyAudioRoutingPermission = getRouters(true);
-            List<IMediaRouter2> routersWithoutModifyAudioRoutingPermission = getRouters(false);
+            List<RouterRecord> routerRecordsWithModifyAudioRoutingPermission =
+                    getRouterRecords(true);
+            List<RouterRecord> routerRecordsWithoutModifyAudioRoutingPermission =
+                    getRouterRecords(false);
             List<IMediaRouter2Manager> managers = getManagers();
 
             // Managers receive all provider updates with all routes.
@@ -1907,22 +1924,22 @@
 
             // Routers with modify audio permission (usually system routers) receive all provider
             // updates with all routes.
-            notifyRoutesUpdatedToRouters(
-                    routersWithModifyAudioRoutingPermission,
+            notifyRoutesUpdatedToRouterRecords(
+                    routerRecordsWithModifyAudioRoutingPermission,
                     new ArrayList<>(mLastNotifiedRoutesToPrivilegedRouters.values()));
 
             if (!isSystemProvider) {
                 // Regular routers receive updates from all non-system providers with all non-system
                 // routes.
-                notifyRoutesUpdatedToRouters(
-                        routersWithoutModifyAudioRoutingPermission,
+                notifyRoutesUpdatedToRouterRecords(
+                        routerRecordsWithoutModifyAudioRoutingPermission,
                         new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
             } else if (hasAddedOrModifiedRoutes) {
                 // On system provider updates, regular routers receive the updated default route.
                 // This is the only system route they should receive.
                 mLastNotifiedRoutesToNonPrivilegedRouters.put(defaultRoute.getId(), defaultRoute);
-                notifyRoutesUpdatedToRouters(
-                        routersWithoutModifyAudioRoutingPermission,
+                notifyRoutesUpdatedToRouterRecords(
+                        routerRecordsWithoutModifyAudioRoutingPermission,
                         new ArrayList<>(mLastNotifiedRoutesToNonPrivilegedRouters.values()));
             }
         }
@@ -2191,8 +2208,8 @@
                 if (mServiceRef.get() == null) {
                     return;
                 }
-                notifySessionInfoChangedToRouters(getRouters(true), sessionInfo);
-                notifySessionInfoChangedToRouters(getRouters(false),
+                notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
+                notifySessionInfoChangedToRouters(getRouterRecords(false),
                         mSystemProvider.getDefaultSessionInfo());
                 return;
             }
@@ -2203,7 +2220,7 @@
                         + sessionInfo);
                 return;
             }
-            notifySessionInfoChangedToRouters(Arrays.asList(routerRecord.mRouter), sessionInfo);
+            notifySessionInfoChangedToRouters(Arrays.asList(routerRecord), sessionInfo);
         }
 
         private void onSessionReleasedOnHandler(@NonNull MediaRoute2Provider provider,
@@ -2316,6 +2333,7 @@
                             == routerRecord.mHasModifyAudioRoutingPermission) {
                         routers.add(routerRecord.mRouter);
                     }
+                    routers.add(routerRecord.mRouter);
                 }
             }
             return routers;
@@ -2345,6 +2363,23 @@
             }
         }
 
+        private List<RouterRecord> getRouterRecords(boolean hasModifyAudioRoutingPermission) {
+            MediaRouter2ServiceImpl service = mServiceRef.get();
+            List<RouterRecord> routerRecords = new ArrayList<>();
+            if (service == null) {
+                return routerRecords;
+            }
+            synchronized (service.mLock) {
+                for (RouterRecord routerRecord : mUserRecord.mRouterRecords) {
+                    if (hasModifyAudioRoutingPermission
+                            == routerRecord.mHasModifyAudioRoutingPermission) {
+                        routerRecords.add(routerRecord);
+                    }
+                }
+                return routerRecords;
+            }
+        }
+
         private List<ManagerRecord> getManagerRecords() {
             MediaRouter2ServiceImpl service = mServiceRef.get();
             if (service == null) {
@@ -2395,22 +2430,45 @@
             }
         }
 
-        private void notifyRoutesUpdatedToRouters(
-                @NonNull List<IMediaRouter2> routers, @NonNull List<MediaRoute2Info> routes) {
-            for (IMediaRouter2 router : routers) {
+        private static void notifyRoutesUpdatedToRouterRecords(
+                @NonNull List<RouterRecord> routerRecords,
+                @NonNull List<MediaRoute2Info> routes) {
+            for (RouterRecord routerRecord: routerRecords) {
+                List<MediaRoute2Info> filteredRoutes = getFilteredRoutesForPackageName(routes,
+                        routerRecord.mPackageName);
                 try {
-                    router.notifyRoutesUpdated(routes);
+                    routerRecord.mRouter.notifyRoutesUpdated(filteredRoutes);
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex);
                 }
             }
         }
 
+        /**
+         * Filters list of routes to return only public routes or routes provided by
+         * the same package name or routes containing this package name in its allow list.
+         * @param routes initial list of routes to be filtered.
+         * @param packageName router's package name to filter routes for it.
+         * @return only the routes that this package name is allowed to see.
+         */
+        private static List<MediaRoute2Info> getFilteredRoutesForPackageName(
+                @NonNull List<MediaRoute2Info> routes,
+                @NonNull String packageName) {
+            List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
+            for (MediaRoute2Info route : routes) {
+                if (route.isVisibleTo(packageName)) {
+                    filteredRoutes.add(route);
+                }
+            }
+            return filteredRoutes;
+        }
+
         private void notifySessionInfoChangedToRouters(
-                @NonNull List<IMediaRouter2> routers, @NonNull RoutingSessionInfo sessionInfo) {
-            for (IMediaRouter2 router : routers) {
+                @NonNull List<RouterRecord> routerRecords,
+                @NonNull RoutingSessionInfo sessionInfo) {
+            for (RouterRecord routerRecord : routerRecords) {
                 try {
-                    router.notifySessionInfoChanged(sessionInfo);
+                    routerRecord.mRouter.notifySessionInfoChanged(sessionInfo);
                 } catch (RemoteException ex) {
                     Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
                 }
diff --git a/services/core/java/com/android/server/media/MediaServerUtils.java b/services/core/java/com/android/server/media/MediaServerUtils.java
index 5fa2b1c..a4a99af 100644
--- a/services/core/java/com/android/server/media/MediaServerUtils.java
+++ b/services/core/java/com/android/server/media/MediaServerUtils.java
@@ -18,7 +18,13 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 
@@ -26,6 +32,39 @@
  * Util class for media server.
  */
 class MediaServerUtils {
+
+    /**
+     * Throws if the given {@code packageName} does not correspond to the given {@code uid}.
+     *
+     * <p>This method trusts calls from {@link Process#ROOT_UID} and {@link Process#SHELL_UID}.
+     *
+     * @param packageName A package name to verify (usually sent over binder by an app).
+     * @param uid The calling uid, obtained via {@link Binder#getCallingUid()}.
+     * @throws IllegalArgumentException If the given {@code packageName} does not correspond to the
+     *     given {@code uid}, and {@code uid} is not the root uid, or the shell uid.
+     */
+    public static void enforcePackageName(String packageName, int uid) {
+        if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
+            return;
+        }
+        if (TextUtils.isEmpty(packageName)) {
+            throw new IllegalArgumentException("packageName may not be empty");
+        }
+        final PackageManagerInternal packageManagerInternal =
+                LocalServices.getService(PackageManagerInternal.class);
+        final int actualUid =
+                packageManagerInternal.getPackageUid(
+                        packageName, 0 /* flags */, UserHandle.getUserId(uid));
+        if (!UserHandle.isSameApp(uid, actualUid)) {
+            throw new IllegalArgumentException(
+                    "packageName does not belong to the calling uid; "
+                            + "pkg="
+                            + packageName
+                            + ", uid="
+                            + uid);
+        }
+    }
+
     /**
      * Verify that caller holds {@link android.Manifest.permission#DUMP}.
      */
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e51ed1b..3a20cd9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -538,30 +538,11 @@
         mHandler.postSessionsChanged(session);
     }
 
-    private void enforcePackageName(String packageName, int uid) {
-        if (TextUtils.isEmpty(packageName)) {
-            throw new IllegalArgumentException("packageName may not be empty");
-        }
-        if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) {
-            // If the caller is shell, then trust the packageName given and allow it
-            // to proceed.
-            return;
-        }
-        final PackageManagerInternal packageManagerInternal =
-                LocalServices.getService(PackageManagerInternal.class);
-        final int actualUid = packageManagerInternal.getPackageUid(
-                packageName, 0 /* flags */, UserHandle.getUserId(uid));
-        if (!UserHandle.isSameApp(uid, actualUid)) {
-            throw new IllegalArgumentException("packageName does not belong to the calling uid; "
-                    + "pkg=" + packageName + ", uid=" + uid);
-        }
-    }
-
     void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
             int callingPid, int callingUid, String callingPackage, String reason) {
         final long token = Binder.clearCallingIdentity();
         try {
-            enforcePackageName(callingPackage, callingUid);
+            MediaServerUtils.enforcePackageName(callingPackage, callingUid);
             if (targetUid != callingUid) {
                 boolean canAllowWhileInUse = mActivityManagerLocal
                         .canAllowWhileInUsePermissionInFgs(callingPid, callingUid, callingPackage);
@@ -1206,7 +1187,7 @@
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(packageName, uid);
                 int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
@@ -1258,7 +1239,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 MediaSessionRecordImpl record;
@@ -1289,7 +1270,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 MediaSessionRecordImpl record;
@@ -1615,7 +1596,7 @@
             final int userId = userHandle.getIdentifier();
             final long token = Binder.clearCallingIdentity();
             try {
-                enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(packageName, uid);
                 enforceMediaPermissions(packageName, pid, uid, userId);
 
                 synchronized (mLock) {
@@ -2129,7 +2110,7 @@
                 // If they gave us a component name verify they own the
                 // package
                 packageName = componentName.getPackageName();
-                enforcePackageName(packageName, uid);
+                MediaServerUtils.enforcePackageName(packageName, uid);
             }
             // Check that they can make calls on behalf of the user and get the final user id
             int resolvedUserId = handleIncomingUser(pid, uid, userId, packageName);
diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index ed31187..abaff4a 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -18,6 +18,7 @@
 
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.PackageManagerService.TAG;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
 
@@ -45,6 +46,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -242,33 +244,39 @@
                 }
             }
 
-            // Prepare the application profiles only for upgrades and
-            // first boot (so that we don't repeat the same operation at
-            // each boot).
-            //
-            // We only have to cover the upgrade and first boot here
-            // because for app installs we prepare the profiles before
-            // invoking dexopt (in installPackageLI).
-            //
-            // We also have to cover non system users because we do not
-            // call the usual install package methods for them.
-            //
-            // NOTE: in order to speed up first boot time we only create
-            // the current profile and do not update the content of the
-            // reference profile. A system image should already be
-            // configured with the right profile keys and the profiles
-            // for the speed-profile prebuilds should already be copied.
-            // That's done in #performDexOptUpgrade.
-            //
-            // TODO(calin, mathieuc): We should use .dm files for
-            // prebuilds profiles instead of manually copying them in
-            // #performDexOptUpgrade. When we do that we should have a
-            // more granular check here and only update the existing
-            // profiles.
-            if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
-                    || (userId != UserHandle.USER_SYSTEM)) {
-                mArtManagerService.prepareAppProfiles(pkg, userId,
-                        /* updateReferenceProfileContent= */ false);
+            if (!useArtService()) { // ART Service handles this on demand instead.
+                // Prepare the application profiles only for upgrades and
+                // first boot (so that we don't repeat the same operation at
+                // each boot).
+                //
+                // We only have to cover the upgrade and first boot here
+                // because for app installs we prepare the profiles before
+                // invoking dexopt (in installPackageLI).
+                //
+                // We also have to cover non system users because we do not
+                // call the usual install package methods for them.
+                //
+                // NOTE: in order to speed up first boot time we only create
+                // the current profile and do not update the content of the
+                // reference profile. A system image should already be
+                // configured with the right profile keys and the profiles
+                // for the speed-profile prebuilds should already be copied.
+                // That's done in #performDexOptUpgrade.
+                //
+                // TODO(calin, mathieuc): We should use .dm files for
+                // prebuilds profiles instead of manually copying them in
+                // #performDexOptUpgrade. When we do that we should have a
+                // more granular check here and only update the existing
+                // profiles.
+                if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()
+                        || (userId != UserHandle.USER_SYSTEM)) {
+                    try {
+                        mArtManagerService.prepareAppProfiles(pkg, userId,
+                                /* updateReferenceProfileContent= */ false);
+                    } catch (LegacyDexoptDisabledException e2) {
+                        throw new RuntimeException(e2);
+                    }
+                }
             }
 
             if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && ceDataInode != -1) {
@@ -578,7 +586,12 @@
             Slog.wtf(TAG, "Package was null!", new Throwable());
             return;
         }
-        mArtManagerService.clearAppProfiles(pkg);
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            mArtManagerService.clearAppProfiles(pkg);
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     public void destroyAppDataLIF(AndroidPackage pkg, int userId, int flags) {
@@ -615,8 +628,11 @@
     }
 
     private void destroyAppProfilesLeafLIF(AndroidPackage pkg) {
+        // TODO(b/251903639): Call into ART Service.
         try {
             mInstaller.destroyAppProfiles(pkg.getPackageName());
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
         } catch (Installer.InstallerException e) {
             Slog.w(TAG, String.valueOf(e));
         }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index fe122f8..9785d47 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -55,9 +55,11 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.FunctionalUtils.ThrowingCheckedSupplier;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.PinnerService;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
@@ -71,7 +73,6 @@
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
 
 /**
  * Controls background dex optimization run as idle job or command line.
@@ -202,7 +203,8 @@
     }
 
     /** Start scheduling job after boot completion */
-    public void systemReady() {
+    public void systemReady() throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         if (mInjector.isBackgroundDexOptDisabled()) {
             return;
         }
@@ -272,7 +274,8 @@
      * @return true if dex optimization is complete. false if the task is cancelled or if there was
      *         an error.
      */
-    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames) {
+    public boolean runBackgroundDexoptJob(@Nullable List<String> packageNames)
+            throws LegacyDexoptDisabledException {
         enforceRootOrShell();
         long identity = Binder.clearCallingIdentity();
         try {
@@ -301,7 +304,8 @@
      *
      * <p>This is only for shell command and only root or shell user can use this.
      */
-    public void cancelBackgroundDexoptJob() {
+    public void cancelBackgroundDexoptJob() throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         enforceRootOrShell();
         Binder.withCleanCallingIdentity(() -> cancelDexOptAndWaitForCompletion());
     }
@@ -315,7 +319,8 @@
      *
      * @param disable True if JobScheduler invocations should be disabled, false otherwise.
      */
-    public void setDisableJobSchedulerJobs(boolean disable) {
+    public void setDisableJobSchedulerJobs(boolean disable) throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         enforceRootOrShell();
         synchronized (mLock) {
             mDisableJobSchedulerJobs = disable;
@@ -323,14 +328,18 @@
     }
 
     /** Adds listener for package update */
-    public void addPackagesUpdatedListener(PackagesUpdatedListener listener) {
+    public void addPackagesUpdatedListener(PackagesUpdatedListener listener)
+            throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         synchronized (mLock) {
             mPackagesUpdatedListeners.add(listener);
         }
     }
 
     /** Removes package update listener */
-    public void removePackagesUpdatedListener(PackagesUpdatedListener listener) {
+    public void removePackagesUpdatedListener(PackagesUpdatedListener listener)
+            throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         synchronized (mLock) {
             mPackagesUpdatedListeners.remove(listener);
         }
@@ -340,7 +349,8 @@
      * Notifies package change and removes the package from the failed package list so that
      * the package can run dexopt again.
      */
-    public void notifyPackageChanged(String packageName) {
+    public void notifyPackageChanged(String packageName) throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         // The idle maintenance job skips packages which previously failed to
         // compile. The given package has changed and may successfully compile
         // now. Remove it from the list of known failing packages.
@@ -387,37 +397,43 @@
                 // Post boot job not finished yet. Run post boot job first.
                 return false;
             }
-            resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread(
-                    "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
-                    () -> {
-                        TimingsTraceAndSlog tr =
-                                new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
-                        tr.traceBegin("jobExecution");
-                        boolean completed = false;
-                        try {
-                            completed = runIdleOptimization(
-                                    pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE);
-                        } finally { // Those cleanup should be done always.
-                            tr.traceEnd();
-                            Slog.i(TAG,
-                                    "dexopt finishing. jobid:" + params.getJobId()
-                                            + " completed:" + completed);
+            try {
+                resetStatesForNewDexOptRunLocked(mInjector.createAndStartThread(
+                        "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"),
+                        () -> {
+                            TimingsTraceAndSlog tr =
+                                    new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER);
+                            tr.traceBegin("jobExecution");
+                            boolean completed = false;
+                            try {
+                                completed = runIdleOptimization(
+                                        pm, pkgs, params.getJobId() == JOB_POST_BOOT_UPDATE);
+                            } catch (LegacyDexoptDisabledException e) {
+                                Slog.wtf(TAG, e);
+                            } finally { // Those cleanup should be done always.
+                                tr.traceEnd();
+                                Slog.i(TAG,
+                                        "dexopt finishing. jobid:" + params.getJobId()
+                                                + " completed:" + completed);
 
-                            writeStatsLog(params);
+                                writeStatsLog(params);
 
-                            if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
-                                if (completed) {
-                                    markPostBootUpdateCompleted(params);
+                                if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
+                                    if (completed) {
+                                        markPostBootUpdateCompleted(params);
+                                    }
+                                    // Reschedule when cancelled
+                                    job.jobFinished(params, !completed);
+                                } else {
+                                    // Periodic job
+                                    job.jobFinished(params, false /* reschedule */);
                                 }
-                                // Reschedule when cancelled
-                                job.jobFinished(params, !completed);
-                            } else {
-                                // Periodic job
-                                job.jobFinished(params, false /* reschedule */);
+                                markDexOptCompleted();
                             }
-                            markDexOptCompleted();
-                        }
-                    }));
+                        }));
+            } catch (LegacyDexoptDisabledException e) {
+                Slog.wtf(TAG, e);
+            }
         }
         return true;
     }
@@ -425,11 +441,16 @@
     /** For BackgroundDexOptJobService to dispatch onStopJob event */
     /* package */ boolean onStopJob(BackgroundDexOptJobService job, JobParameters params) {
         Slog.i(TAG, "onStopJob:" + params.getJobId());
-        // This cannot block as it is in main thread, thus dispatch to a newly created thread and
-        // cancel it from there.
-        // As this event does not happen often, creating a new thread is justified rather than
-        // having one thread kept permanently.
-        mInjector.createAndStartThread("DexOptCancel", this::cancelDexOptAndWaitForCompletion);
+        // This cannot block as it is in main thread, thus dispatch to a newly created thread
+        // and cancel it from there. As this event does not happen often, creating a new thread
+        // is justified rather than having one thread kept permanently.
+        mInjector.createAndStartThread("DexOptCancel", () -> {
+            try {
+                cancelDexOptAndWaitForCompletion();
+            } catch (LegacyDexoptDisabledException e) {
+                Slog.wtf(TAG, e);
+            }
+        });
         // Always reschedule for cancellation.
         return true;
     }
@@ -438,7 +459,7 @@
      * Cancels pending dexopt and wait for completion of the cancellation. This can block the caller
      * until cancellation is done.
      */
-    private void cancelDexOptAndWaitForCompletion() {
+    private void cancelDexOptAndWaitForCompletion() throws LegacyDexoptDisabledException {
         synchronized (mLock) {
             if (mDexOptThread == null) {
                 return;
@@ -496,7 +517,8 @@
     }
 
     @GuardedBy("mLock")
-    private void resetStatesForNewDexOptRunLocked(Thread thread) {
+    private void resetStatesForNewDexOptRunLocked(Thread thread)
+            throws LegacyDexoptDisabledException {
         mDexOptThread = thread;
         mLastCancelledPackages.clear();
         controlDexOptBlockingLocked(false);
@@ -510,7 +532,7 @@
     }
 
     @GuardedBy("mLock")
-    private void controlDexOptBlockingLocked(boolean block) {
+    private void controlDexOptBlockingLocked(boolean block) throws LegacyDexoptDisabledException {
         PackageManagerService pm = mInjector.getPackageManagerService();
         mDexOptHelper.controlDexOptBlocking(block);
     }
@@ -564,8 +586,8 @@
      * Returns whether we've successfully run the job. Note that it will return true even if some
      * packages may have failed compiling.
      */
-    private boolean runIdleOptimization(
-            PackageManagerService pm, List<String> pkgs, boolean isPostBootUpdate) {
+    private boolean runIdleOptimization(PackageManagerService pm, List<String> pkgs,
+            boolean isPostBootUpdate) throws LegacyDexoptDisabledException {
         synchronized (mLock) {
             mLastExecutionStatus = STATUS_UNSPECIFIED;
             mLastExecutionStartUptimeMs = SystemClock.uptimeMillis();
@@ -631,7 +653,8 @@
 
     @Status
     private int idleOptimizePackages(PackageManagerService pm, List<String> pkgs,
-            long lowStorageThreshold, boolean isPostBootUpdate) {
+            long lowStorageThreshold, boolean isPostBootUpdate)
+            throws LegacyDexoptDisabledException {
         ArraySet<String> updatedPackages = new ArraySet<>();
 
         try {
@@ -707,7 +730,8 @@
 
     @Status
     private int optimizePackages(List<String> pkgs, long lowStorageThreshold,
-            ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
+            ArraySet<String> updatedPackages, boolean isPostBootUpdate)
+            throws LegacyDexoptDisabledException {
         boolean supportSecondaryDex = mInjector.supportSecondaryDex();
 
         // Keep the error if there is any error from any package.
@@ -760,7 +784,8 @@
      */
     @DexOptResult
     private int downgradePackage(@NonNull Computer snapshot, PackageManagerService pm, String pkg,
-            boolean isForPrimaryDex, boolean isPostBootUpdate) {
+            boolean isForPrimaryDex, boolean isPostBootUpdate)
+            throws LegacyDexoptDisabledException {
         if (DEBUG) {
             Slog.d(TAG, "Downgrading " + pkg);
         }
@@ -808,7 +833,7 @@
     }
 
     @Status
-    private int reconcileSecondaryDexFiles() {
+    private int reconcileSecondaryDexFiles() throws LegacyDexoptDisabledException {
         // TODO(calin): should we denylist packages for which we fail to reconcile?
         for (String p : mInjector.getDexManager().getAllPackagesWithSecondaryDexFiles()) {
             if (isCancelling()) {
@@ -829,7 +854,8 @@
      * @return PackageDexOptimizer#DEX_OPT_*
      */
     @DexOptResult
-    private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate) {
+    private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate)
+            throws LegacyDexoptDisabledException {
         int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
                                       : PackageManagerService.REASON_BACKGROUND_DEXOPT;
         String filter = getCompilerFilterForReason(reason);
@@ -857,7 +883,8 @@
     }
 
     @DexOptResult
-    private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags) {
+    private int performDexOptPrimary(String pkg, int reason, String filter, int dexoptFlags)
+            throws LegacyDexoptDisabledException {
         DexoptOptions dexoptOptions =
                 new DexoptOptions(pkg, reason, filter, /*splitName=*/null, dexoptFlags);
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/true,
@@ -865,7 +892,8 @@
     }
 
     @DexOptResult
-    private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags) {
+    private int performDexOptSecondary(String pkg, int reason, String filter, int dexoptFlags)
+            throws LegacyDexoptDisabledException {
         DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason, filter, /*splitName=*/null,
                 dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
         return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/false,
@@ -885,8 +913,9 @@
      *  {@link PackageDexOptimizer#DEX_OPT_FAILED}
      */
     @DexOptResult
-    private int trackPerformDexOpt(
-            String pkg, boolean isForPrimaryDex, Supplier<Integer> performDexOptWrapper) {
+    private int trackPerformDexOpt(String pkg, boolean isForPrimaryDex,
+            ThrowingCheckedSupplier<Integer, LegacyDexoptDisabledException> performDexOptWrapper)
+            throws LegacyDexoptDisabledException {
         ArraySet<String> failedPackageNames;
         synchronized (mLock) {
             failedPackageNames =
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index f433d6ad..d321b09 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -124,6 +124,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.PackageDexUsage;
 import com.android.server.pm.parsing.PackageInfoUtils;
@@ -3009,8 +3010,13 @@
                     ipw.println("[" + pkgName + "]");
                     ipw.increaseIndent();
 
-                    mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
-                            mDexManager.getPackageUseInfoOrDefault(pkgName));
+                    // TODO(b/251903639): Call into ART Service.
+                    try {
+                        mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting,
+                                mDexManager.getPackageUseInfoOrDefault(pkgName));
+                    } catch (LegacyDexoptDisabledException e) {
+                        throw new RuntimeException(e);
+                    }
                     ipw.decreaseIndent();
                 }
                 ipw.println("BgDexopt state:");
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 69aaa0d..39aa9c1 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -66,6 +66,8 @@
 import com.android.server.art.model.ArtFlags;
 import com.android.server.art.model.OptimizeParams;
 import com.android.server.art.model.OptimizeResult;
+import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.PackageDexOptimizer.DexOptResult;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
@@ -125,8 +127,9 @@
      * and {@code numberOfPackagesFailed}.
      */
     public int[] performDexOptUpgrade(List<PackageStateInternal> packageStates, boolean showDialog,
-            final int compilationReason, boolean bootComplete) {
-
+            final int compilationReason, boolean bootComplete)
+            throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         int numberOfPackagesVisited = 0;
         int numberOfPackagesOptimized = 0;
         int numberOfPackagesSkipped = 0;
@@ -158,7 +161,7 @@
                             // even if things are already compiled.
                             // useProfileForDexopt = true;
                         }
-                    } catch (Exception e) {
+                    } catch (InstallerException | RuntimeException e) {
                         Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath() + " ",
                                 e);
                     }
@@ -193,7 +196,7 @@
                                 } else {
                                     useProfileForDexopt = true;
                                 }
-                            } catch (Exception e) {
+                            } catch (InstallerException | RuntimeException e) {
                                 Log.e(TAG, "Failed to copy profile "
                                         + profileFile.getAbsolutePath() + " ", e);
                             }
@@ -284,7 +287,8 @@
      * Checks if system UI package (typically "com.android.systemui") needs to be re-compiled, and
      * compiles it if needed.
      */
-    private void checkAndDexOptSystemUi() {
+    private void checkAndDexOptSystemUi() throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         Computer snapshot = mPm.snapshotComputer();
         String sysUiPackageName =
                 mPm.mContext.getString(com.android.internal.R.string.config_systemUi);
@@ -320,7 +324,7 @@
                             Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath());
                         }
                     }
-                } catch (Exception e) {
+                } catch (InstallerException | RuntimeException e) {
                     Log.e(TAG, "Failed to copy profile " + profileFile.getAbsolutePath(), e);
                 }
             }
@@ -341,7 +345,7 @@
     }
 
     @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
-    public void performPackageDexOptUpgradeIfNeeded() {
+    public void performPackageDexOptUpgradeIfNeeded() throws LegacyDexoptDisabledException {
         PackageManagerServiceUtils.enforceSystemOrRoot(
                 "Only the system can request package update");
 
@@ -413,7 +417,12 @@
         }
 
         if (options.isDexoptOnlySecondaryDex()) {
-            return mPm.getDexManager().dexoptSecondaryDex(options);
+            // TODO(b/251903639): Call into ART Service.
+            try {
+                return mPm.getDexManager().dexoptSecondaryDex(options);
+            } catch (LegacyDexoptDisabledException e) {
+                throw new RuntimeException(e);
+            }
         } else {
             int dexoptStatus = performDexOptWithStatus(options);
             return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
@@ -470,6 +479,8 @@
         final long callingId = Binder.clearCallingIdentity();
         try {
             return performDexOptInternalWithDependenciesLI(p, pkgSetting, options);
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
@@ -542,7 +553,8 @@
 
     @DexOptResult
     private int performDexOptInternalWithDependenciesLI(
-            AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options) {
+            AndroidPackage p, @NonNull PackageStateInternal pkgSetting, DexoptOptions options)
+            throws LegacyDexoptDisabledException {
         // System server gets a special path.
         if (PLATFORM_PACKAGE_NAME.equals(p.getPackageName())) {
             return mPm.getDexManager().dexoptSystemServer(options);
@@ -621,7 +633,11 @@
         if (artSrvRes.isPresent()) {
             res = artSrvRes.get();
         } else {
-            res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
+            try {
+                res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
+            } catch (LegacyDexoptDisabledException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
@@ -666,8 +682,8 @@
         return installerPkg.getUid() == Binder.getCallingUid();
     }
 
-    public boolean performDexOptSecondary(String packageName, String compilerFilter,
-            boolean force) {
+    public boolean performDexOptSecondary(
+            String packageName, String compilerFilter, boolean force) {
         int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX
                 | DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES
                 | DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -869,7 +885,7 @@
         }
     }
 
-    /*package*/ void controlDexOptBlocking(boolean block) {
+    /*package*/ void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
         mPm.mPackageDexOptimizer.controlDexOptBlocking(block);
     }
 
@@ -929,10 +945,17 @@
     }
 
     /**
+     * Returns true if ART Service should be used for package optimization.
+     */
+    public static boolean useArtService() {
+        return SystemProperties.getBoolean("dalvik.vm.useartservice", false);
+    }
+
+    /**
      * Returns {@link DexUseManagerLocal} if ART Service should be used for package optimization.
      */
     public static @Nullable DexUseManagerLocal getDexUseManagerLocal() {
-        if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
+        if (!useArtService()) {
             return null;
         }
         try {
@@ -946,7 +969,7 @@
      * Returns {@link ArtManagerLocal} if ART Service should be used for package optimization.
      */
     private static @Nullable ArtManagerLocal getArtManagerLocal() {
-        if (!"true".equals(SystemProperties.get("dalvik.vm.useartservice", ""))) {
+        if (!useArtService()) {
             return null;
         }
         try {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 16bf0fe..6f7484e 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -46,6 +46,7 @@
 import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
 import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
 
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
@@ -157,6 +158,7 @@
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.EventLogTags;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
@@ -261,7 +263,8 @@
         final PackageSetting oldPkgSetting = request.getScanRequestOldPackageSetting();
         final PackageSetting originalPkgSetting = request.getScanRequestOriginalPackageSetting();
         final String realPkgName = request.getRealPackageName();
-        final List<String> changedAbiCodePath = request.getChangedAbiCodePath();
+        final List<String> changedAbiCodePath =
+                useArtService() ? null : request.getChangedAbiCodePath();
         final PackageSetting pkgSetting;
         if (request.getScanRequestPackageSetting() != null) {
             SharedUserSetting requestSharedUserSetting = mPm.mSettings.getSharedUserSettingLPr(
@@ -362,6 +365,8 @@
         }
         pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
 
+        // The conditional on useArtService() for changedAbiCodePath above means this is skipped
+        // when ART Service is in use, since it has its own dex file GC.
         if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
             for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                 final String codePathString = changedAbiCodePath.get(i);
@@ -370,6 +375,8 @@
                         mPm.mInstaller.rmdex(codePathString,
                                 getDexCodeInstructionSet(getPreferredInstructionSet()));
                     }
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
                 } catch (Installer.InstallerException ignored) {
                 }
             }
@@ -2282,12 +2289,18 @@
                         pkg.getBaseApkPath(), pkg.getSplitCodePaths());
             }
 
-            // Prepare the application profiles for the new code paths.
-            // This needs to be done before invoking dexopt so that any install-time profile
-            // can be used for optimizations.
-            mArtManagerService.prepareAppProfiles(
-                    pkg, mPm.resolveUserIds(installRequest.getUserId()),
-                    /* updateReferenceProfileContent= */ true);
+            if (!useArtService()) { // ART Service handles this on demand instead.
+                // Prepare the application profiles for the new code paths.
+                // This needs to be done before invoking dexopt so that any install-time profile
+                // can be used for optimizations.
+                try {
+                    mArtManagerService.prepareAppProfiles(pkg,
+                            mPm.resolveUserIds(installRequest.getUserId()),
+                            /* updateReferenceProfileContent= */ true);
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
+            }
 
             // Compute the compilation reason from the installation scenario.
             final int compilationReason =
@@ -2365,19 +2378,30 @@
 
                 realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
 
-                mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
-                        null /* instructionSets */,
-                        mPm.getOrCreateCompilerPackageStats(pkg),
-                        mDexManager.getPackageUseInfoOrDefault(packageName),
-                        dexoptOptions);
+                // TODO(b/251903639): Call into ART Service.
+                try {
+                    mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
+                            null /* instructionSets */, mPm.getOrCreateCompilerPackageStats(pkg),
+                            mDexManager.getPackageUseInfoOrDefault(packageName), dexoptOptions);
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
-            // Notify BackgroundDexOptService that the package has been changed.
-            // If this is an update of a package which used to fail to compile,
-            // BackgroundDexOptService will remove it from its denylist.
-            // TODO: Layering violation
-            BackgroundDexOptService.getService().notifyPackageChanged(packageName);
+            if (!useArtService()) {
+                // Notify BackgroundDexOptService that the package has been changed.
+                // If this is an update of a package which used to fail to compile,
+                // BackgroundDexOptService will remove it from its denylist.
+                // ART Service currently doesn't support this and will retry packages in every
+                // background dexopt.
+                // TODO: Layering violation
+                try {
+                    BackgroundDexOptService.getService().notifyPackageChanged(packageName);
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
+            }
         }
         PackageManagerServiceUtils.waitForNativeBinariesExtractionForIncremental(
                 incrementalStorages);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index a078b5d..9329f06 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm;
 
+import static com.android.server.pm.DexOptHelper.useArtService;
+
 import android.annotation.AppIdInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -609,11 +611,13 @@
      * @throws InstallerException if {@code dexopt} fails.
      */
     public boolean dexopt(String apkPath, int uid, String pkgName, String instructionSet,
-            int dexoptNeeded, @Nullable String outputPath, int dexFlags,
-            String compilerFilter, @Nullable String volumeUuid, @Nullable String classLoaderContext,
+            int dexoptNeeded, @Nullable String outputPath, int dexFlags, String compilerFilter,
+            @Nullable String volumeUuid, @Nullable String classLoaderContext,
             @Nullable String seInfo, boolean downgrade, int targetSdkVersion,
             @Nullable String profileName, @Nullable String dexMetadataPath,
-            @Nullable String compilationReason) throws InstallerException {
+            @Nullable String compilationReason)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         assertValidInstructionSet(instructionSet);
         BlockGuard.getVmPolicy().onPathAccess(apkPath);
         BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -637,7 +641,8 @@
      *
      * @param block set to true to enable blocking / false to disable blocking.
      */
-    public void controlDexOptBlocking(boolean block) {
+    public void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         try {
             mInstalld.controlDexOptBlocking(block);
         } catch (Exception e) {
@@ -655,7 +660,8 @@
      *         {@link #PROFILE_ANALYSIS_DONT_OPTIMIZE_EMPTY_PROFILES}
      */
     public int mergeProfiles(int uid, String packageName, String profileName)
-            throws InstallerException {
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
         try {
             return mInstalld.mergeProfiles(uid, packageName, profileName);
@@ -668,8 +674,9 @@
      * Dumps profiles associated with a package in a human readable format.
      */
     public boolean dumpProfiles(int uid, String packageName, String profileName, String codePath,
-                                boolean dumpClassesAndMethods)
-            throws InstallerException {
+            boolean dumpClassesAndMethods)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return false;
         BlockGuard.getVmPolicy().onPathAccess(codePath);
         try {
@@ -681,7 +688,8 @@
     }
 
     public boolean copySystemProfile(String systemProfile, int uid, String packageName,
-                String profileName) throws InstallerException {
+            String profileName) throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return false;
         try {
             return mInstalld.copySystemProfile(systemProfile, uid, packageName, profileName);
@@ -690,7 +698,9 @@
         }
     }
 
-    public void rmdex(String codePath, String instructionSet) throws InstallerException {
+    public void rmdex(String codePath, String instructionSet)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         assertValidInstructionSet(instructionSet);
         if (!checkBeforeRemote()) return;
         BlockGuard.getVmPolicy().onPathAccess(codePath);
@@ -714,7 +724,9 @@
         }
     }
 
-    public void clearAppProfiles(String packageName, String profileName) throws InstallerException {
+    public void clearAppProfiles(String packageName, String profileName)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.clearAppProfiles(packageName, profileName);
@@ -723,7 +735,9 @@
         }
     }
 
-    public void destroyAppProfiles(String packageName) throws InstallerException {
+    public void destroyAppProfiles(String packageName)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.destroyAppProfiles(packageName);
@@ -737,7 +751,8 @@
      * @throws InstallerException if the deletion fails.
      */
     public void deleteReferenceProfile(String packageName, String profileName)
-            throws InstallerException {
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.deleteReferenceProfile(packageName, profileName);
@@ -799,7 +814,8 @@
      * Creates an oat dir for given package and instruction set.
      */
     public void createOatDir(String packageName, String oatDir, String dexInstructionSet)
-            throws InstallerException {
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.createOatDir(packageName, oatDir, dexInstructionSet);
@@ -843,7 +859,8 @@
      * of freed bytes.
      */
     public long deleteOdex(String packageName, String apkPath, String instructionSet,
-            String outputPath) throws InstallerException {
+            String outputPath) throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return -1;
         BlockGuard.getVmPolicy().onPathAccess(apkPath);
         BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -855,7 +872,9 @@
     }
 
     public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
-            String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
+            String[] isas, @Nullable String volumeUuid, int flags)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         for (int i = 0; i < isas.length; i++) {
             assertValidInstructionSet(isas[i]);
         }
@@ -881,7 +900,8 @@
     }
 
     public boolean createProfileSnapshot(int appId, String packageName, String profileName,
-            String classpath) throws InstallerException {
+            String classpath) throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return false;
         try {
             return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath);
@@ -891,7 +911,8 @@
     }
 
     public void destroyProfileSnapshot(String packageName, String profileName)
-            throws InstallerException {
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return;
         try {
             mInstalld.destroyProfileSnapshot(packageName, profileName);
@@ -952,7 +973,9 @@
      * </ul>
      */
     public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
-            String profileName, String codePath, String dexMetadataPath) throws InstallerException {
+            String profileName, String codePath, String dexMetadataPath)
+            throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return false;
         BlockGuard.getVmPolicy().onPathAccess(codePath);
         BlockGuard.getVmPolicy().onPathAccess(dexMetadataPath);
@@ -1117,7 +1140,8 @@
      * @throws InstallerException if failed to get the visibility of the optimized artifacts.
      */
     public int getOdexVisibility(String packageName, String apkPath, String instructionSet,
-            String outputPath) throws InstallerException {
+            String outputPath) throws InstallerException, LegacyDexoptDisabledException {
+        checkLegacyDexoptDisabled();
         if (!checkBeforeRemote()) return -1;
         BlockGuard.getVmPolicy().onPathAccess(apkPath);
         BlockGuard.getVmPolicy().onPathAccess(outputPath);
@@ -1137,4 +1161,26 @@
             throw new InstallerException(e.toString());
         }
     }
+
+    /**
+     * A checked exception that is thrown in legacy dexopt code paths when ART Service should be
+     * used instead.
+     */
+    public static class LegacyDexoptDisabledException extends Exception {
+        // TODO(b/260124949): Remove the legacy dexopt code paths, i.e. this exception and all code
+        // that may throw it.
+        public LegacyDexoptDisabledException() {
+            super("Invalid call to legacy dexopt installd method while ART Service is in use.");
+        }
+    }
+
+    /**
+     * Throws LegacyDexoptDisabledException if ART Service should be used instead of the
+     * {@link android.os.IInstalld} method that follows this method call.
+     */
+    public static void checkLegacyDexoptDisabled() throws LegacyDexoptDisabledException {
+        if (useArtService()) {
+            throw new LegacyDexoptDisabledException();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 56f2493..490b2a9 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -37,6 +37,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.pkg.AndroidPackage;
@@ -167,7 +168,13 @@
             Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
                     + DexOptHelper.packagesToString(others));
             for (PackageStateInternal pkg : others) {
-                mPackageManagerService.deleteOatArtifactsOfPackage(snapshot, pkg.getPackageName());
+                // TODO(b/251903639): Call into ART Service.
+                try {
+                    mPackageManagerService.deleteOatArtifactsOfPackage(
+                            snapshot, pkg.getPackageName());
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
             }
         }
         long spaceAvailableNow = getAvailableSpace();
@@ -188,7 +195,7 @@
                             + pkgSetting.getTransientState()
                             .getLatestForegroundPackageUseTimeInMills());
                 }
-            } catch (Exception ignored) {
+            } catch (RuntimeException ignored) {
             }
         }
     }
@@ -352,13 +359,17 @@
         PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
                 collectingInstaller, mPackageManagerService.mInstallLock, mContext);
 
-        optimizer.performDexOpt(pkg, pkgSetting,
-                null /* ISAs */,
-                null /* CompilerStats.PackageStats */,
-                mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
-                        pkg.getPackageName()),
-                new DexoptOptions(pkg.getPackageName(), compilationReason,
-                        DexoptOptions.DEXOPT_BOOT_COMPLETE));
+        // TODO(b/251903639): Allow this use of legacy dexopt code even when ART Service is enabled.
+        try {
+            optimizer.performDexOpt(pkg, pkgSetting, null /* ISAs */,
+                    null /* CompilerStats.PackageStats */,
+                    mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(
+                            pkg.getPackageName()),
+                    new DexoptOptions(pkg.getPackageName(), compilationReason,
+                            DexoptOptions.DEXOPT_BOOT_COMPLETE));
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
 
         return commands;
     }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index aaf8755..0a71892 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -71,6 +71,7 @@
 import com.android.server.LocalServices;
 import com.android.server.apphibernation.AppHibernationManagerInternal;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.ArtStatsLogUtils;
 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger;
@@ -219,7 +220,8 @@
     @DexOptResult
     int performDexOpt(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
             String[] instructionSets, CompilerStats.PackageStats packageStats,
-            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
+            throws LegacyDexoptDisabledException {
         if (PLATFORM_PACKAGE_NAME.equals(pkg.getPackageName())) {
             throw new IllegalArgumentException("System server dexopting should be done via "
                     + " DexManager and PackageDexOptimizer#dexoptSystemServerPath");
@@ -245,7 +247,7 @@
     /**
      * Cancels currently running dex optimization.
      */
-    void controlDexOptBlocking(boolean block) {
+    void controlDexOptBlocking(boolean block) throws LegacyDexoptDisabledException {
         // This method should not hold mInstallLock as cancelling should be possible while
         // the lock is held by other thread running performDexOpt.
         getInstallerWithoutLock().controlDexOptBlocking(block);
@@ -259,7 +261,8 @@
     @DexOptResult
     private int performDexOptLI(AndroidPackage pkg, @NonNull PackageStateInternal pkgSetting,
             String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
-            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
+            PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options)
+            throws LegacyDexoptDisabledException {
         // ClassLoader only refers non-native (jar) shared libraries and must ignore
         // native (so) shared libraries. See also LoadedApk#createSharedLibraryLoader().
         final List<SharedLibraryInfo> sharedLibraries = pkgSetting.getTransientState()
@@ -439,7 +442,7 @@
      */
     @GuardedBy("mInstallLock")
     private boolean prepareCloudProfile(AndroidPackage pkg, String profileName, String path,
-            @Nullable String dexMetadataPath) {
+            @Nullable String dexMetadataPath) throws LegacyDexoptDisabledException {
         if (dexMetadataPath != null) {
             try {
                 // Make sure we don't keep any existing contents.
@@ -472,7 +475,7 @@
             String path, String isa, String compilerFilter, int profileAnalysisResult,
             String classLoaderContext, int dexoptFlags, int uid,
             CompilerStats.PackageStats packageStats, boolean downgrade, String profileName,
-            String dexMetadataPath, int compilationReason) {
+            String dexMetadataPath, int compilationReason) throws LegacyDexoptDisabledException {
         String oatDir = getPackageOatDirIfSupported(pkgSetting, pkg);
 
         int dexoptNeeded = getDexoptNeeded(pkg.getPackageName(), path, isa, compilerFilter,
@@ -525,8 +528,8 @@
      */
     @GuardedBy("mInstallLock")
     @DexOptResult
-    public int dexoptSystemServerPath(
-            String dexPath, PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+    public int dexoptSystemServerPath(String dexPath, PackageDexUsage.DexUseInfo dexUseInfo,
+            DexoptOptions options) throws LegacyDexoptDisabledException {
         int dexoptFlags = DEXOPT_PUBLIC
                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
@@ -601,7 +604,8 @@
      */
     @DexOptResult
     public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
-            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
+            throws LegacyDexoptDisabledException {
         if (info.uid == -1) {
             throw new IllegalArgumentException("Dexopt for path " + path + " has invalid uid.");
         }
@@ -643,7 +647,7 @@
                         + " time out. Operation took " + duration + " ms. Thread: "
                         + Thread.currentThread().getName());
             }
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             Slog.wtf(TAG, "Error while releasing " + mDexoptWakeLock.getTag() + " lock", e);
         }
     }
@@ -651,7 +655,8 @@
     @GuardedBy("mInstallLock")
     @DexOptResult
     private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
-            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+            PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options)
+            throws LegacyDexoptDisabledException {
         String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
                 dexUseInfo.isUsedByOtherApps());
         // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
@@ -729,7 +734,8 @@
      * Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
      */
     void dumpDexoptState(IndentingPrintWriter pw, AndroidPackage pkg,
-            PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo) {
+            PackageStateInternal pkgSetting, PackageDexUsage.PackageUseInfo useInfo)
+            throws LegacyDexoptDisabledException {
         final String[] instructionSets = getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(),
                 pkgSetting.getSecondaryCpuAbi());
         final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
@@ -929,7 +935,8 @@
     @GuardedBy("mInstallLock")
     private int getDexoptNeeded(String packageName, String path, String isa, String compilerFilter,
             String classLoaderContext, int profileAnalysisResult, boolean downgrade,
-            int dexoptFlags, String oatDir) {
+            int dexoptFlags, String oatDir) throws LegacyDexoptDisabledException {
+        Installer.checkLegacyDexoptDisabled();
         final boolean shouldBePublic = (dexoptFlags & DEXOPT_PUBLIC) != 0;
         final boolean isProfileGuidedFilter = (dexoptFlags & DEXOPT_PROFILE_GUIDED) != 0;
         boolean newProfile = profileAnalysisResult == PROFILE_ANALYSIS_OPTIMIZE;
@@ -962,7 +969,7 @@
         } catch (IOException ioe) {
             Slog.w(TAG, "IOException reading apk: " + path, ioe);
             return DEX_OPT_FAILED;
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unexpected exception when calling dexoptNeeded on " + path, e);
             return DEX_OPT_FAILED;
         }
@@ -976,11 +983,12 @@
 
     /** Returns true if the current artifacts of the app are private to the app itself. */
     @GuardedBy("mInstallLock")
-    private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir) {
+    private boolean isOdexPrivate(String packageName, String path, String isa, String oatDir)
+            throws LegacyDexoptDisabledException {
         try {
             return mInstaller.getOdexVisibility(packageName, path, isa, oatDir)
                     == Installer.ODEX_IS_PRIVATE;
-        } catch (Exception e) {
+        } catch (InstallerException e) {
             Slog.w(TAG, "Failed to get odex visibility for " + path, e);
             return false;
         }
@@ -996,7 +1004,7 @@
      * may return a different result.
      */
     private int analyseProfiles(AndroidPackage pkg, int uid, String profileName,
-            String compilerFilter) {
+            String compilerFilter) throws LegacyDexoptDisabledException {
         // Check if we are allowed to merge and if the compiler filter is profile guided.
         if (!isProfileGuidedCompilerFilter(compilerFilter)) {
             return PROFILE_ANALYSIS_DONT_OPTIMIZE_SMALL_DELTA;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index afcd9d1..0556c3b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -48,6 +48,7 @@
 import static com.android.internal.util.XmlUtils.writeByteArrayAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static com.android.internal.util.XmlUtils.writeUriAttribute;
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 import static com.android.server.pm.PackageManagerService.APP_METADATA_FILE_NAME;
 
@@ -164,6 +165,7 @@
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.pkg.AndroidPackage;
 import com.android.server.pm.pkg.PackageStateInternal;
@@ -2382,9 +2384,15 @@
                 }
 
                 if (isLinkPossible(fromFiles, toDir)) {
-                    if (!mResolvedInstructionSets.isEmpty()) {
-                        final File oatDir = new File(toDir, "oat");
-                        createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
+                    if (!useArtService()) { // ART Service creates oat dirs on demand instead.
+                        if (!mResolvedInstructionSets.isEmpty()) {
+                            final File oatDir = new File(toDir, "oat");
+                            try {
+                                createOatDirs(tempPackageName, mResolvedInstructionSets, oatDir);
+                            } catch (LegacyDexoptDisabledException e) {
+                                throw new RuntimeException(e);
+                            }
+                        }
                     }
                     // pre-create lib dirs for linking if necessary
                     if (!mResolvedNativeLibPaths.isEmpty()) {
@@ -3629,7 +3637,7 @@
     }
 
     private void createOatDirs(String packageName, List<String> instructionSets, File fromDir)
-            throws PackageManagerException {
+            throws PackageManagerException, LegacyDexoptDisabledException {
         for (String instructionSet : instructionSets) {
             try {
                 mInstaller.createOatDir(packageName, fromDir.getAbsolutePath(), instructionSet);
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index fb47c8a..04f5e56 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -47,6 +47,7 @@
 import android.util.ArraySet;
 import android.util.SparseArray;
 
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DynamicCodeLogger;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -708,7 +709,12 @@
     @Override
     @Deprecated
     public final long deleteOatArtifactsOfPackage(String packageName) {
-        return mService.deleteOatArtifactsOfPackage(snapshot(), packageName);
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            return mService.deleteOatArtifactsOfPackage(snapshot(), packageName);
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b825ec4..399e32a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -39,6 +39,7 @@
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility;
 import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
+import static com.android.server.pm.DexOptHelper.useArtService;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
@@ -198,6 +199,7 @@
 import com.android.server.compat.CompatChange;
 import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.ArtManagerService;
 import com.android.server.pm.dex.ArtUtils;
@@ -2136,16 +2138,19 @@
                 // the rest of the commands above) because there's precious little we
                 // can do about it. A settings error is reported, though.
                 final List<String> changedAbiCodePath =
-                        ScanPackageUtils.applyAdjustedAbiToSharedUser(
-                                setting, null /*scannedPackage*/,
+                        ScanPackageUtils.applyAdjustedAbiToSharedUser(setting,
+                                null /*scannedPackage*/,
                                 mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
                                         setting.getPackageStates(), null /*scannedPackage*/));
-                if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+                if (!useArtService() && // Skip for ART Service since it has its own dex file GC.
+                        changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
                     for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
                         final String codePathString = changedAbiCodePath.get(i);
                         try {
                             mInstaller.rmdex(codePathString,
                                     getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        } catch (LegacyDexoptDisabledException e) {
+                            throw new RuntimeException(e);
                         } catch (InstallerException ignored) {
                         }
                     }
@@ -2952,7 +2957,12 @@
     }
 
     public void updatePackagesIfNeeded() {
-        mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     private void notifyPackageUseInternal(String packageName, int reason) {
@@ -4266,7 +4276,12 @@
                     }
                 });
 
-        mBackgroundDexOptService.systemReady();
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            mBackgroundDexOptService.systemReady();
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
 
         // Prune unused static shared libraries which have been cached a period of time
         schedulePruneUnusedStaticSharedLibraries(false /* delay */);
@@ -4615,7 +4630,8 @@
 
             final Computer snapshot = snapshotComputer();
             final AndroidPackage pkg = snapshot.getPackage(packageName);
-            try (PackageFreezer ignored = freezePackage(packageName, "clearApplicationProfileData")) {
+            try (PackageFreezer ignored =
+                            freezePackage(packageName, "clearApplicationProfileData")) {
                 synchronized (mInstallLock) {
                     mAppDataHelper.clearAppProfilesLIF(pkg);
                 }
@@ -4797,9 +4813,14 @@
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
 
+            // TODO(b/251903639): Call into ART Service.
             synchronized (mInstallLock) {
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles");
-                mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
+                try {
+                    mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods);
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
                 Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
         }
@@ -5497,20 +5518,40 @@
          */
         @Override
         public void reconcileSecondaryDexFiles(String packageName) {
+            if (useArtService()) {
+                // ART Service currently relies on a GC to find stale oat files, including secondary
+                // dex files. Hence it doesn't use this call for anything.
+                return;
+            }
+
             final Computer snapshot = snapshotComputer();
             if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
                 return;
             } else if (snapshot.isInstantAppInternal(
-                    packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
+                               packageName, UserHandle.getCallingUserId(), Process.SYSTEM_UID)) {
                 return;
             }
-            mDexManager.reconcileSecondaryDexFiles(packageName);
+            try {
+                mDexManager.reconcileSecondaryDexFiles(packageName);
+            } catch (LegacyDexoptDisabledException e) {
+                throw new RuntimeException(e);
+            }
         }
 
         @Override
         public void registerDexModule(String packageName, String dexModulePath,
                 boolean isSharedModule,
                 IDexModuleRegisterCallback callback) {
+            if (useArtService()) {
+                // ART Service currently doesn't support this explicit dexopting and instead relies
+                // on background dexopt for secondary dex files. This API is problematic since it
+                // doesn't provide the correct classloader context.
+                Slog.i(TAG,
+                        "Ignored unsupported registerDexModule call for " + dexModulePath + " in "
+                                + packageName);
+                return;
+            }
+
             int userId = UserHandle.getCallingUserId();
             ApplicationInfo ai = snapshot().getApplicationInfo(packageName, /*flags*/ 0, userId);
             DexManager.RegisterDexModuleResult result;
@@ -5520,7 +5561,12 @@
                                 " calling user. package=" + packageName + ", user=" + userId);
                 result = new DexManager.RegisterDexModuleResult(false, "Package not installed");
             } else {
-                result = mDexManager.registerDexModule(ai, dexModulePath, isSharedModule, userId);
+                try {
+                    result = mDexManager.registerDexModule(
+                            ai, dexModulePath, isSharedModule, userId);
+                } catch (LegacyDexoptDisabledException e) {
+                    throw new RuntimeException(e);
+                }
             }
 
             if (callback != null) {
@@ -6994,7 +7040,8 @@
         return AndroidPackageUtils.canHaveOatDir(packageState, packageState.getPkg());
     }
 
-    long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName) {
+    long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName)
+            throws LegacyDexoptDisabledException {
         PackageManagerServiceUtils.enforceSystemOrRootOrShell(
                 "Only the system or shell can delete oat artifacts");
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2138c20..6af1d0f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -113,6 +113,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.art.ArtManagerLocal;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 import com.android.server.pm.permission.PermissionAllowlist;
@@ -1953,52 +1954,62 @@
     }
 
     private int runBgDexOpt() throws RemoteException {
-        String opt = getNextOption();
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            String opt = getNextOption();
 
-        if (opt == null) {
-            List<String> packageNames = new ArrayList<>();
-            String arg;
-            while ((arg = getNextArg()) != null) {
-                packageNames.add(arg);
-            }
-            if (!BackgroundDexOptService.getService().runBackgroundDexoptJob(
-                    packageNames.isEmpty() ? null : packageNames)) {
-                getOutPrintWriter().println("Failure");
-                return -1;
-            }
-        } else {
-            String extraArg = getNextArg();
-            if (extraArg != null) {
-                getErrPrintWriter().println("Invalid argument: " + extraArg);
-                return -1;
-            }
-
-            switch (opt) {
-                case "--cancel":
-                    return cancelBgDexOptJob();
-
-                case "--disable":
-                    BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true);
-                    break;
-
-                case "--enable":
-                    BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false);
-                    break;
-
-                default:
-                    getErrPrintWriter().println("Unknown option: " + opt);
+            if (opt == null) {
+                List<String> packageNames = new ArrayList<>();
+                String arg;
+                while ((arg = getNextArg()) != null) {
+                    packageNames.add(arg);
+                }
+                if (!BackgroundDexOptService.getService().runBackgroundDexoptJob(
+                            packageNames.isEmpty() ? null : packageNames)) {
+                    getOutPrintWriter().println("Failure");
                     return -1;
-            }
-        }
+                }
+            } else {
+                String extraArg = getNextArg();
+                if (extraArg != null) {
+                    getErrPrintWriter().println("Invalid argument: " + extraArg);
+                    return -1;
+                }
 
-        getOutPrintWriter().println("Success");
-        return 0;
+                switch (opt) {
+                    case "--cancel":
+                        return cancelBgDexOptJob();
+
+                    case "--disable":
+                        BackgroundDexOptService.getService().setDisableJobSchedulerJobs(true);
+                        break;
+
+                    case "--enable":
+                        BackgroundDexOptService.getService().setDisableJobSchedulerJobs(false);
+                        break;
+
+                    default:
+                        getErrPrintWriter().println("Unknown option: " + opt);
+                        return -1;
+                }
+            }
+
+            getOutPrintWriter().println("Success");
+            return 0;
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     private int cancelBgDexOptJob() throws RemoteException {
-        BackgroundDexOptService.getService().cancelBackgroundDexoptJob();
-        getOutPrintWriter().println("Success");
-        return 0;
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            BackgroundDexOptService.getService().cancelBackgroundDexoptJob();
+            getOutPrintWriter().println("Success");
+            return 0;
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
+        }
     }
 
     private int runDeleteDexOpt() throws RemoteException {
@@ -2008,8 +2019,8 @@
             pw.println("Error: no package name");
             return 1;
         }
-        long freedBytes = LocalServices.getService(
-                PackageManagerInternal.class).deleteOatArtifactsOfPackage(packageName);
+        long freedBytes = LocalServices.getService(PackageManagerInternal.class)
+                                  .deleteOatArtifactsOfPackage(packageName);
         if (freedBytes < 0) {
             pw.println("Error: delete failed");
             return 1;
@@ -2273,7 +2284,7 @@
             if (abandonSession) {
                 try {
                     doAbandonSession(sessionId, false /*logSuccess*/);
-                } catch (Exception ignore) {
+                } catch (RuntimeException ignore) {
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index eb99536..e5aaddb 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -45,6 +45,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.parsing.PackageCacher;
 import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
 import com.android.server.pm.parsing.pkg.PackageImpl;
@@ -424,8 +425,11 @@
             String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
             for (String codePath : allCodePaths) {
                 for (String dexCodeInstructionSet : dexCodeInstructionSets) {
+                    // TODO(b/251903639): Call into ART Service.
                     try {
                         mPm.mInstaller.rmdex(codePath, dexCodeInstructionSet);
+                    } catch (LegacyDexoptDisabledException e) {
+                        throw new RuntimeException(e);
                     } catch (Installer.InstallerException ignored) {
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index 750e893..2af01d6 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -47,6 +47,9 @@
      */
     @Nullable
     public final PackageSetting mPkgSetting;
+
+    // TODO(b/260124949): Check if this can be dropped when the legacy PackageManager dexopt code is
+    // cleaned up.
     /** ABI code paths that have changed in the package scan */
     @Nullable public final List<String> mChangedAbiCodePath;
 
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index e19ebce..b5b6347 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -52,6 +52,7 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.PackageManagerServiceCompilerMapping;
 import com.android.server.pm.parsing.PackageInfoUtils;
@@ -177,7 +178,6 @@
                 == ApplicationInfo.FLAG_DEBUGGABLE;
     }
 
-
     @Override
     public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
             @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
@@ -210,15 +210,20 @@
             Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
         }
 
-        if (bootImageProfile) {
-            snapshotBootImageProfile(callback);
-        } else {
-            snapshotAppProfile(packageName, codePath, callback);
+        // TODO(b/251903639): Call into ART Service.
+        try {
+            if (bootImageProfile) {
+                snapshotBootImageProfile(callback);
+            } else {
+                snapshotAppProfile(packageName, codePath, callback);
+            }
+        } catch (LegacyDexoptDisabledException e) {
+            throw new RuntimeException(e);
         }
     }
 
     private void snapshotAppProfile(String packageName, String codePath,
-            ISnapshotRuntimeProfileCallback callback) {
+            ISnapshotRuntimeProfileCallback callback) throws LegacyDexoptDisabledException {
         PackageInfo info = null;
         try {
             // Note that we use the default user 0 to retrieve the package info.
@@ -269,7 +274,8 @@
     }
 
     private void createProfileSnapshot(String packageName, String profileName, String classpath,
-            int appId, ISnapshotRuntimeProfileCallback callback) {
+            int appId, ISnapshotRuntimeProfileCallback callback)
+            throws LegacyDexoptDisabledException {
         // Ask the installer to snapshot the profile.
         try {
             if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) {
@@ -299,7 +305,8 @@
         }
     }
 
-    private void destroyProfileSnapshot(String packageName, String profileName) {
+    private void destroyProfileSnapshot(String packageName, String profileName)
+            throws LegacyDexoptDisabledException {
         if (DEBUG) {
             Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
         }
@@ -333,7 +340,8 @@
         }
     }
 
-    private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) {
+    private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback)
+            throws LegacyDexoptDisabledException {
         // Combine the profiles for boot classpath and system server classpath.
         // This avoids having yet another type of profiles and simplifies the processing.
         String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"),
@@ -364,7 +372,7 @@
         mHandler.post(() -> {
             try {
                 callback.onError(errCode);
-            } catch (Exception e) {
+            } catch (RemoteException | RuntimeException e) {
                 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
             }
         });
@@ -387,7 +395,7 @@
                             + packageName);
                     callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
                 }
-            } catch (Exception e) {
+            } catch (RemoteException | RuntimeException e) {
                 Slog.w(TAG,
                         "Failed to call onSuccess after profile snapshot for " + packageName, e);
             } finally {
@@ -402,9 +410,8 @@
      *   - create the current primary profile to save time at app startup time.
      *   - copy the profiles from the associated dex metadata file to the reference profile.
      */
-    public void prepareAppProfiles(
-            AndroidPackage pkg, @UserIdInt int user,
-            boolean updateReferenceProfileContent) {
+    public void prepareAppProfiles(AndroidPackage pkg, @UserIdInt int user,
+            boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException {
         final int appId = UserHandle.getAppId(pkg.getUid());
         if (user < 0) {
             Slog.wtf(TAG, "Invalid user id: " + user);
@@ -444,9 +451,8 @@
     /**
      * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
      */
-    public void prepareAppProfiles(
-            AndroidPackage pkg, int[] user,
-            boolean updateReferenceProfileContent) {
+    public void prepareAppProfiles(AndroidPackage pkg, int[] user,
+            boolean updateReferenceProfileContent) throws LegacyDexoptDisabledException {
         for (int i = 0; i < user.length; i++) {
             prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
         }
@@ -455,7 +461,7 @@
     /**
      * Clear the profiles for the given package.
      */
-    public void clearAppProfiles(AndroidPackage pkg) {
+    public void clearAppProfiles(AndroidPackage pkg) throws LegacyDexoptDisabledException {
         try {
             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
             for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
@@ -470,7 +476,8 @@
     /**
      * Dumps the profiles for the given package.
      */
-    public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods) {
+    public void dumpProfiles(AndroidPackage pkg, boolean dumpClassesAndMethods)
+            throws LegacyDexoptDisabledException {
         final int sharedGid = UserHandle.getSharedAppGid(pkg.getUid());
         try {
             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 8c2da45..fbf7409 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -46,6 +46,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.PackageDexOptimizer;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.PackageManagerServiceUtils;
@@ -190,7 +191,7 @@
         try {
             notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
                     loaderUserId, loaderIsIsolatedProcess);
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             Slog.w(TAG, "Exception while notifying dex load for package " +
                     loadingAppInfo.packageName, e);
         }
@@ -325,7 +326,7 @@
     public void load(Map<Integer, List<PackageInfo>> existingPackages) {
         try {
             loadInternal(existingPackages);
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             mPackageDexUsage.clear();
             mDynamicCodeLogger.clear();
             Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
@@ -457,7 +458,7 @@
             List<String> packagesToKeepDataAbout = new ArrayList<>();
             mPackageDexUsage.syncData(
                     packageToUsersMap, packageToCodePaths, packagesToKeepDataAbout);
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             mPackageDexUsage.clear();
             Slog.w(TAG, "Exception while loading package dex usage. "
                     + "Starting with a fresh state.", e);
@@ -465,7 +466,7 @@
 
         try {
             mDynamicCodeLogger.readAndSync(packageToUsersMap);
-        } catch (Exception e) {
+        } catch (RuntimeException e) {
             mDynamicCodeLogger.clear();
             Slog.w(TAG, "Exception while loading package dynamic code usage. "
                     + "Starting with a fresh state.", e);
@@ -514,7 +515,7 @@
      * @return true if all secondary dex files were processed successfully (compiled or skipped
      *         because they don't need to be compiled)..
      */
-    public boolean dexoptSecondaryDex(DexoptOptions options) {
+    public boolean dexoptSecondaryDex(DexoptOptions options) throws LegacyDexoptDisabledException {
         if (isPlatformPackage(options.getPackageName())) {
             // We could easily redirect to #dexoptSystemServer in this case. But there should be
             // no-one calling this method directly for system server.
@@ -574,7 +575,8 @@
      * <p>PackageDexOptimizer.DEX_OPT_FAILED if any dexopt operation failed.
      * <p>PackageDexOptimizer.DEX_OPT_PERFORMED if all dexopt operations succeeded.
      */
-    public int dexoptSystemServer(DexoptOptions options) {
+    public int dexoptSystemServer(DexoptOptions options) throws LegacyDexoptDisabledException {
+        // TODO(b/254043366): Clean this up.
         if (!isPlatformPackage(options.getPackageName())) {
             Slog.wtf(TAG, "Non system server package used when trying to dexopt system server:"
                     + options.getPackageName());
@@ -664,7 +666,8 @@
      * {@code packagName} and the actual dex files. For all dex files that were
      * deleted, update the internal records and delete any generated oat files.
      */
-    public void reconcileSecondaryDexFiles(String packageName) {
+    public void reconcileSecondaryDexFiles(String packageName)
+            throws LegacyDexoptDisabledException {
         PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
         if (useInfo.getDexUseInfoMap().isEmpty()) {
             if (DEBUG) {
@@ -756,7 +759,7 @@
     // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
     // compilation happening here will use a pessimistic context.
     public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
-            boolean isSharedModule, int userId) {
+            boolean isSharedModule, int userId) throws LegacyDexoptDisabledException {
         // Find the owning package record.
         DexSearchResult searchResult = getDexPackage(info, dexPath, userId);
 
@@ -1012,7 +1015,8 @@
      * @param packageInfo the package information.
      * @return the number of freed bytes or -1 if there was an error in the process.
      */
-    public long deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+    public long deleteOptimizedFiles(ArtPackageInfo packageInfo)
+            throws LegacyDexoptDisabledException {
         long freedBytes = 0;
         boolean hadErrors = false;
         final String packageName = packageInfo.getPackageName();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a53d0f9..65acdc1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4300,9 +4300,7 @@
             case KeyEvent.KEYCODE_DEMO_APP_3:
             case KeyEvent.KEYCODE_DEMO_APP_4: {
                 // TODO(b/254604589): Dispatch KeyEvent to System UI.
-                if (!mStylusButtonsDisabled) {
-                    sendSystemKeyToStatusBarAsync(keyCode);
-                }
+                sendSystemKeyToStatusBarAsync(keyCode);
 
                 // Just drop if keys are not intercepted for direct key.
                 result &= ~ACTION_PASS_TO_USER;
@@ -4312,7 +4310,9 @@
             case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
-                // TODO(go/android-stylus-buttons): Handle stylus button presses.
+                if (!mStylusButtonsDisabled) {
+                    sendSystemKeyToStatusBarAsync(keyCode);
+                }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
             }
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 9d098c6..1be9074 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -29,7 +29,6 @@
 import android.app.timedetector.ITimeDetectorService;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
 import android.content.Context;
 import android.os.Binder;
 import android.os.Handler;
@@ -361,6 +360,34 @@
         mHandler.post(() -> mTimeDetectorStrategy.suggestNetworkTime(timeSignal));
     }
 
+    @Override
+    public UnixEpochTime latestNetworkTime() {
+        NetworkTimeSuggestion suggestion = getLatestNetworkSuggestion();
+        if (suggestion != null) {
+            return suggestion.getUnixEpochTime();
+        } else {
+            throw new ParcelableException(new DateTimeException("Missing network time fix"));
+        }
+    }
+
+    /**
+     * Returns the latest network suggestion accepted. For use by {@link TimeDetectorShellCommand}.
+     */
+    @Nullable
+    NetworkTimeSuggestion getLatestNetworkSuggestion() {
+        // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
+        //  be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
+        //  detector. mNtpTrustedTime can be removed once this happens.
+        NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
+        if (ntpResult != null) {
+            UnixEpochTime unixEpochTime = new UnixEpochTime(
+                    ntpResult.getElapsedRealtimeMillis(), ntpResult.getTimeMillis());
+            return new NetworkTimeSuggestion(unixEpochTime, ntpResult.getUncertaintyMillis());
+        } else {
+            return null;
+        }
+    }
+
     void suggestGnssTime(@NonNull GnssTimeSuggestion timeSignal) {
         enforceSuggestGnssTimePermission();
         Objects.requireNonNull(timeSignal);
@@ -377,19 +404,6 @@
     }
 
     @Override
-    public TimePoint latestNetworkTime() {
-        // TODO(b/222295093): Return the latest network time from mTimeDetectorStrategy once we can
-        //  be sure that all uses of NtpTrustedTime results in a suggestion being made to the time
-        //  detector. mNtpTrustedTime can be removed once this happens.
-        NtpTrustedTime.TimeResult ntpResult = mNtpTrustedTime.getCachedTimeResult();
-        if (ntpResult != null) {
-            return new TimePoint(ntpResult.getTimeMillis(), ntpResult.getElapsedRealtimeMillis());
-        } else {
-            throw new ParcelableException(new DateTimeException("Missing network time fix"));
-        }
-    }
-
-    @Override
     protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
             @Nullable String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -421,7 +435,7 @@
     private void enforceSuggestNetworkTimePermission() {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.SET_TIME,
-                "set time");
+                "suggest network time");
     }
 
     private void enforceSuggestGnssTimePermission() {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 70e7dae..fd6d606 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6344,7 +6344,7 @@
                 int wakefulness) {
             synchronized (mGlobalLock) {
                 if (mHomeProcess != null && (dumpPackage == null
-                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                        || mHomeProcess.containsPackage(dumpPackage))) {
                     if (needSep) {
                         pw.println();
                         needSep = false;
@@ -6352,7 +6352,7 @@
                     pw.println("  mHomeProcess: " + mHomeProcess);
                 }
                 if (mPreviousProcess != null && (dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                        || mPreviousProcess.containsPackage(dumpPackage))) {
                     if (needSep) {
                         pw.println();
                         needSep = false;
@@ -6360,14 +6360,14 @@
                     pw.println("  mPreviousProcess: " + mPreviousProcess);
                 }
                 if (dumpAll && (mPreviousProcess == null || dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                        || mPreviousProcess.containsPackage(dumpPackage))) {
                     StringBuilder sb = new StringBuilder(128);
                     sb.append("  mPreviousProcessVisibleTime: ");
                     TimeUtils.formatDuration(mPreviousProcessVisibleTime, sb);
                     pw.println(sb);
                 }
                 if (mHeavyWeightProcess != null && (dumpPackage == null
-                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                        || mHeavyWeightProcess.containsPackage(dumpPackage))) {
                     if (needSep) {
                         pw.println();
                         needSep = false;
@@ -6491,18 +6491,18 @@
                 }
 
                 if (mHomeProcess != null && (dumpPackage == null
-                        || mHomeProcess.mPkgList.contains(dumpPackage))) {
+                        || mHomeProcess.containsPackage(dumpPackage))) {
                     mHomeProcess.dumpDebug(proto, HOME_PROC);
                 }
 
                 if (mPreviousProcess != null && (dumpPackage == null
-                        || mPreviousProcess.mPkgList.contains(dumpPackage))) {
+                        || mPreviousProcess.containsPackage(dumpPackage))) {
                     mPreviousProcess.dumpDebug(proto, PREVIOUS_PROC);
                     proto.write(PREVIOUS_PROC_VISIBLE_TIME_MS, mPreviousProcessVisibleTime);
                 }
 
                 if (mHeavyWeightProcess != null && (dumpPackage == null
-                        || mHeavyWeightProcess.mPkgList.contains(dumpPackage))) {
+                        || mHeavyWeightProcess.containsPackage(dumpPackage))) {
                     mHeavyWeightProcess.dumpDebug(proto, HEAVY_WEIGHT_PROC);
                 }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 103b9b2..b1bc52a 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1714,7 +1714,7 @@
                     // Don't kill the home process along with tasks from the same package.
                     continue;
                 }
-                if (!proc.mPkgList.contains(pkg)) {
+                if (!proc.containsPackage(pkg)) {
                     // Don't kill process that is not associated with this task.
                     continue;
                 }
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index a035948..d5828ef 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -566,7 +566,7 @@
             SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap();
             for (int i = pidMap.size() - 1; i >= 0; i--) {
                 final WindowProcessController app = pidMap.valueAt(i);
-                if (!app.mPkgList.contains(packageName)) {
+                if (!app.containsPackage(packageName)) {
                     continue;
                 }
                 try {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b5c82a8..15a5ebf 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -910,6 +910,22 @@
     }
 
     @Override
+    public boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
+        if (embeddedWindow == null) {
+            return false;
+        }
+
+        final long identity = Binder.clearCallingIdentity();
+        boolean didTransfer = false;
+        try {
+            didTransfer = mService.transferEmbeddedTouchFocusToHost(embeddedWindow);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return didTransfer;
+    }
+
+    @Override
     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
             RemoteCallback callback) {
         final long origId = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d058892c..9b09d94 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8682,6 +8682,38 @@
         clientChannel.copyTo(outInputChannel);
     }
 
+    boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) {
+        final IBinder windowBinder = embeddedWindow.asBinder();
+        final IBinder hostInputChannel, embeddedInputChannel;
+        synchronized (mGlobalLock) {
+            final EmbeddedWindowController.EmbeddedWindow ew =
+                mEmbeddedWindowController.getByWindowToken(windowBinder);
+            if (ew == null) {
+                Slog.w(TAG, "Attempt to transfer touch focus from non-existent embedded window");
+                return false;
+            }
+            final WindowState hostWindowState = ew.getWindowState();
+            if (hostWindowState == null) {
+                Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no" +
+                    " associated host");
+                return false;
+            }
+            embeddedInputChannel = ew.getInputChannelToken();
+            if (embeddedInputChannel == null) {
+                Slog.w(TAG, "Attempt to transfer touch focus from embedded window with no input" +
+                    " channel");
+                return false;
+            }
+            hostInputChannel = hostWindowState.mInputChannelToken;
+            if (hostInputChannel == null) {
+                Slog.w(TAG, "Attempt to transfer touch focus to a host window with no" +
+                    " input channel");
+                return false;
+            }
+            return mInputManager.transferTouchFocus(embeddedInputChannel, hostInputChannel);
+        }
+    }
+
     private void updateInputChannel(IBinder channelToken, int callingUid, int callingPid,
             int displayId, SurfaceControl surface, String name,
             InputApplicationHandle applicationHandle, int flags,
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9b361fe..3e279b2 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -76,6 +76,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.IRemoteAnimationRunner;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.protolog.common.ProtoLog;
@@ -118,7 +119,8 @@
     // communicate back to the activity manager side.
     public final Object mOwner;
     // List of packages running in the process
-    final ArraySet<String> mPkgList = new ArraySet<>();
+    @GuardedBy("itself")
+    private final ArrayList<String> mPkgList = new ArrayList<>(1);
     private final WindowProcessListener mListener;
     private final ActivityTaskManagerService mAtm;
     private final BackgroundLaunchProcessController mBgLaunchController;
@@ -645,18 +647,26 @@
 
     @HotPath(caller = HotPath.PROCESS_CHANGE)
     public void addPackage(String packageName) {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
-            mPkgList.add(packageName);
+        synchronized (mPkgList) {
+            if (!mPkgList.contains(packageName)) {
+                mPkgList.add(packageName);
+            }
         }
     }
 
     @HotPath(caller = HotPath.PROCESS_CHANGE)
     public void clearPackageList() {
-        synchronized (mAtm.mGlobalLockWithoutBoost) {
+        synchronized (mPkgList) {
             mPkgList.clear();
         }
     }
 
+    boolean containsPackage(String packageName) {
+        synchronized (mPkgList) {
+            return mPkgList.contains(packageName);
+        }
+    }
+
     void addActivityIfNeeded(ActivityRecord r) {
         // even if we already track this activity, note down that it has been launched
         setLastActivityLaunchTime(r.lastLaunchTime);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5ecf737..a1f4096 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -329,8 +329,8 @@
 
             mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
                     flags, this, attrs.type);
-            mSurfaceController.setColorSpaceAgnostic((attrs.privateFlags
-                    & WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
+            mSurfaceController.setColorSpaceAgnostic(w.getPendingTransaction(),
+                    (attrs.privateFlags & LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC) != 0);
 
             w.setHasSurface(true);
             // The surface instance is changed. Make sure the input info can be applied to the
@@ -527,7 +527,7 @@
         if (mSurfaceController == null) {
             return;
         }
-        mSurfaceController.setColorSpaceAgnostic(agnostic);
+        mSurfaceController.setColorSpaceAgnostic(mWin.getPendingTransaction(), agnostic);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 4a5c473..607ce25 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -236,24 +236,13 @@
         }
     }
 
-    void setColorSpaceAgnostic(boolean agnostic) {
+    void setColorSpaceAgnostic(SurfaceControl.Transaction t, boolean agnostic) {
         ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isColorSpaceAgnostic=%b: %s", agnostic, title);
 
         if (mSurfaceControl == null) {
             return;
         }
-        if (SHOW_LIGHT_TRANSACTIONS) {
-            Slog.i(TAG, ">>> OPEN TRANSACTION setColorSpaceAgnosticLocked");
-        }
-        mService.openSurfaceTransaction();
-        try {
-            getGlobalTransaction().setColorSpaceAgnostic(mSurfaceControl, agnostic);
-        } finally {
-            mService.closeSurfaceTransaction("setColorSpaceAgnostic");
-            if (SHOW_LIGHT_TRANSACTIONS) {
-                Slog.i(TAG, "<<< CLOSE TRANSACTION setColorSpaceAgnosticLocked");
-            }
-        }
+        t.setColorSpaceAgnostic(mSurfaceControl, agnostic);
     }
 
     boolean showRobustly(SurfaceControl.Transaction t) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 70a7a02..4c42791 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -34,6 +34,7 @@
 import android.app.admin.DeviceAdminInfo;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.FactoryResetProtectionPolicy;
+import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.PackagePolicy;
 import android.app.admin.PasswordPolicy;
 import android.app.admin.PreferentialNetworkServiceConfig;
@@ -168,6 +169,7 @@
     private static final String TAG_PROTECTED_PACKAGES = "protected_packages";
     private static final String TAG_SUSPENDED_PACKAGES = "suspended-packages";
     private static final String TAG_MTE_POLICY = "mte-policy";
+    private static final String TAG_MANAGED_SUBSCRIPTIONS_POLICY = "managed_subscriptions_policy";
     private static final String ATTR_VALUE = "value";
     private static final String ATTR_LAST_NETWORK_LOGGING_NOTIFICATION = "last-notification";
     private static final String ATTR_NUM_NETWORK_LOGGING_NOTIFICATIONS = "num-notifications";
@@ -272,6 +274,9 @@
     // Wi-Fi SSID restriction policy.
     WifiSsidPolicy mWifiSsidPolicy;
 
+    // Managed subscriptions policy.
+    ManagedSubscriptionsPolicy mManagedSubscriptionsPolicy;
+
     // TODO: review implementation decisions with frameworks team
     boolean specifiesGlobalProxy = false;
     String globalProxySpec = null;
@@ -642,6 +647,11 @@
                 mManagedProfileCallerIdAccess);
         writePackagePolicy(out, TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY,
                 mManagedProfileContactsAccess);
+        if (mManagedSubscriptionsPolicy != null) {
+            out.startTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY);
+            mManagedSubscriptionsPolicy.saveToXml(out);
+            out.endTag(null, TAG_MANAGED_SUBSCRIPTIONS_POLICY);
+        }
     }
 
     private void writePackagePolicy(TypedXmlSerializer out, String tag,
@@ -946,6 +956,8 @@
                 mManagedProfileCallerIdAccess = readPackagePolicy(parser);
             } else if (TAG_CROSS_PROFILE_CONTACTS_SEARCH_POLICY.equals(tag)) {
                 mManagedProfileContactsAccess = readPackagePolicy(parser);
+            } else if (TAG_MANAGED_SUBSCRIPTIONS_POLICY.equals(tag)) {
+                mManagedSubscriptionsPolicy = ManagedSubscriptionsPolicy.readFromXml(parser);
             } else {
                 Slogf.w(LOG_TAG, "Unknown admin tag: %s", tag);
                 XmlUtils.skipCurrentTag(parser);
@@ -1414,5 +1426,13 @@
 
         pw.print("accountTypesWithManagementDisabled=");
         pw.println(accountTypesWithManagementDisabled);
+
+        if (mManagedSubscriptionsPolicy != null) {
+            pw.println("mManagedSubscriptionsPolicy:");
+            pw.increaseIndent();
+            pw.print("mPolicyType=");
+            mManagedSubscriptionsPolicy.getPolicyType();
+            pw.decreaseIndent();
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2b92b22..cdb2e08 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -218,6 +218,7 @@
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.app.admin.FullyManagedDeviceProvisioningParams;
 import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.NetworkEvent;
 import android.app.admin.PackagePolicy;
 import android.app.admin.ParcelableGranteeMap;
@@ -334,6 +335,8 @@
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.stats.devicepolicy.DevicePolicyEnums;
+import android.telecom.TelecomManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.text.TextUtils;
@@ -768,6 +771,9 @@
     private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
     private final Object mESIDInitilizationLock = new Object();
     private EnterpriseSpecificIdCalculator mEsidCalculator;
+    private final Object mSubscriptionsChangedListenerLock = new Object();
+    @GuardedBy("mSubscriptionsChangedListenerLock")
+    private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionsChangedListener;
 
     /**
      * Contains the list of OEM Default Role Holders for Contact-related roles
@@ -973,8 +979,6 @@
     @GuardedBy("getLockObject()")
     final SparseArray<DevicePolicyData> mUserData;
 
-    @GuardedBy("getLockObject()")
-
     final Handler mHandler;
     final Handler mBackgroundHandler;
 
@@ -3092,6 +3096,7 @@
                 onLockSettingsReady();
                 loadAdminDataAsync();
                 mOwners.systemReady();
+                applyManagedSubscriptionsPolicyIfRequired();
                 break;
             case SystemService.PHASE_ACTIVITY_MANAGER_READY:
                 synchronized (getLockObject()) {
@@ -3110,6 +3115,22 @@
         }
     }
 
+    private void applyManagedSubscriptionsPolicyIfRequired() {
+        int copeProfileUserId = getOrganizationOwnedProfileUserId();
+        // This policy is relevant only for COPE devices.
+        if (copeProfileUserId != UserHandle.USER_NULL) {
+            unregisterOnSubscriptionsChangedListener();
+            int policyType = getManagedSubscriptionsPolicy().getPolicyType();
+            if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS) {
+                // By default, assign all current and future subs to system user on COPE devices.
+                registerListenerToAssignSubscriptionsToUser(UserHandle.USER_SYSTEM);
+            } else if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+                // Add listener to assign all current and future subs to managed profile.
+                registerListenerToAssignSubscriptionsToUser(copeProfileUserId);
+            }
+        }
+    }
+
     private void updatePersonalAppsSuspensionOnUserStart(int userHandle) {
         final int profileUserHandle = getManagedUserId(userHandle);
         if (profileUserHandle >= 0) {
@@ -6993,9 +7014,23 @@
         }
         mLockSettingsInternal.refreshStrongAuthTimeout(parentId);
 
+        clearManagedSubscriptionsPolicy();
+
         Slogf.i(LOG_TAG, "Cleaning up device-wide policies done.");
     }
 
+    private void clearManagedSubscriptionsPolicy() {
+        unregisterOnSubscriptionsChangedListener();
+
+        SubscriptionManager subscriptionManager = mContext.getSystemService(
+                SubscriptionManager.class);
+        //Iterate over all the subscriptions and remove association with any user.
+        int[] subscriptionIds = subscriptionManager.getActiveSubscriptionIdList(false);
+        for (int subId : subscriptionIds) {
+            subscriptionManager.setSubscriptionUserHandle(subId, null);
+        }
+    }
+
     /**
      * @param factoryReset null: legacy behaviour, false: attempt to remove user, true: attempt to
      *                     factory reset
@@ -10088,6 +10123,10 @@
                 mStateCache.dump(pw);
                 pw.println();
             }
+
+            synchronized (mSubscriptionsChangedListenerLock) {
+                pw.println("Subscription changed listener : " + mSubscriptionsChangedListener);
+            }
             mHandler.post(() -> handleDump(pw));
             dumpResources(pw);
         }
@@ -19952,4 +19991,139 @@
                 HEADLESS_FLAG,
                 DEFAULT_HEADLESS_FLAG);
     }
+
+    @Override
+    public ManagedSubscriptionsPolicy getManagedSubscriptionsPolicy() {
+        synchronized (getLockObject()) {
+            ActiveAdmin admin = getProfileOwnerOfOrganizationOwnedDeviceLocked();
+            if (admin != null && admin.mManagedSubscriptionsPolicy != null) {
+                return admin.mManagedSubscriptionsPolicy;
+            }
+        }
+        return new ManagedSubscriptionsPolicy(
+                ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS);
+    }
+
+    @Override
+    public void setManagedSubscriptionsPolicy(ManagedSubscriptionsPolicy policy) {
+        CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller),
+                "This policy can only be set by a profile owner on an organization-owned device.");
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin admin = getProfileOwnerLocked(caller.getUserId());
+            if (hasUserSetupCompleted(UserHandle.USER_SYSTEM) && !isAdminTestOnlyLocked(
+                    admin.info.getComponent(), caller.getUserId())) {
+                throw new IllegalStateException("Not allowed to apply this policy after setup");
+            }
+            boolean changed = false;
+            if (!Objects.equals(policy, admin.mManagedSubscriptionsPolicy)) {
+                admin.mManagedSubscriptionsPolicy = policy;
+                changed = true;
+            }
+            if (changed) {
+                saveSettingsLocked(caller.getUserId());
+            } else {
+                return;
+            }
+        }
+
+        applyManagedSubscriptionsPolicyIfRequired();
+
+        int policyType = getManagedSubscriptionsPolicy().getPolicyType();
+        if (policyType == ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS) {
+            final long id = mInjector.binderClearCallingIdentity();
+            try {
+                int parentUserId = getProfileParentId(caller.getUserId());
+                installOemDefaultDialerAndMessagesApp(parentUserId, caller.getUserId());
+            } finally {
+                mInjector.binderRestoreCallingIdentity(id);
+            }
+        }
+    }
+
+    private void installOemDefaultDialerAndMessagesApp(int sourceUserId, int targetUserId) {
+        try {
+            UserHandle sourceUserHandle = UserHandle.of(sourceUserId);
+            TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class);
+            String dialerAppPackage = telecomManager.getDefaultDialerPackage(
+                    sourceUserHandle);
+            String messagesAppPackage = SmsApplication.getDefaultSmsApplicationAsUser(mContext,
+                    true, sourceUserHandle).getPackageName();
+            if (dialerAppPackage != null) {
+                mIPackageManager.installExistingPackageAsUser(dialerAppPackage, targetUserId,
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_POLICY, null);
+            } else {
+                Slogf.w(LOG_TAG, "Couldn't install dialer app, dialer app package is null");
+            }
+
+            if (messagesAppPackage != null) {
+                mIPackageManager.installExistingPackageAsUser(messagesAppPackage, targetUserId,
+                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+                        PackageManager.INSTALL_REASON_POLICY, null);
+            } else {
+                Slogf.w(LOG_TAG, "Couldn't install messages app, messages app package is null");
+            }
+        } catch (RemoteException re) {
+            // shouldn't happen
+            Slogf.wtf(LOG_TAG, "Failed to install dialer/messages app", re);
+        }
+    }
+
+    private void registerListenerToAssignSubscriptionsToUser(int userId) {
+        synchronized (mSubscriptionsChangedListenerLock) {
+            if (mSubscriptionsChangedListener != null) {
+                return;
+            }
+            SubscriptionManager subscriptionManager = mContext.getSystemService(
+                    SubscriptionManager.class);
+            // Listener to assign all current and future subs to managed profile.
+            mSubscriptionsChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener(
+                    mHandler.getLooper()) {
+                @Override
+                public void onSubscriptionsChanged() {
+                    final long id = mInjector.binderClearCallingIdentity();
+                    try {
+                        int[] subscriptionIds = subscriptionManager.getActiveSubscriptionIdList(
+                                false);
+                        for (int subId : subscriptionIds) {
+                            UserHandle associatedUserHandle =
+                                    subscriptionManager.getSubscriptionUserHandle(subId);
+                            if (associatedUserHandle == null
+                                    || associatedUserHandle.getIdentifier() != userId) {
+                                subscriptionManager.setSubscriptionUserHandle(subId,
+                                        UserHandle.of(userId));
+                            }
+                        }
+                    } finally {
+                        mInjector.binderRestoreCallingIdentity(id);
+                    }
+                }
+            };
+
+            final long id = mInjector.binderClearCallingIdentity();
+            try {
+                // When listener is added onSubscriptionsChanged gets called immediately for once
+                // (even if subscriptions are not changed) and later on when subscriptions changes.
+                subscriptionManager.addOnSubscriptionsChangedListener(
+                        mSubscriptionsChangedListener.getHandlerExecutor(),
+                        mSubscriptionsChangedListener);
+            } finally {
+                mInjector.binderRestoreCallingIdentity(id);
+            }
+        }
+    }
+
+    private void unregisterOnSubscriptionsChangedListener() {
+        synchronized (mSubscriptionsChangedListenerLock) {
+            if (mSubscriptionsChangedListener != null) {
+                SubscriptionManager subscriptionManager = mContext.getSystemService(
+                        SubscriptionManager.class);
+                subscriptionManager.removeOnSubscriptionsChangedListener(
+                        mSubscriptionsChangedListener);
+                mSubscriptionsChangedListener = null;
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
index 01674bb..8b36da5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -169,19 +169,19 @@
     }
 
     @Test
-    public void testBootCompleted() {
+    public void testBootCompleted() throws Exception {
         initUntilBootCompleted();
     }
 
     @Test
-    public void testNoExecutionForIdleJobBeforePostBootUpdate() {
+    public void testNoExecutionForIdleJobBeforePostBootUpdate() throws Exception {
         initUntilBootCompleted();
 
         assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
     }
 
     @Test
-    public void testNoExecutionForLowStorage() {
+    public void testNoExecutionForLowStorage() throws Exception {
         initUntilBootCompleted();
         when(mPackageManager.isStorageLow()).thenReturn(true);
 
@@ -191,7 +191,7 @@
     }
 
     @Test
-    public void testNoExecutionForNoOptimizablePackages() {
+    public void testNoExecutionForNoOptimizablePackages() throws Exception {
         initUntilBootCompleted();
         when(mDexOptHelper.getOptimizablePackages(any())).thenReturn(Collections.emptyList());
 
@@ -201,7 +201,7 @@
     }
 
     @Test
-    public void testPostBootUpdateFullRun() {
+    public void testPostBootUpdateFullRun() throws Exception {
         initUntilBootCompleted();
 
         runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
@@ -210,7 +210,7 @@
     }
 
     @Test
-    public void testPostBootUpdateFullRunWithPackageFailure() {
+    public void testPostBootUpdateFullRunWithPackageFailure() throws Exception {
         mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED;
 
         initUntilBootCompleted();
@@ -224,7 +224,7 @@
     }
 
     @Test
-    public void testIdleJobFullRun() {
+    public void testIdleJobFullRun() throws Exception {
         initUntilBootCompleted();
         runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
                 /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
@@ -235,7 +235,7 @@
     }
 
     @Test
-    public void testIdleJobFullRunWithFailureOnceAndSuccessAfterUpdate() {
+    public void testIdleJobFullRunWithFailureOnceAndSuccessAfterUpdate() throws Exception {
         mDexOptResultForPackageAAA = PackageDexOptimizer.DEX_OPT_FAILED;
 
         initUntilBootCompleted();
@@ -271,7 +271,7 @@
     }
 
     @Test
-    public void testIdleJobFullRunWithFatalError() {
+    public void testIdleJobFullRunWithFatalError() throws Exception {
         initUntilBootCompleted();
         runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot,
                 /* expectedReschedule= */ false, /* expectedStatus= */ STATUS_OK,
@@ -285,7 +285,7 @@
     }
 
     @Test
-    public void testSystemReadyWhenDisabled() {
+    public void testSystemReadyWhenDisabled() throws Exception {
         when(mInjector.isBackgroundDexOptDisabled()).thenReturn(true);
 
         mService.systemReady();
@@ -294,7 +294,7 @@
     }
 
     @Test
-    public void testStopByCancelFlag() {
+    public void testStopByCancelFlag() throws Exception {
         when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
         initUntilBootCompleted();
 
@@ -447,7 +447,7 @@
     }
 
     @Test
-    public void testStopByThermal() {
+    public void testStopByThermal() throws Exception {
         when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
         initUntilBootCompleted();
 
@@ -478,7 +478,7 @@
     }
 
     @Test
-    public void testDisableJobSchedulerJobs() {
+    public void testDisableJobSchedulerJobs() throws Exception {
         when(mInjector.getCallingUid()).thenReturn(Process.SHELL_UID);
         mService.setDisableJobSchedulerJobs(true);
         assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
@@ -492,7 +492,7 @@
         assertThrows(SecurityException.class, () -> mService.setDisableJobSchedulerJobs(true));
     }
 
-    private void initUntilBootCompleted() {
+    private void initUntilBootCompleted() throws Exception {
         ArgumentCaptor<BroadcastReceiver> argReceiver = ArgumentCaptor.forClass(
                 BroadcastReceiver.class);
         ArgumentCaptor<IntentFilter> argIntentFilter = ArgumentCaptor.forClass(IntentFilter.class);
@@ -515,7 +515,7 @@
         assertThat(jobIds).containsExactlyElementsIn(expectedJobIds);
     }
 
-    private void verifyLastControlDexOptBlockingCall(boolean expected) {
+    private void verifyLastControlDexOptBlockingCall(boolean expected) throws Exception {
         ArgumentCaptor<Boolean> argDexOptBlock = ArgumentCaptor.forClass(Boolean.class);
         verify(mDexOptHelper, atLeastOnce()).controlDexOptBlocking(argDexOptBlock.capture());
         assertThat(argDexOptBlock.getValue()).isEqualTo(expected);
@@ -523,7 +523,7 @@
 
     private void runFullJob(BackgroundDexOptJobService jobService, JobParameters params,
             boolean expectedReschedule, int expectedStatus, int totalJobFinishedWithParams,
-            @Nullable String expectedSkippedPackage) {
+            @Nullable String expectedSkippedPackage) throws Exception {
         when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
         addFullRunSequence(expectedSkippedPackage);
         assertThat(mService.onStartJob(jobService, params)).isTrue();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 52a550b..d056348 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -21,10 +21,12 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
 import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
 
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -51,7 +53,9 @@
 import android.content.pm.ServiceInfo;
 import android.graphics.drawable.Icon;
 import android.hardware.display.DisplayManagerGlobal;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.TestableContext;
 import android.view.Display;
@@ -61,6 +65,7 @@
 import android.view.accessibility.AccessibilityWindowAttributes;
 
 import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 
 import com.android.compatibility.common.util.TestUtils;
@@ -76,12 +81,12 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatchers;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -92,13 +97,16 @@
  * APCT tests for {@link AccessibilityManagerService}.
  */
 public class AccessibilityManagerServiceTest {
-    private static final String TAG = "A11Y_MANAGER_SERVICE_TEST";
+    @Rule
+    public final A11yTestableContext mTestableContext = new A11yTestableContext(
+            ApplicationProvider.getApplicationContext());
+
     private static final int ACTION_ID = 20;
     private static final String LABEL = "label";
     private static final String INTENT_ACTION = "TESTACTION";
     private static final String DESCRIPTION = "description";
     private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
-            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION),
+            ApplicationProvider.getApplicationContext(), 0, new Intent(INTENT_ACTION),
             PendingIntent.FLAG_MUTABLE_UNAUDITED);
     private static final RemoteAction TEST_ACTION = new RemoteAction(
             Icon.createWithContentUri("content://test"),
@@ -130,11 +138,7 @@
     @Mock private MagnificationController mMockMagnificationController;
     @Mock private FullScreenMagnificationController mMockFullScreenMagnificationController;
     @Mock private ProxyManager mProxyManager;
-
-    @Rule
-    public final TestableContext mTestableContext = new TestableContext(
-            getInstrumentation().getTargetContext(), null);
-
+    @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
     private MessageCapturingHandler mHandler = new MessageCapturingHandler(null);
     private AccessibilityServiceConnection mAccessibilityServiceConnection;
     private AccessibilityInputFilter mInputFilter;
@@ -228,11 +232,8 @@
         doThrow(SecurityException.class).when(mMockSecurityPolicy)
                 .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
 
-        try {
-            mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID);
-            Assert.fail();
-        } catch (SecurityException expected) {
-        }
+        assertThrows(SecurityException.class,
+                () -> mA11yms.registerSystemAction(TEST_ACTION, ACTION_ID));
         verify(mMockSystemActionPerformer, never()).registerSystemAction(ACTION_ID, TEST_ACTION);
     }
 
@@ -248,11 +249,8 @@
         doThrow(SecurityException.class).when(mMockSecurityPolicy)
                 .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
 
-        try {
-            mA11yms.unregisterSystemAction(ACTION_ID);
-            Assert.fail();
-        } catch (SecurityException expected) {
-        }
+        assertThrows(SecurityException.class,
+                () -> mA11yms.unregisterSystemAction(ACTION_ID));
         verify(mMockSystemActionPerformer, never()).unregisterSystemAction(ACTION_ID);
     }
 
@@ -290,11 +288,9 @@
     public void testRegisterProxyWithoutPermission() throws Exception {
         doThrow(SecurityException.class).when(mMockSecurityPolicy)
                 .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
-        try {
-            mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY);
-            Assert.fail();
-        } catch (SecurityException expected) {
-        }
+
+        assertThrows(SecurityException.class,
+                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
                 any(), any(), any(), any());
     }
@@ -302,11 +298,8 @@
     @SmallTest
     @Test
     public void testRegisterProxyForDefaultDisplay() throws Exception {
-        try {
-            mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY);
-            Assert.fail();
-        } catch (IllegalArgumentException expected) {
-        }
+        assertThrows(IllegalArgumentException.class,
+                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
                 any(), any(), any(), any());
     }
@@ -314,11 +307,8 @@
     @SmallTest
     @Test
     public void testRegisterProxyForInvalidDisplay() throws Exception {
-        try {
-            mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY);
-            Assert.fail();
-        } catch (IllegalArgumentException expected) {
-        }
+        assertThrows(IllegalArgumentException.class,
+                () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.INVALID_DISPLAY));
         verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(),
                 any(), any(), any(), any());
     }
@@ -337,11 +327,9 @@
     public void testUnRegisterProxyWithoutPermission() throws Exception {
         doThrow(SecurityException.class).when(mMockSecurityPolicy)
                 .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
-        try {
-            mA11yms.unregisterProxyForDisplay(TEST_DISPLAY);
-            Assert.fail();
-        } catch (SecurityException expected) {
-        }
+
+        assertThrows(SecurityException.class,
+                () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY));
         verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY);
     }
 
@@ -357,8 +345,9 @@
 
         mA11yms.onMagnificationTransitionEndedLocked(Display.DEFAULT_DISPLAY, false);
 
-        Assert.assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW,
-                userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY));
+        assertThat(userState.getMagnificationModeLocked(Display.DEFAULT_DISPLAY)).isEqualTo(
+                ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
     }
 
     @SmallTest
@@ -376,7 +365,7 @@
 
         ArgumentCaptor<Display> displayCaptor = ArgumentCaptor.forClass(Display.class);
         verify(mInputFilter, timeout(100)).refreshMagnificationMode(displayCaptor.capture());
-        Assert.assertEquals(Display.DEFAULT_DISPLAY, displayCaptor.getValue().getDisplayId());
+        assertThat(displayCaptor.getValue().getDisplayId()).isEqualTo(Display.DEFAULT_DISPLAY);
     }
 
     @SmallTest
@@ -506,10 +495,59 @@
                 attributes);
     }
 
+    @SmallTest
+    @Test
+    public void testPerformAccessibilityShortcut_hearingAids_startActivityWithExpectedComponent() {
+        final AccessibilityUserState userState = mA11yms.mUserStates.get(
+                mA11yms.getCurrentUserIdLocked());
+        mockManageAccessibilityGranted(mTestableContext);
+        userState.mAccessibilityShortcutKeyTargets.add(
+                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+
+        mA11yms.performAccessibilityShortcut(
+                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+        mHandler.sendAllMessages();
+
+        assertStartActivityWithExpectedComponentName(mTestableContext.getMockContext(),
+                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+    }
+
+    private void mockManageAccessibilityGranted(TestableContext context) {
+        context.getTestablePermissions().setPermission(Manifest.permission.MANAGE_ACCESSIBILITY,
+                PackageManager.PERMISSION_GRANTED);
+    }
+
+    private void assertStartActivityWithExpectedComponentName(Context mockContext,
+            String componentName) {
+        verify(mockContext).startActivityAsUser(mIntentArgumentCaptor.capture(),
+                any(Bundle.class), any(UserHandle.class));
+        assertThat(mIntentArgumentCaptor.getValue().getStringExtra(
+                Intent.EXTRA_COMPONENT_NAME)).isEqualTo(componentName);
+    }
+
     public static class FakeInputFilter extends AccessibilityInputFilter {
         FakeInputFilter(Context context,
                 AccessibilityManagerService service) {
             super(context, service);
         }
     }
+
+    private static class A11yTestableContext extends TestableContext {
+
+        private final Context mMockContext;
+
+        A11yTestableContext(Context base) {
+            super(base);
+            mMockContext = Mockito.mock(Context.class);
+        }
+
+        @Override
+        public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
+            mMockContext.startActivityAsUser(intent, options, user);
+        }
+
+        Context getMockContext() {
+            return mMockContext;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a55b196..61c3f13 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5014,7 +5014,8 @@
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
         when(getServices().userManager.getPrimaryUser())
                 .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
-
+        when(getServices().subscriptionManager.getActiveSubscriptionIdList(false)).thenReturn(
+                new int[1]);
         // Set some device-wide policies:
         // Security logging
         when(getServices().settings.securityLogGetLoggingEnabledProperty()).thenReturn(true);
@@ -5062,6 +5063,7 @@
         // Unsuspend personal apps
         verify(getServices().packageManagerInternal)
                 .unsuspendForSuspendingPackage(PLATFORM_PACKAGE_NAME, UserHandle.USER_SYSTEM);
+        verify(getServices().subscriptionManager).setSubscriptionUserHandle(0, null);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index ac1667d..c739969 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -239,6 +239,8 @@
                 return mMockSystemServices.locationManager;
             case Context.ROLE_SERVICE:
                 return mMockSystemServices.roleManager;
+            case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
+                return mMockSystemServices.subscriptionManager;
         }
         throw new UnsupportedOperationException();
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index cec9d50..2a6a979 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -63,6 +63,7 @@
 import android.permission.IPermissionManager;
 import android.provider.Settings;
 import android.security.KeyChain;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
@@ -135,6 +136,7 @@
     public final DevicePolicyManager devicePolicyManager;
     public final LocationManager locationManager;
     public final RoleManager roleManager;
+    public final SubscriptionManager subscriptionManager;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
     public final BuildMock buildMock = new BuildMock();
@@ -188,6 +190,7 @@
         devicePolicyManager = mock(DevicePolicyManager.class);
         locationManager = mock(LocationManager.class);
         roleManager = realContext.getSystemService(RoleManager.class);
+        subscriptionManager = mock(SubscriptionManager.class);
 
         // Package manager is huge, so we use a partial mock instead.
         packageManager = spy(realContext.getPackageManager());
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 4b417ba..a857238 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -20,6 +20,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -44,7 +45,6 @@
 import android.app.time.UnixEpochTime;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TelephonyTimeSuggestion;
-import android.app.timedetector.TimePoint;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.HandlerThread;
@@ -385,8 +385,8 @@
                 1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
         when(mMockNtpTrustedTime.getCachedTimeResult())
                 .thenReturn(latestNetworkTime);
-        TimePoint expected = new TimePoint(latestNetworkTime.getTimeMillis(),
-                latestNetworkTime.getElapsedRealtimeMillis());
+        UnixEpochTime expected = new UnixEpochTime(
+                latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
         assertEquals(expected, mTimeDetectorService.latestNetworkTime());
     }
 
@@ -397,6 +397,25 @@
     }
 
     @Test
+    public void testGetLatestNetworkSuggestion() {
+        NtpTrustedTime.TimeResult latestNetworkTime = new NtpTrustedTime.TimeResult(
+                1234L, 54321L, 999, InetSocketAddress.createUnresolved("test.timeserver", 123));
+        when(mMockNtpTrustedTime.getCachedTimeResult())
+                .thenReturn(latestNetworkTime);
+        UnixEpochTime expectedUnixEpochTime = new UnixEpochTime(
+                latestNetworkTime.getElapsedRealtimeMillis(), latestNetworkTime.getTimeMillis());
+        NetworkTimeSuggestion expected = new NetworkTimeSuggestion(
+                expectedUnixEpochTime, latestNetworkTime.getUncertaintyMillis());
+        assertEquals(expected, mTimeDetectorService.getLatestNetworkSuggestion());
+    }
+
+    @Test
+    public void testGetLatestNetworkSuggestion_noTimeAvailable() {
+        when(mMockNtpTrustedTime.getCachedTimeResult()).thenReturn(null);
+        assertNull(mTimeDetectorService.getLatestNetworkSuggestion());
+    }
+
+    @Test
     public void testGetTimeState() {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
         TimeState fakeState = new TimeState(new UnixEpochTime(12345L, 98765L), true);