Merge "Update CPA startActivity javadoc to refer to passing the result back"
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index 531436e..fd4b106 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -307,6 +307,41 @@
         }
     }
 
+    /**
+     * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
+     * match the {@code queryExpression} in given namespaces and schemaTypes which is set via
+     * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchema}.
+     *
+     * <p> An empty {@code queryExpression} matches all documents.
+     *
+     * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in
+     * the current database.
+     *
+     * @param queryExpression Query String to search.
+     * @param searchSpec Defines what and how to remove
+     * @param executor Executor on which to invoke the callback.
+     * @param callback Callback to receive errors resulting from removing the documents. If the
+     *                 operation succeeds, the callback will be invoked with {@code null}.
+     */
+    public void removeByQuery(@NonNull String queryExpression,
+            @NonNull SearchSpec searchSpec,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<AppSearchResult<Void>> callback) {
+        Objects.requireNonNull(queryExpression);
+        Objects.requireNonNull(searchSpec);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+        try {
+            mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(),
+                    new IAppSearchResultCallback.Stub() {
+                        public void onResult(AppSearchResult result) {
+                            executor.execute(() -> callback.accept(result));
+                        }
+                    });
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     // TODO(b/162450968) port query() and SearchResults.java to platform.
-    // TODO(b/162450968) port removeByQuery() to platform.
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index 4a98102..62e60d7 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -119,13 +119,14 @@
      * @param databaseName The databaseName this query for.
      * @param queryExpression String to search for
      * @param searchSpecBundle SearchSpec bundle
-     * @param callback {@link AndroidFuture}&lt;{@link AppSearchResult}&lt;{@link SearchResults}&gt;&gt;
+     * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
+     *     {@link AppSearchResult}&lt;{@link Void}&gt;.
      */
     void removeByQuery(
         in String databaseName,
         in String queryExpression,
         in Bundle searchSpecBundle,
-        in AndroidFuture<AppSearchResult> callback);
+        in IAppSearchResultCallback callback);
 
     /**
      * Creates and initializes AppSearchImpl for the calling app.
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 8269799..c9dd89c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -225,21 +225,21 @@
                 @NonNull String databaseName,
                 @NonNull String queryExpression,
                 @NonNull Bundle searchSpecBundle,
-                @NonNull AndroidFuture<AppSearchResult> callback) {
+                @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(databaseName);
             Preconditions.checkNotNull(queryExpression);
             Preconditions.checkNotNull(searchSpecBundle);
-            Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUidOrThrow();
             int callingUserId = UserHandle.getUserId(callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                 databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid);
-                impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle));
-                callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null));
+                impl.removeByQuery(databaseName, queryExpression,
+                        new SearchSpec(searchSpecBundle));
+                invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
-                callback.complete(throwableToFailedResult(t));
+                invokeCallbackOnError(callback, t);
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 2c8a558..3597c27 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -462,11 +462,11 @@
     public @NetworkType int getNetworkType() {
         if (networkRequest == null) {
             return NETWORK_TYPE_NONE;
-        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED)) {
+        } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_METERED)) {
             return NETWORK_TYPE_UNMETERED;
-        } else if (networkRequest.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+        } else if (networkRequest.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
             return NETWORK_TYPE_NOT_ROAMING;
-        } else if (networkRequest.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+        } else if (networkRequest.hasTransport(TRANSPORT_CELLULAR)) {
             return NETWORK_TYPE_CELLULAR;
         } else {
             return NETWORK_TYPE_ANY;
@@ -1558,7 +1558,7 @@
         if (isPersisted) {
             // We can't serialize network specifiers
             if (networkRequest != null
-                    && networkRequest.networkCapabilities.getNetworkSpecifier() != null) {
+                    && networkRequest.getNetworkSpecifier() != null) {
                 throw new IllegalArgumentException(
                         "Network specifiers aren't supported for persistent jobs");
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d4ea7af..20c77da 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -417,6 +417,9 @@
         @VisibleForTesting
         static final String KEY_LAZY_BATCHING = "lazy_batching";
 
+        private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE =
+                "time_tick_allowed_while_idle";
+
         private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
         private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
         private static final long DEFAULT_MAX_INTERVAL = 365 * DateUtils.DAY_IN_MILLIS;
@@ -440,6 +443,7 @@
         private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY;
 
         private static final boolean DEFAULT_LAZY_BATCHING = true;
+        private static final boolean DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE = true;
 
         // Minimum futurity of a new alarm
         public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -470,6 +474,7 @@
         public long APP_STANDBY_RESTRICTED_WINDOW = DEFAULT_APP_STANDBY_RESTRICTED_WINDOW;
 
         public boolean LAZY_BATCHING = DEFAULT_LAZY_BATCHING;
+        public boolean TIME_TICK_ALLOWED_WHILE_IDLE = DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE;
 
         private long mLastAllowWhileIdleWhitelistDuration = -1;
 
@@ -557,6 +562,11 @@
                                 migrateAlarmsToNewStoreLocked();
                             }
                             break;
+                        case KEY_TIME_TICK_ALLOWED_WHILE_IDLE:
+                            TIME_TICK_ALLOWED_WHILE_IDLE = properties.getBoolean(
+                                    KEY_TIME_TICK_ALLOWED_WHILE_IDLE,
+                                    DEFAULT_TIME_TICK_ALLOWED_WHILE_IDLE);
+                            break;
                         default:
                             if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
                                 // The quotas need to be updated in order, so we can't just rely
@@ -690,6 +700,9 @@
             pw.print(KEY_LAZY_BATCHING, LAZY_BATCHING);
             pw.println();
 
+            pw.print(KEY_TIME_TICK_ALLOWED_WHILE_IDLE, TIME_TICK_ALLOWED_WHILE_IDLE);
+            pw.println();
+
             pw.decreaseIndent();
         }
 
@@ -3756,9 +3769,14 @@
             final long tickEventDelay = nextTime - currentTime;
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
+
+            int flags = AlarmManager.FLAG_STANDALONE;
+            flags |= mConstants.TIME_TICK_ALLOWED_WHILE_IDLE ? FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED
+                    : 0;
+
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, null, mTimeTickTrigger, TIME_TICK_TAG, AlarmManager.FLAG_STANDALONE,
-                    workSource, null, Process.myUid(), "android");
+                    0, null, mTimeTickTrigger, TIME_TICK_TAG, flags, workSource, null,
+                    Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
             synchronized (mLock) {
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index c2ee6dc..ef1e413 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -101,8 +101,6 @@
             runFstrim();
         } else if ("set-virtual-disk".equals(op)) {
             runSetVirtualDisk();
-        } else if ("set-isolated-storage".equals(op)) {
-            runIsolatedStorage();
         } else if ("start-checkpoint".equals(op)) {
             runStartCheckpoint();
         } else if ("supports-checkpoint".equals(op)) {
@@ -286,28 +284,6 @@
                 StorageManager.DEBUG_VIRTUAL_DISK);
     }
 
-    public void runIsolatedStorage() throws RemoteException {
-        final int value;
-        final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON
-                | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF;
-        switch (nextArg()) {
-            case "on":
-            case "true":
-                value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON;
-                break;
-            case "off":
-                value = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF;
-                break;
-            case "default":
-            case "false":
-                value = 0;
-                break;
-            default:
-                return;
-        }
-        mSm.setDebugFlags(value, mask);
-    }
-
     public void runIdleMaint() throws RemoteException {
         final boolean im_run = "run".equals(nextArg());
         if (im_run) {
@@ -367,8 +343,6 @@
         System.err.println("");
         System.err.println("       sm set-emulate-fbe [true|false]");
         System.err.println("");
-        System.err.println("       sm set-isolated-storage [on|off|default]");
-        System.err.println("");
         System.err.println("       sm start-checkpoint <num-retries>");
         System.err.println("");
         System.err.println("       sm supports-checkpoint");
diff --git a/cmds/statsd/src/OWNERS b/cmds/statsd/src/OWNERS
deleted file mode 100644
index 0f3ddf7..0000000
--- a/cmds/statsd/src/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Temporary OWNERS Block to assist with migration
-# bug: 167962588
-per-file *atoms.proto = set noparent
-per-file *atom_field_options.proto = set noparent
-per-file *atoms.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com
-per-file *atom_field_options.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index e302b25..07f8395 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6968,6 +6968,7 @@
     method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
     method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int);
+    method public boolean hasKeyPair(@NonNull String);
     method public boolean hasLockdownAdminConfiguredNetworks(@NonNull android.content.ComponentName);
     method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]);
     method public boolean installExistingPackage(@NonNull android.content.ComponentName, String);
@@ -7343,6 +7344,7 @@
     field public static final int TAG_MEDIA_UNMOUNT = 210014; // 0x3345e
     field public static final int TAG_OS_SHUTDOWN = 210010; // 0x3345a
     field public static final int TAG_OS_STARTUP = 210009; // 0x33459
+    field public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED = 210035; // 0x33473
     field public static final int TAG_PASSWORD_COMPLEXITY_SET = 210017; // 0x33461
     field public static final int TAG_PASSWORD_EXPIRATION_SET = 210016; // 0x33460
     field public static final int TAG_PASSWORD_HISTORY_LENGTH_SET = 210018; // 0x33462
@@ -50849,6 +50851,33 @@
     method public void onActionViewExpanded();
   }
 
+  public final class ContentInfo {
+    method @NonNull public android.content.ClipData getClip();
+    method @Nullable public android.os.Bundle getExtras();
+    method public int getFlags();
+    method @Nullable public android.net.Uri getLinkUri();
+    method public int getSource();
+    method @NonNull public java.util.Map<java.lang.Boolean,android.view.ContentInfo> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
+    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
+    field public static final int SOURCE_APP = 0; // 0x0
+    field public static final int SOURCE_AUTOFILL = 4; // 0x4
+    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
+    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
+    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
+    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
+  }
+
+  public static final class ContentInfo.Builder {
+    ctor public ContentInfo.Builder(@NonNull android.view.ContentInfo);
+    ctor public ContentInfo.Builder(@NonNull android.content.ClipData, int);
+    method @NonNull public android.view.ContentInfo build();
+    method @NonNull public android.view.ContentInfo.Builder setClip(@NonNull android.content.ClipData);
+    method @NonNull public android.view.ContentInfo.Builder setExtras(@Nullable android.os.Bundle);
+    method @NonNull public android.view.ContentInfo.Builder setFlags(int);
+    method @NonNull public android.view.ContentInfo.Builder setLinkUri(@Nullable android.net.Uri);
+    method @NonNull public android.view.ContentInfo.Builder setSource(int);
+  }
+
   public interface ContextMenu extends android.view.Menu {
     method public void clearHeader();
     method public android.view.ContextMenu setHeaderIcon(@DrawableRes int);
@@ -52064,34 +52093,7 @@
   }
 
   public interface OnReceiveContentListener {
-    method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.View, @NonNull android.view.OnReceiveContentListener.Payload);
-  }
-
-  public static final class OnReceiveContentListener.Payload {
-    method @NonNull public android.content.ClipData getClip();
-    method @Nullable public android.os.Bundle getExtras();
-    method public int getFlags();
-    method @Nullable public android.net.Uri getLinkUri();
-    method public int getSource();
-    method @NonNull public java.util.Map<java.lang.Boolean,android.view.OnReceiveContentListener.Payload> partition(@NonNull java.util.function.Predicate<android.content.ClipData.Item>);
-    field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1
-    field public static final int SOURCE_APP = 0; // 0x0
-    field public static final int SOURCE_AUTOFILL = 4; // 0x4
-    field public static final int SOURCE_CLIPBOARD = 1; // 0x1
-    field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3
-    field public static final int SOURCE_INPUT_METHOD = 2; // 0x2
-    field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5
-  }
-
-  public static final class OnReceiveContentListener.Payload.Builder {
-    ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.view.OnReceiveContentListener.Payload);
-    ctor public OnReceiveContentListener.Payload.Builder(@NonNull android.content.ClipData, int);
-    method @NonNull public android.view.OnReceiveContentListener.Payload build();
-    method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setClip(@NonNull android.content.ClipData);
-    method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setExtras(@Nullable android.os.Bundle);
-    method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setFlags(int);
-    method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setLinkUri(@Nullable android.net.Uri);
-    method @NonNull public android.view.OnReceiveContentListener.Payload.Builder setSource(int);
+    method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.View, @NonNull android.view.ContentInfo);
   }
 
   public abstract class OrientationEventListener {
@@ -52842,7 +52844,7 @@
     method public void onProvideContentCaptureStructure(@NonNull android.view.ViewStructure, int);
     method public void onProvideStructure(android.view.ViewStructure);
     method public void onProvideVirtualStructure(android.view.ViewStructure);
-    method @Nullable public android.view.OnReceiveContentListener.Payload onReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
+    method @Nullable public android.view.ContentInfo onReceiveContent(@NonNull android.view.ContentInfo);
     method public android.view.PointerIcon onResolvePointerIcon(android.view.MotionEvent, int);
     method @CallSuper protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
@@ -52868,7 +52870,7 @@
     method public boolean performHapticFeedback(int, int);
     method public boolean performLongClick();
     method public boolean performLongClick(float, float);
-    method @Nullable public android.view.OnReceiveContentListener.Payload performReceiveContent(@NonNull android.view.OnReceiveContentListener.Payload);
+    method @Nullable public android.view.ContentInfo performReceiveContent(@NonNull android.view.ContentInfo);
     method public void playSoundEffect(int);
     method public boolean post(Runnable);
     method public boolean postDelayed(Runnable, long);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 99179ec..8ba5a94 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -8420,6 +8420,8 @@
     field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
     field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
     field public static final String NAMESPACE_SCHEDULER = "scheduler";
+    field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
+    field public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
     field @Deprecated public static final String NAMESPACE_STORAGE = "storage";
     field public static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot";
     field public static final String NAMESPACE_SYSTEMUI = "systemui";
@@ -11432,6 +11434,61 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.AudioCodecAttributes> CREATOR;
   }
 
+  public interface DelegateMessageCallback {
+    method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
+    method public void onMessageSendFailure(@NonNull String, int);
+    method public void onMessageSent(@NonNull String);
+  }
+
+  public final class DelegateRegistrationState implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteredFeatureTags();
+    method @NonNull public java.util.Set<android.telephony.ims.FeatureTagState> getDeregisteringFeatureTags();
+    method @NonNull public java.util.Set<java.lang.String> getRegisteredFeatureTags();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRegistrationState> CREATOR;
+    field public static final int DEREGISTERED_REASON_NOT_PROVISIONED = 1; // 0x1
+    field public static final int DEREGISTERED_REASON_NOT_REGISTERED = 2; // 0x2
+    field public static final int DEREGISTERED_REASON_UNKNOWN = 0; // 0x0
+    field public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6; // 0x6
+    field public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5; // 0x5
+    field public static final int DEREGISTERING_REASON_PDN_CHANGE = 3; // 0x3
+    field public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4; // 0x4
+  }
+
+  public static final class DelegateRegistrationState.Builder {
+    ctor public DelegateRegistrationState.Builder();
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteredFeatureTag(@NonNull String, int);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addDeregisteringFeatureTag(@NonNull String, int);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTag(@NonNull String);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState.Builder addRegisteredFeatureTags(@NonNull java.util.Set<java.lang.String>);
+    method @NonNull public android.telephony.ims.DelegateRegistrationState build();
+  }
+
+  public final class DelegateRequest implements android.os.Parcelable {
+    ctor public DelegateRequest(@NonNull java.util.Set<java.lang.String>);
+    method public int describeContents();
+    method @NonNull public java.util.Set<java.lang.String> getFeatureTags();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.DelegateRequest> CREATOR;
+  }
+
+  public interface DelegateStateCallback {
+    method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>);
+    method public void onDestroyed(int);
+    method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState);
+    method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
+  public final class FeatureTagState implements android.os.Parcelable {
+    ctor public FeatureTagState(@NonNull String, int);
+    method public int describeContents();
+    method @NonNull public String getFeatureTag();
+    method public int getState();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.FeatureTagState> CREATOR;
+  }
+
   public final class ImsCallForwardInfo implements android.os.Parcelable {
     ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int);
     method public int describeContents();
@@ -11952,8 +12009,107 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RtpHeaderExtensionType> CREATOR;
   }
 
+  public interface SipDelegateConnection {
+    method public void notifyMessageReceiveError(@NonNull String, int);
+    method public void notifyMessageReceived(@NonNull String);
+    method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
+  }
+
+  public final class SipDelegateImsConfiguration implements android.os.Parcelable {
+    method public boolean containsKey(@NonNull String);
+    method @NonNull public android.os.PersistableBundle copyBundle();
+    method public int describeContents();
+    method public boolean getBoolean(@NonNull String, boolean);
+    method public int getInt(@NonNull String, int);
+    method @Nullable public String getString(@NonNull String);
+    method public long getVersion();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+    field public static final String IPTYPE_IPV4 = "IPV4";
+    field public static final String IPTYPE_IPV6 = "IPV6";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+    field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+    field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+    field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+    field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+    field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+    field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+    field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+    field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+    field public static final String SIP_TRANSPORT_TCP = "TCP";
+    field public static final String SIP_TRANSPORT_UDP = "UDP";
+  }
+
+  public static final class SipDelegateImsConfiguration.Builder {
+    ctor public SipDelegateImsConfiguration.Builder(int);
+    ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+  }
+
   public class SipDelegateManager {
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSupported() throws android.telephony.ims.ImsException;
+    field public static final int DENIED_REASON_INVALID = 4; // 0x4
+    field public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1; // 0x1
+    field public static final int DENIED_REASON_NOT_ALLOWED = 2; // 0x2
+    field public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3; // 0x3
+    field public static final int DENIED_REASON_UNKNOWN = 0; // 0x0
+    field public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2; // 0x2
+    field public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1; // 0x1
+    field public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11; // 0xb
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5; // 0x5
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6; // 0x6
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4; // 0x4
+    field public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3; // 0x3
+    field public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8; // 0x8
+    field public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9; // 0x9
+    field public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10; // 0xa
+    field public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7; // 0x7
+    field public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0; // 0x0
+    field public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2; // 0x2
+    field public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1; // 0x1
+    field public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4; // 0x4
+    field public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0; // 0x0
+    field public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3; // 0x3
+  }
+
+  public final class SipMessage implements android.os.Parcelable {
+    ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
+    method public int describeContents();
+    method @NonNull public byte[] getContent();
+    method @NonNull public String getHeaderSection();
+    method @NonNull public String getStartLine();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
 
 }
@@ -12047,6 +12203,19 @@
 
 package android.telephony.ims.stub {
 
+  public interface DelegateConnectionMessageCallback {
+    method public void onMessageReceived(@NonNull android.telephony.ims.SipMessage);
+    method public void onMessageSendFailure(@NonNull String, int);
+    method public void onMessageSent(@NonNull String);
+  }
+
+  public interface DelegateConnectionStateCallback {
+    method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection);
+    method public void onDestroyed(int);
+    method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>);
+    method public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
   public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
     ctor public ImsCallSessionImplBase();
     method public void accept(int, android.telephony.ims.ImsStreamMediaProfile);
@@ -12207,8 +12376,17 @@
     method public int updateColr(int);
   }
 
+  public interface SipDelegate {
+    method public void closeDialog(@NonNull String);
+    method public void notifyMessageReceiveError(@NonNull String, int);
+    method public void notifyMessageReceived(@NonNull String);
+    method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
+  }
+
   public class SipTransportImplBase {
     ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
+    method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback);
+    method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);
   }
 
 }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5434bcc..e392ed7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -7,6 +7,7 @@
     field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
     field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
     field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
+    field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
     field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
     field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
     field public static final String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
@@ -1499,7 +1500,6 @@
     field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
     field public static final String HIDDEN_API_POLICY = "hidden_api_policy";
     field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
-    field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
     field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist";
     field public static final String LOW_POWER_MODE = "low_power";
     field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky";
diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java
index fc93f03..08861d4 100644
--- a/core/java/android/annotation/RequiresFeature.java
+++ b/core/java/android/annotation/RequiresFeature.java
@@ -30,7 +30,6 @@
  * Denotes that the annotated element requires one or more device features. This
  * is used to auto-generate documentation.
  *
- * @see PackageManager#hasSystemFeature(String)
  * @hide
  */
 @Retention(SOURCE)
@@ -38,8 +37,16 @@
 public @interface RequiresFeature {
     /**
      * The name of the device feature that is required.
-     *
-     * @see PackageManager#hasSystemFeature(String)
      */
     String value();
+
+    /**
+     * Defines the name of the method that should be called to check whether the feature is
+     * available, using the same signature format as javadoc. The feature checking method can have
+     * multiple parameters, but the feature name parameter must be of type String and must also be
+     * the first String-type parameter.
+     * <p>
+     * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}.
+     */
+    String enforcement() default("android.content.pm.PackageManager#hasSystemFeature");
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fc95718..1c2f682 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5189,8 +5189,8 @@
      * result callbacks including {@link #onRequestPermissionsResult(int, String[], int[])}.
      * </p>
      * <p>
-     * The <a href="https://github.com/googlesamples/android-RuntimePermissions">
-     * RuntimePermissions</a> sample app demonstrates how to use this method to
+     * The <a href="https://github.com/android/permissions-samples">
+     * RuntimePermissions</a> sample apps demonstrate how to use this method to
      * request permissions at run time.
      * </p>
      *
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5537edb..e3048df 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1854,16 +1854,12 @@
      * the recent tasks.
      */
     @Deprecated
-    public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags)
-            throws SecurityException {
-        try {
-            if (maxNum < 0) {
-                throw new IllegalArgumentException("The requested number of tasks should be >= 0");
-            }
-            return getTaskService().getRecentTasks(maxNum, flags, mContext.getUserId()).getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+    public List<RecentTaskInfo> getRecentTasks(int maxNum, int flags) throws SecurityException {
+        if (maxNum < 0) {
+            throw new IllegalArgumentException("The requested number of tasks should be >= 0");
         }
+        return ActivityTaskManager.getInstance().getRecentTasks(
+                maxNum, flags, mContext.getUserId());
     }
 
     /**
@@ -2084,11 +2080,7 @@
     @Deprecated
     public List<RunningTaskInfo> getRunningTasks(int maxNum)
             throws SecurityException {
-        try {
-            return getTaskService().getTasks(maxNum);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return ActivityTaskManager.getInstance().getTasks(maxNum);
     }
 
     /**
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index c7b9089..03c1a01 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -27,7 +27,6 @@
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -35,6 +34,7 @@
 import android.os.ServiceManager;
 import android.util.DisplayMetrics;
 import android.util.Singleton;
+import android.view.RemoteAnimationDefinition;
 
 import java.util.List;
 
@@ -147,7 +147,20 @@
 
     private static int sMaxRecentTasks = -1;
 
-    ActivityTaskManager(Context context, Handler handler) {
+    private static final Singleton<ActivityTaskManager> sInstance =
+            new Singleton<ActivityTaskManager>() {
+                @Override
+                protected ActivityTaskManager create() {
+                    return new ActivityTaskManager();
+                }
+            };
+
+    private ActivityTaskManager() {
+    }
+
+    /** @hide */
+    public static ActivityTaskManager getInstance() {
+        return sInstance.get();
     }
 
     /** @hide */
@@ -444,6 +457,98 @@
     }
 
     /**
+     * @return List of running tasks.
+     * @hide
+     */
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
+        return getTasks(maxNum, false /* filterForVisibleRecents */);
+    }
+
+    /**
+     * @return List of running tasks that can be filtered by visibility in recents.
+     * @hide
+     */
+    public List<ActivityManager.RunningTaskInfo> getTasks(
+            int maxNum, boolean filterOnlyVisibleRecents) {
+        try {
+            return getService().getTasks(maxNum, filterOnlyVisibleRecents);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @return List of recent tasks.
+     * @hide
+     */
+    public List<ActivityManager.RecentTaskInfo> getRecentTasks(
+            int maxNum, int flags, int userId) {
+        try {
+            return getService().getRecentTasks(maxNum, flags, userId).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void registerTaskStackListener(TaskStackListener listener) {
+        try {
+            getService().registerTaskStackListener(listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public void unregisterTaskStackListener(TaskStackListener listener) {
+        try {
+            getService().unregisterTaskStackListener(listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public Rect getTaskBounds(int taskId) {
+        try {
+            return getService().getTaskBounds(taskId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers remote animations for a display.
+     * @hide
+     */
+    public void registerRemoteAnimationsForDisplay(
+            int displayId, RemoteAnimationDefinition definition) {
+        try {
+            getService().registerRemoteAnimationsForDisplay(displayId, definition);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public boolean isInLockTaskMode() {
+        try {
+            return getService().isInLockTaskMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /** @hide */
+    public boolean removeTask(int taskId) {
+        try {
+            return getService().removeTask(taskId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Information you can retrieve about a root task in the system.
      * @hide
      */
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index bd5913e..ab48bae 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -83,6 +83,15 @@
  *
  * {@hide}
  */
+// TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external
+// caller go through that call instead. This would help us better separate and control the API
+// surface exposed.
+// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token')
+// to a separate interface that is only available to the Activity.
+// TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce
+// interface duplication.
+// TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be
+// going through task organizer now.
 interface IActivityTaskManager {
     int startActivity(in IApplicationThread caller, in String callingPackage,
             in String callingFeatureId, in Intent intent, in String resolvedType,
@@ -154,9 +163,7 @@
     void setFocusedTask(int taskId);
     boolean removeTask(int taskId);
     void removeAllVisibleRecentTasks();
-    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
-    List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
-            boolean filterOnlyVisibleRecents);
+    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents);
     boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
     boolean navigateUpTo(in IBinder token, in Intent target, int resultCode,
             in Intent resultData);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a886bed..d5977e7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -30,6 +30,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Px;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -4873,6 +4874,7 @@
             // Small icon doesn't need to be reset, as it's always set. Resetting would prevent
             // re-using the drawable when the notification is updated.
             contentView.setBoolean(R.id.expand_button, "setExpanded", false);
+            contentView.setViewVisibility(R.id.app_name_text, View.GONE);
             contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
             contentView.setViewVisibility(R.id.header_text, View.GONE);
@@ -5105,33 +5107,29 @@
             if (result == null) {
                 result = new TemplateBindResult();
             }
-            boolean largeIconShown = bindLargeIcon(contentView, p);
+            final boolean largeIconShown = bindLargeIcon(contentView, p);
             calculateLargeIconMarginEnd(largeIconShown, result);
             if (p.mHeaderless) {
                 // views in the headerless (collapsed) state
-                contentView.setViewLayoutMarginEnd(R.id.notification_standard_view_column,
-                        result.getHeadingExtraMarginEnd());
+                result.mHeadingExtraMarginSet.applyToView(contentView,
+                        R.id.notification_headerless_view_column);
             } else {
                 // views in states with a header (big states)
-                contentView.setInt(R.id.notification_header, "setTopLineExtraMarginEnd",
-                        result.getHeadingExtraMarginEnd());
-                contentView.setViewLayoutMarginEnd(R.id.line1, result.getTitleMarginEnd());
+                result.mHeadingExtraMarginSet.applyToView(contentView, R.id.notification_header);
+                result.mTitleMarginSet.applyToView(contentView, R.id.line1);
             }
         }
 
         private void calculateLargeIconMarginEnd(boolean largeIconShown,
                 @NonNull TemplateBindResult result) {
-            int contentMargin = mContext.getResources().getDimensionPixelSize(
+            final Resources resources = mContext.getResources();
+            final int contentMargin = resources.getDimensionPixelOffset(
                     R.dimen.notification_content_margin_end);
-            int expanderSize = mContext.getResources().getDimensionPixelSize(
+            final int expanderSize = resources.getDimensionPixelSize(
                     R.dimen.notification_header_expand_icon_size) - contentMargin;
-            int extraMarginEnd = 0;
-            if (largeIconShown) {
-                int iconSize = mContext.getResources().getDimensionPixelSize(
-                        R.dimen.notification_right_icon_size);
-                extraMarginEnd = iconSize + contentMargin;
-            }
-            result.setRightIconState(largeIconShown, extraMarginEnd, expanderSize);
+            final int extraMarginEndIfVisible = resources.getDimensionPixelSize(
+                    R.dimen.notification_right_icon_size) + contentMargin;
+            result.setRightIconState(largeIconShown, extraMarginEndIfVisible, expanderSize);
         }
 
         /**
@@ -5153,9 +5151,14 @@
 
         private void bindNotificationHeader(RemoteViews contentView, StandardTemplateParams p) {
             bindSmallIcon(contentView, p);
-            boolean hasTextToLeft = bindHeaderAppName(contentView, p);
+            // Populate text left-to-right so that separators are only shown between strings
+            boolean hasTextToLeft = bindHeaderAppName(contentView, p, false /* force */);
             hasTextToLeft |= bindHeaderTextSecondary(contentView, p, hasTextToLeft);
             hasTextToLeft |= bindHeaderText(contentView, p, hasTextToLeft);
+            if (!hasTextToLeft) {
+                // If there's still no text, force add the app name so there is some text.
+                hasTextToLeft |= bindHeaderAppName(contentView, p, true /* force */);
+            }
             bindHeaderChronometerAndTime(contentView, p, hasTextToLeft);
             bindProfileBadge(contentView, p);
             bindAlertedIcon(contentView, p);
@@ -5219,7 +5222,7 @@
                     && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
                 summaryText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
             }
-            if (summaryText != null) {
+            if (!TextUtils.isEmpty(summaryText)) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
                 contentView.setTextViewText(R.id.header_text, processTextSpans(
                         processLegacyText(summaryText)));
@@ -5291,13 +5294,13 @@
         /**
          * @return true if the app name will be visible
          */
-        private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p) {
-            if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED) {
-                contentView.setViewVisibility(R.id.app_name_text, View.GONE);
+        private boolean bindHeaderAppName(RemoteViews contentView, StandardTemplateParams p,
+                boolean force) {
+            if (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED && !force) {
+                // unless the force flag is set, don't show the app name in the minimized state.
                 return false;
             }
             if (p.mHeaderless && p.hasTitle()) {
-                contentView.setViewVisibility(R.id.app_name_text, View.GONE);
                 // the headerless template will have the TITLE in this position; return true to
                 // keep the divider visible between that title and the next text element.
                 return true;
@@ -7759,8 +7762,10 @@
             addExtras(mBuilder.mN.extras);
             if (!isConversationLayout) {
                 // also update the end margin if there is an image
+                // NOTE: This template doesn't support moving this icon to the left, so we don't
+                // need to fully apply the MarginSet
                 contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
-                        bindResult.getHeadingExtraMarginEnd());
+                        bindResult.mHeadingExtraMarginSet.getValue());
             }
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.isColorized(p)
@@ -8757,9 +8762,8 @@
             if (!headerless) {
                 // also update the end margin to account for the large icon or expander
                 Resources resources = mBuilder.mContext.getResources();
-                int endMargin = resources.getDimensionPixelSize(
-                        R.dimen.notification_content_margin_end) + result.getTitleMarginEnd();
-                remoteViews.setViewLayoutMarginEnd(R.id.notification_main_column, endMargin);
+                result.mTitleMarginSet.applyToView(remoteViews, R.id.notification_main_column,
+                        resources.getDimensionPixelOffset(R.dimen.notification_content_margin_end));
             }
         }
 
@@ -10997,42 +11001,74 @@
      */
     private static class TemplateBindResult {
         boolean mRightIconVisible;
-        int mRightIconMarginEnd;
-        int mExpanderSize;
 
         /**
-         * @return the margin end that needs to be added to the heading so that it won't overlap
+         * The margin end that needs to be added to the heading so that it won't overlap
          * with the large icon.  This value includes the space required to accommodate the large
          * icon, but should be added to the space needed to accommodate the expander. This does
          * not include the 16dp content margin that all notification views must have.
          */
-        public int getHeadingExtraMarginEnd() {
-            return mRightIconMarginEnd;
-        }
+        public final MarginSet mHeadingExtraMarginSet = new MarginSet();
 
         /**
-         * @return the margin end that needs to be added to the heading so that it won't overlap
+         * The margin end that needs to be added to the heading so that it won't overlap
          * with the large icon.  This value includes the space required to accommodate the large
          * icon as well as the expander.  This does not include the 16dp content margin that all
          * notification views must have.
          */
-        public int getHeadingFullMarginEnd() {
-            return mRightIconMarginEnd + mExpanderSize;
-        }
+        public final MarginSet mHeadingFullMarginSet = new MarginSet();
 
         /**
-         * @return the margin end that needs to be added to the title text of the big state
+         * The margin end that needs to be added to the title text of the big state
          * so that it won't overlap with the large icon, but assuming the text can run under
          * the expander when that icon is not visible.
          */
-        public int getTitleMarginEnd() {
-            return mRightIconVisible ? getHeadingFullMarginEnd() : 0;
+        public final MarginSet mTitleMarginSet = new MarginSet();
+
+        public void setRightIconState(boolean visible, int marginEndIfVisible, int expanderSize) {
+            mRightIconVisible = visible;
+            mHeadingExtraMarginSet.setValues(0, marginEndIfVisible);
+            mHeadingFullMarginSet.setValues(expanderSize, marginEndIfVisible + expanderSize);
+            mTitleMarginSet.setValues(0, marginEndIfVisible + expanderSize);
         }
 
-        public void setRightIconState(boolean visible, int marginEnd, int expanderSize) {
-            mRightIconVisible = visible;
-            mRightIconMarginEnd = marginEnd;
-            mExpanderSize = expanderSize;
+        /**
+         * This contains the end margins for a view when the right icon is visible or not.  These
+         * values are both needed so that NotificationGroupingUtil can 'move' the right_icon to the
+         * left_icon and adjust the margins, and to undo that change as well.
+         */
+        private class MarginSet {
+            private int mValueIfGone;
+            private int mValueIfVisible;
+
+            public void setValues(int valueIfGone, int valueIfVisible) {
+                mValueIfGone = valueIfGone;
+                mValueIfVisible = valueIfVisible;
+            }
+
+            public void applyToView(@NonNull RemoteViews views, @IdRes int viewId) {
+                applyToView(views, viewId, 0);
+            }
+
+            public void applyToView(@NonNull RemoteViews views, @IdRes int viewId,
+                    @Px int extraMargin) {
+                final int marginEnd = getValue() + extraMargin;
+                if (viewId == R.id.notification_header) {
+                    views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd);
+                } else {
+                    views.setViewLayoutMarginEnd(viewId, marginEnd);
+                }
+                if (mRightIconVisible) {
+                    views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible,
+                            mValueIfVisible + extraMargin);
+                    views.setIntTag(viewId, R.id.tag_margin_end_when_icon_gone,
+                            mValueIfGone + extraMargin);
+                }
+            }
+
+            public int getValue() {
+                return mRightIconVisible ? mValueIfVisible : mValueIfGone;
+            }
         }
     }
 
@@ -11074,7 +11110,9 @@
         }
 
         final boolean hasTitle() {
-            return title != null && title.length() != 0 && !mHasCustomContent;
+            // We hide the title when the notification is a decorated custom view so that decorated
+            // custom views always have to include their own title.
+            return !TextUtils.isEmpty(title) && !mHasCustomContent;
         }
 
         final StandardTemplateParams viewType(int viewType) {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 7377790..7287acd 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -284,8 +284,7 @@
                 new CachedServiceFetcher<ActivityTaskManager>() {
             @Override
             public ActivityTaskManager createService(ContextImpl ctx) {
-                return new ActivityTaskManager(
-                        ctx.getOuterContext(), ctx.mMainThread.getHandler());
+                return ActivityTaskManager.getInstance();
             }});
 
         registerService(Context.URI_GRANTS_SERVICE, UriGrantsManager.class,
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index d2be8a4..36241a8 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -92,6 +92,54 @@
                 }
             ],
             "file_patterns": ["(/|^)Activity.java"]
+        },
+        {
+            "name": "CtsContentTestCases",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.content.wm.cts"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
+        },
+        {
+            "name": "CtsOsTestCases",
+            "options": [
+                {
+                    "include-annotation": "android.platform.test.annotations.Presubmit"
+                },
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.os.cts.StrictModeTest"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
+        },
+        {
+            "name": "FrameworksCoreTests",
+            "options": [
+                {
+                    "exclude-annotation": "androidx.test.filters.FlakyTest"
+                },
+                {
+                    "exclude-annotation": "org.junit.Ignore"
+                },
+                {
+                    "include-filter": "android.content.ContextTest"
+                }
+            ],
+            "file_patterns": ["(/|^)ContextImpl.java"]
         }
     ],
     "postsubmit": [
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index ca67dba..1a8a4b7 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -214,7 +214,6 @@
      */
     public int parentTaskId;
 
-
     /**
      * Parent bounds.
      * @hide
@@ -227,6 +226,12 @@
      */
     public boolean isFocused;
 
+    /**
+     * Whether this task is visible.
+     * @hide
+     */
+    public boolean isVisible;
+
     TaskInfo() {
         // Do nothing
     }
@@ -311,7 +316,8 @@
                 && pictureInPictureParams == that.pictureInPictureParams
                 && getWindowingMode() == that.getWindowingMode()
                 && Objects.equals(taskDescription, that.taskDescription)
-                && isFocused == that.isFocused;
+                && isFocused == that.isFocused
+                && isVisible == that.isVisible;
     }
 
     private boolean equalsLetterboxParams(TaskInfo that) {
@@ -358,6 +364,7 @@
         parentTaskId = source.readInt();
         parentBounds = source.readTypedObject(Rect.CREATOR);
         isFocused = source.readBoolean();
+        isVisible = source.readBoolean();
     }
 
     /**
@@ -394,6 +401,7 @@
         dest.writeInt(parentTaskId);
         dest.writeTypedObject(parentBounds, flags);
         dest.writeBoolean(isFocused);
+        dest.writeBoolean(isVisible);
     }
 
     @Override
@@ -413,12 +421,13 @@
                 + " topActivityType=" + topActivityType
                 + " pictureInPictureParams=" + pictureInPictureParams
                 + " topActivityInfo=" + topActivityInfo
-                + " launchCookies" + launchCookies
+                + " launchCookies=" + launchCookies
                 + " letterboxActivityBounds=" + letterboxActivityBounds
                 + " positionInParent=" + positionInParent
                 + " parentTaskId=" + parentTaskId
                 + " parentBounds=" + parentBounds
                 + " isFocused=" + isFocused
+                + " isVisible=" + isVisible
                 + "}";
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 42427fa..26784f2c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3721,6 +3721,27 @@
     }
 
     /**
+     * Returns the password complexity that applies to this user, aggregated from other users if
+     * necessary (for example, if the DPC has set password complexity requirements on the parent
+     * profile DPM instance of a managed profile user, they would apply to the primary user on the
+     * device).
+     * @hide
+     */
+    @PasswordComplexity
+    public int getAggregatedPasswordComplexityForUser(int userId) {
+        if (mService == null) {
+            return PASSWORD_COMPLEXITY_NONE;
+        }
+
+        try {
+            return mService.getAggregatedPasswordComplexityForUser(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+
+    /**
      * When called by a profile owner of a managed profile returns true if the profile uses unified
      * challenge with its parent user.
      *
@@ -5349,6 +5370,27 @@
         }
     }
 
+    // STOPSHIP(b/174298501): clarify the expected return value following generateKeyPair call.
+    /**
+     * Called by a device or profile owner, or delegated certificate installer, to query whether a
+     * certificate and private key are installed under a given alias.
+     *
+     * @param alias The alias under which the key pair is installed.
+     * @return {@code true} if a key pair with this alias exists, {@code false} otherwise.
+     * @throws SecurityException if the caller is not a device or profile owner or a delegated
+     *         certificate installer.
+     * @see #setDelegatedScopes
+     * @see #DELEGATION_CERT_INSTALL
+     */
+    public boolean hasKeyPair(@NonNull String alias) {
+        throwIfParentInstance("hasKeyPair");
+        try {
+            return mService.hasKeyPair(mContext.getPackageName(), alias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Called by a device or profile owner, or delegated certificate installer, to generate a
      * new private/public key pair. If the device supports key generation via secure hardware,
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 58368bc..8be3cdc 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -89,6 +89,7 @@
     int getPasswordComplexity(boolean parent);
     void setRequiredPasswordComplexity(int passwordComplexity, boolean parent);
     int getRequiredPasswordComplexity(boolean parent);
+    int getAggregatedPasswordComplexityForUser(int userId);
     boolean isUsingUnifiedPassword(in ComponentName admin);
     int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
@@ -184,6 +185,7 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
+    boolean hasKeyPair(in String callerPackage, in String alias);
     boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
             in ParcelableKeyGenParameterSpec keySpec,
             in int idAttestationFlags, out KeymasterCertificateChain attestationChain);
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 86f91d7..1cf4567 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -85,7 +85,8 @@
             TAG_CRYPTO_SELF_TEST_COMPLETED,
             TAG_KEY_INTEGRITY_VIOLATION,
             TAG_CERT_VALIDATION_FAILURE,
-            TAG_CAMERA_POLICY_SET
+            TAG_CAMERA_POLICY_SET,
+            TAG_PASSWORD_COMPLEXITY_REQUIRED
     })
     public @interface SecurityLogTag {}
 
@@ -478,6 +479,21 @@
             SecurityLogTags.SECURITY_CAMERA_POLICY_SET;
 
     /**
+     * Indicates that an admin has set a password complexity requirement, using the platform's
+     * pre-defined complexity levels. The log entry contains the following information about the
+     * event, encapsulated in an {@link Object} array and accessible via
+     * {@link SecurityEvent#getData()}:
+     * <li> [0] admin package name ({@code String})
+     * <li> [1] admin user ID ({@code Integer})
+     * <li> [2] target user ID ({@code Integer})
+     * <li> [3] Password complexity ({@code Integer})
+     *
+     * @see DevicePolicyManager#setRequiredPasswordComplexity(int)
+     */
+    public static final int TAG_PASSWORD_COMPLEXITY_REQUIRED =
+            SecurityLogTags.SECURITY_PASSWORD_COMPLEXITY_REQUIRED;
+
+    /**
      * Event severity level indicating that the event corresponds to normal workflow.
      */
     public static final int LEVEL_INFO = 1;
@@ -617,6 +633,7 @@
                 case TAG_USER_RESTRICTION_ADDED:
                 case TAG_USER_RESTRICTION_REMOVED:
                 case TAG_CAMERA_POLICY_SET:
+                case TAG_PASSWORD_COMPLEXITY_REQUIRED:
                     return LEVEL_INFO;
                 case TAG_CERT_AUTHORITY_REMOVED:
                 case TAG_CRYPTO_SELF_TEST_COMPLETED:
diff --git a/core/java/android/app/admin/SecurityLogTags.logtags b/core/java/android/app/admin/SecurityLogTags.logtags
index 100fd4c..db5245c 100644
--- a/core/java/android/app/admin/SecurityLogTags.logtags
+++ b/core/java/android/app/admin/SecurityLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
 
 option java_package android.app.admin
 
@@ -39,3 +39,4 @@
 210032 security_key_integrity_violation         (key_id|3),(uid|1)
 210033 security_cert_validation_failure         (reason|3)
 210034 security_camera_policy_set               (package|3),(admin_user|1),(target_user|1),(disabled|1)
+210035 security_password_complexity_required    (package|3),(admin_user|1),(target_user|1),(complexity|1)
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index e35fb03..d7dc86a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -63,7 +63,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
-import android.os.storage.StorageManager;
 import android.system.Int32Ref;
 import android.text.TextUtils;
 import android.util.EventLog;
@@ -110,7 +109,7 @@
      *
      * @hide
      */
-    public static final boolean DEPRECATE_DATA_COLUMNS = StorageManager.hasIsolatedStorage();
+    public static final boolean DEPRECATE_DATA_COLUMNS = true;
 
     /**
      * Special filesystem path prefix which indicates that a path should be
diff --git a/core/java/android/content/TEST_MAPPING b/core/java/android/content/TEST_MAPPING
new file mode 100644
index 0000000..a2880df
--- /dev/null
+++ b/core/java/android/content/TEST_MAPPING
@@ -0,0 +1,52 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsContentTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.content.wm.cts"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    },
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.os.cts.StrictModeTest"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    },
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.content.ContextTest"
+        }
+      ],
+      "file_patterns": ["(/|^)Context.java", "(/|^)ContextWrapper.java"]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 3290022..ca5eeb1 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -911,12 +911,52 @@
     public interface DeviceConfig {
 
         /**
-         * Key for refresh rate in the zone defined by thresholds.
+         * Key for refresh rate in the low zone defined by thresholds.
          *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
          * @see android.R.integer#config_defaultZoneBehavior
          */
-        String KEY_REFRESH_RATE_IN_ZONE = "refresh_rate_in_zone";
+        String KEY_REFRESH_RATE_IN_LOW_ZONE = "refresh_rate_in_zone";
+
+        /**
+         * Key for accessing the low display brightness thresholds for the configured refresh
+         * rate zone.
+         * The value will be a pair of comma separated integers representing the minimum and maximum
+         * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_brightness_thresholds";
+
+        /**
+         * Key for accessing the low ambient brightness thresholds for the configured refresh
+         * rate zone. The value will be a pair of comma separated integers representing the minimum
+         * and maximum thresholds of the zone, respectively, in lux.
+         *
+         * Note that the name and value don't match because they were added before we had a high
+         * zone to consider.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @hide
+         */
+        String KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "peak_refresh_rate_ambient_thresholds";
+        /**
+         * Key for refresh rate in the high zone defined by thresholds.
+         *
+         * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+         * @see android.R.integer#config_fixedRefreshRateInHighZone
+         */
+        String KEY_REFRESH_RATE_IN_HIGH_ZONE = "refresh_rate_in_high_zone";
 
         /**
          * Key for accessing the display brightness thresholds for the configured refresh rate zone.
@@ -924,11 +964,11 @@
          * thresholds of the zone, respectively, in display backlight units (i.e. [0, 255]).
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_brightnessThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_brightnessHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_brightness_thresholds";
+        String KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_display_brightness_thresholds";
 
         /**
          * Key for accessing the ambient brightness thresholds for the configured refresh rate zone.
@@ -936,12 +976,11 @@
          * thresholds of the zone, respectively, in lux.
          *
          * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
-         * @see android.R.array#config_ambientThresholdsOfPeakRefreshRate
+         * @see android.R.array#config_ambientHighThresholdsOfFixedRefreshRate
          * @hide
          */
-        String KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS =
-                "peak_refresh_rate_ambient_thresholds";
-
+        String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
+                "fixed_refresh_rate_high_ambient_brightness_thresholds";
         /**
          * Key for default peak refresh rate
          *
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index d31218d..a17a498 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -51,13 +51,6 @@
     public static final int ID_NONE = -1;
 
     /**
-     * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any
-     * provider, so they use this constant for clarity instead of NONE.
-     * @hide only used by ConnectivityService.
-     */
-    public static final int ID_VPN = -2;
-
-    /**
      * The first providerId value that will be allocated.
      * @hide only used by ConnectivityService.
      */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index 0f2a9f2..f76eb86 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -1138,8 +1138,7 @@
          *
          * @param primitiveId The primitive to add
          * @param scale The scale to apply to the intensity of the primitive.
-         * @param delay The amount of time, in milliseconds, to wait between playing the prior
-         *              primitive and this one
+         * @param delay The amount of time in milliseconds to wait before playing this primitive
          * @return The {@link Composition} object to enable adding multiple primitives in one chain.
          */
         @NonNull
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index cfc3e01..870d224 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -156,10 +156,6 @@
     /** {@hide} */
     public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
     /** {@hide} */
-    public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage";
-    /** {@hide} */
-    public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot";
-    /** {@hide} */
     public static final String PROP_FORCED_SCOPED_STORAGE_WHITELIST =
             "forced_scoped_storage_whitelist";
 
@@ -263,10 +259,6 @@
     public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 4;
     /** {@hide} */
     public static final int DEBUG_VIRTUAL_DISK = 1 << 5;
-    /** {@hide} */
-    public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6;
-    /** {@hide} */
-    public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7;
 
     /** {@hide} */
     public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE;
@@ -1695,16 +1687,13 @@
 
     /**
      * Return if the currently booted device has the "isolated storage" feature
-     * flag enabled. This will eventually be fully enabled in the final
-     * {@link android.os.Build.VERSION_CODES#Q} release.
+     * flag enabled.
      *
      * @hide
      */
     @SystemApi
     public static boolean hasIsolatedStorage() {
-        // Prefer to use snapshot for current boot when available
-        return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT,
-                SystemProperties.getBoolean(PROP_ISOLATED_STORAGE, true));
+        return false;
     }
 
     /**
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 4379ce1..55ba15a 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -28,50 +28,8 @@
  * @hide Only for use within the system server.
  */
 public abstract class StorageManagerInternal {
-
     /**
-     * Policy that influences how external storage is mounted and reported.
-     */
-    public interface ExternalStorageMountPolicy {
-        /**
-         * Gets the external storage mount mode for the given uid.
-         *
-         * @param uid The UID for which to determine mount mode.
-         * @param packageName The package in the UID for making the call.
-         * @return The mount mode.
-         *
-         * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_NONE
-         * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_DEFAULT
-         * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_READ
-         * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_WRITE
-         */
-        public int getMountMode(int uid, String packageName);
-
-        /**
-         * Gets whether external storage should be reported to the given UID.
-         *
-         * @param uid The UID for which to determine whether it has external storage.
-         * @param packageName The package in the UID for making the call.
-         * @return Weather to report external storage.
-         * @return True to report the state of external storage, false to
-         *     report it as unmounted.
-         */
-        public boolean hasExternalStorage(int uid, String packageName);
-    }
-
-    /**
-     * Adds a policy for determining how external storage is mounted and reported.
-     * The mount mode is the most conservative result from querying all registered
-     * policies. Similarly, the reported state is the most conservative result from
-     * querying all registered policies.
-     *
-     * @param policy The policy to add.
-     */
-    public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy);
-
-    /**
-     * Gets the mount mode to use for a given UID as determined by consultin all
-     * policies.
+     * Gets the mount mode to use for a given UID
      *
      * @param uid The UID for which to get mount mode.
      * @param packageName The package in the UID for making the call.
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 901494b..237a9f2 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -200,6 +200,21 @@
         internalPath = parcel.readString8();
     }
 
+    public VolumeInfo(VolumeInfo volumeInfo) {
+        this.id = volumeInfo.id;
+        this.type = volumeInfo.type;
+        this.disk = volumeInfo.disk;
+        this.partGuid = volumeInfo.partGuid;
+        this.mountFlags = volumeInfo.mountFlags;
+        this.mountUserId = volumeInfo.mountUserId;
+        this.state = volumeInfo.state;
+        this.fsType = volumeInfo.fsType;
+        this.fsUuid = volumeInfo.fsUuid;
+        this.fsLabel = volumeInfo.fsLabel;
+        this.path = volumeInfo.path;
+        this.internalPath = volumeInfo.internalPath;
+    }
+
     @UnsupportedAppUsage
     public static @NonNull String getEnvironmentForState(int state) {
         final String envState = sStateToEnvironment.get(state);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 714bcea..44cc0f5 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -451,6 +451,22 @@
             "connectivity_thermal_power_manager";
 
     /**
+     * Namespace for all statsd native features that can be applied immediately.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
+
+    /**
+     * Namespace for all statsd native features that are applied on boot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_STATSD_NATIVE_BOOT = "statsd_native_boot";
+
+    /**
      * Namespace for configuration related features.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 884f8cc..249a781 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13286,16 +13286,6 @@
                 "storage_settings_clobber_threshold";
 
         /**
-         * If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link Secure#LOCATION_MODE_OFF}
-         * temporarily for all users.
-         *
-         * @hide
-         */
-        @TestApi
-        public static final String LOCATION_GLOBAL_KILL_SWITCH =
-                "location_global_kill_switch";
-
-        /**
          * If set to 1, SettingsProvider's restoreAnyVersion="true" attribute will be ignored
          * and restoring to lower version of platform API will be skipped.
          *
@@ -13415,11 +13405,6 @@
         public static final String MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY =
                 "max_sound_trigger_detection_service_ops_per_day";
 
-        /** {@hide} */
-        public static final String ISOLATED_STORAGE_LOCAL = "isolated_storage_local";
-        /** {@hide} */
-        public static final String ISOLATED_STORAGE_REMOTE = "isolated_storage_remote";
-
         /**
          * Indicates whether aware is available in the current location.
          * @hide
diff --git a/core/java/android/view/ContentInfo.java b/core/java/android/view/ContentInfo.java
new file mode 100644
index 0000000..b58937b
--- /dev/null
+++ b/core/java/android/view/ContentInfo.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2020 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.view;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.ArrayMap;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Predicate;
+
+/**
+ * Holds all the relevant data for a request to {@link View#performReceiveContent}.
+ */
+public final class ContentInfo {
+
+    /**
+     * Specifies the UI through which content is being inserted. Future versions of Android may
+     * support additional values.
+     *
+     * @hide
+     */
+    @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
+            SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Source {}
+
+    /**
+     * Specifies that the operation was triggered by the app that contains the target view.
+     */
+    public static final int SOURCE_APP = 0;
+
+    /**
+     * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
+     * "Paste as plain text" action in the insertion/selection menu).
+     */
+    public static final int SOURCE_CLIPBOARD = 1;
+
+    /**
+     * Specifies that the operation was triggered from the soft keyboard (also known as input
+     * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
+     * for more info.
+     */
+    public static final int SOURCE_INPUT_METHOD = 2;
+
+    /**
+     * Specifies that the operation was triggered by the drag/drop framework. See
+     * https://developer.android.com/guide/topics/ui/drag-drop for more info.
+     */
+    public static final int SOURCE_DRAG_AND_DROP = 3;
+
+    /**
+     * Specifies that the operation was triggered by the autofill framework. See
+     * https://developer.android.com/guide/topics/text/autofill for more info.
+     */
+    public static final int SOURCE_AUTOFILL = 4;
+
+    /**
+     * Specifies that the operation was triggered by a result from a
+     * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
+     * menu.
+     */
+    public static final int SOURCE_PROCESS_TEXT = 5;
+
+    /**
+     * Returns the symbolic name of the given source.
+     *
+     * @hide
+     */
+    static String sourceToString(@Source int source) {
+        switch (source) {
+            case SOURCE_APP: return "SOURCE_APP";
+            case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
+            case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
+            case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
+            case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
+            case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
+        }
+        return String.valueOf(source);
+    }
+
+    /**
+     * Flags to configure the insertion behavior.
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Flags {}
+
+    /**
+     * Flag requesting that the content should be converted to plain text prior to inserting.
+     */
+    public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
+
+    /**
+     * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
+     *
+     * @hide
+     */
+    static String flagsToString(@Flags int flags) {
+        if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
+            return "FLAG_CONVERT_TO_PLAIN_TEXT";
+        }
+        return String.valueOf(flags);
+    }
+
+    @NonNull
+    private final ClipData mClip;
+    @Source
+    private final int mSource;
+    @Flags
+    private final int mFlags;
+    @Nullable
+    private final Uri mLinkUri;
+    @Nullable
+    private final Bundle mExtras;
+
+    private ContentInfo(Builder b) {
+        this.mClip = Objects.requireNonNull(b.mClip);
+        this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
+                "source");
+        this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
+        this.mLinkUri = b.mLinkUri;
+        this.mExtras = b.mExtras;
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "ContentInfo{"
+                + "clip=" + mClip
+                + ", source=" + sourceToString(mSource)
+                + ", flags=" + flagsToString(mFlags)
+                + ", linkUri=" + mLinkUri
+                + ", extras=" + mExtras
+                + "}";
+    }
+
+    /**
+     * The data to be inserted.
+     */
+    @NonNull
+    public ClipData getClip() {
+        return mClip;
+    }
+
+    /**
+     * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
+     * may pass additional values.
+     */
+    @Source
+    public int getSource() {
+        return mSource;
+    }
+
+    /**
+     * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
+     */
+    @Flags
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Optional http/https URI for the content that may be provided by the IME. This is only
+     * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
+     * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
+     * IME.
+     */
+    @Nullable
+    public Uri getLinkUri() {
+        return mLinkUri;
+    }
+
+    /**
+     * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
+     * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
+     * the IME.
+     */
+    @Nullable
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    /**
+     * Partitions the content based on the given predicate.
+     *
+     * <p>Similar to a
+     * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector},
+     * this function classifies the content and organizes it into a map, grouping the items that
+     * matched vs didn't match the predicate.
+     *
+     * <p>Except for the {@link ClipData} items, the returned objects will contain all the same
+     * metadata as the original.
+     *
+     * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which
+     * partition to place it into.
+     * @return A map containing the partitioned content. The map will contain a single entry if
+     * all items were classified into the same partition (all matched or all didn't match the
+     * predicate) or two entries (if there's at least one item that matched the predicate and at
+     * least one item that didn't match the predicate).
+     */
+    @NonNull
+    public Map<Boolean, ContentInfo> partition(@NonNull Predicate<ClipData.Item> itemPredicate) {
+        if (mClip.getItemCount() == 1) {
+            Map<Boolean, ContentInfo> result = new ArrayMap<>(1);
+            result.put(itemPredicate.test(mClip.getItemAt(0)), this);
+            return result;
+        }
+        ArrayList<ClipData.Item> accepted = new ArrayList<>();
+        ArrayList<ClipData.Item> remaining = new ArrayList<>();
+        for (int i = 0; i < mClip.getItemCount(); i++) {
+            ClipData.Item item = mClip.getItemAt(i);
+            if (itemPredicate.test(item)) {
+                accepted.add(item);
+            } else {
+                remaining.add(item);
+            }
+        }
+        Map<Boolean, ContentInfo> result = new ArrayMap<>(2);
+        if (!accepted.isEmpty()) {
+            ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted);
+            result.put(true, new Builder(this).setClip(acceptedClip).build());
+        }
+        if (!remaining.isEmpty()) {
+            ClipData remainingClip = new ClipData(mClip.getDescription(), remaining);
+            result.put(false, new Builder(this).setClip(remainingClip).build());
+        }
+        return result;
+    }
+
+    /**
+     * Builder for {@link ContentInfo}.
+     */
+    public static final class Builder {
+        @NonNull
+        private ClipData mClip;
+        @Source
+        private int mSource;
+        @Flags
+        private  int mFlags;
+        @Nullable
+        private Uri mLinkUri;
+        @Nullable
+        private Bundle mExtras;
+
+        /**
+         * Creates a new builder initialized with the data from the given builder.
+         */
+        public Builder(@NonNull ContentInfo other) {
+            mClip = other.mClip;
+            mSource = other.mSource;
+            mFlags = other.mFlags;
+            mLinkUri = other.mLinkUri;
+            mExtras = other.mExtras;
+        }
+
+        /**
+         * Creates a new builder.
+         * @param clip   The data to insert.
+         * @param source The source of the operation. See {@code SOURCE_} constants.
+         */
+        public Builder(@NonNull ClipData clip, @Source int source) {
+            mClip = clip;
+            mSource = source;
+        }
+
+        /**
+         * Sets the data to be inserted.
+         * @param clip The data to insert.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setClip(@NonNull ClipData clip) {
+            mClip = clip;
+            return this;
+        }
+
+        /**
+         * Sets the source of the operation.
+         * @param source The source of the operation. See {@code SOURCE_} constants.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setSource(@Source int source) {
+            mSource = source;
+            return this;
+        }
+
+        /**
+         * Sets flags that control content insertion behavior.
+         * @param flags Optional flags to configure the insertion behavior. Use 0 for default
+         *              behavior. See {@code FLAG_} constants.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setFlags(@Flags int flags) {
+            mFlags = flags;
+            return this;
+        }
+
+        /**
+         * Sets the http/https URI for the content. See
+         * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
+         * @param linkUri Optional http/https URI for the content.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setLinkUri(@Nullable Uri linkUri) {
+            mLinkUri = linkUri;
+            return this;
+        }
+
+        /**
+         * Sets additional metadata.
+         * @param extras Optional bundle with additional metadata.
+         * @return this builder
+         */
+        @NonNull
+        public Builder setExtras(@Nullable Bundle extras) {
+            mExtras = extras;
+            return this;
+        }
+
+        /**
+         * @return A new {@link ContentInfo} instance with the data from this builder.
+         */
+        @NonNull
+        public ContentInfo build() {
+            return new ContentInfo(this);
+        }
+    }
+}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index defa58e..c8bfd36 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -15,8 +15,13 @@
  */
 
 package android.view;
+
+import android.annotation.IntDef;
 import android.graphics.Rect;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Standard constants and tools for placing an object within a potentially
  * larger container.
@@ -122,6 +127,32 @@
      */
     public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
 
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+        Gravity.FILL,
+        Gravity.FILL_HORIZONTAL,
+        Gravity.FILL_VERTICAL,
+        Gravity.START,
+        Gravity.END,
+        Gravity.LEFT,
+        Gravity.RIGHT,
+        Gravity.TOP,
+        Gravity.BOTTOM,
+        Gravity.CENTER,
+        Gravity.CENTER_HORIZONTAL,
+        Gravity.CENTER_VERTICAL,
+        Gravity.DISPLAY_CLIP_HORIZONTAL,
+        Gravity.DISPLAY_CLIP_VERTICAL,
+        Gravity.CLIP_HORIZONTAL,
+        Gravity.CLIP_VERTICAL,
+        Gravity.NO_GRAVITY
+    })
+    public @interface GravityFlags {}
+
     /**
      * Apply a gravity constant to an object. This supposes that the layout direction is LTR.
      * 
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 1a6eea5..a23b7e1 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,6 +32,7 @@
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
+import android.service.attestation.ImpressionToken;
 import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -760,4 +761,23 @@
      * {@link android.content.pm.PackageManager#getHoldLockToken()}.
      */
     void holdLock(in IBinder token, in int durationMs);
+
+    /**
+     * Gets an array of support hashing algorithms that can be used to generate the hash of the
+     * screenshot. The String value of one algorithm should be used when requesting to generate
+     * the impression attestation token.
+     *
+     * @return a String array of supported hashing algorithms.
+     */
+    String[] getSupportedImpressionAlgorithms();
+
+    /**
+     * Validate the impression token was generated by the system. The impression token passed in
+     * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+     *
+     * @param impressionToken The token to verify that it was generated by the system.
+     * @return true if the token was generated by the system or false if the token cannot be
+     *         verified.
+     */
+    boolean verifyImpressionToken(in ImpressionToken impressionToken);
 }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 0089a85..cfdaf8c 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,6 +22,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
+import android.service.attestation.ImpressionToken;
 import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.InputChannel;
@@ -344,4 +345,16 @@
      *                     window, the system will try to find a new focus target.
      */
     void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
+
+    /**
+     * Generates an impression token that can be used to validate whether specific content was on
+     * screen.
+     *
+     * @param window The token for the window where the view to attest is shown.
+     * @param boundsInWindow The size and position of the ads view in the window
+     * @param hashAlgorithm The String for the hashing algorithm to use based on values returned
+     *                      from {@link IWindowManager#getSupportedImpressionAlgorithms()}
+     */
+    ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+            in String hashAlgorithm);
 }
diff --git a/core/java/android/view/OnReceiveContentListener.java b/core/java/android/view/OnReceiveContentListener.java
index db9c538..b551fa8 100644
--- a/core/java/android/view/OnReceiveContentListener.java
+++ b/core/java/android/view/OnReceiveContentListener.java
@@ -16,22 +16,8 @@
 
 package android.view;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ClipData;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.ArrayMap;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Predicate;
 
 /**
  * Listener for apps to implement handling for insertion of content. Content may be both text and
@@ -48,10 +34,13 @@
  *     public static final String[] MIME_TYPES = new String[] {"image/*", "video/*"};
  *
  *     &#64;Override
- *     public Payload onReceiveContent(View view, Payload payload) {
- *         Map&lt;Boolean, Payload&gt; split = payload.partition(item -&gt; item.getUri() != null);
- *         if (split.get(true) != null) {
- *             ClipData clip = payload.getClip();
+ *     public ContentInfo onReceiveContent(View view, ContentInfo payload) {
+ *         Map&lt;Boolean, ContentInfo&gt; split =
+ *                 payload.partition(item -&gt; item.getUri() != null);
+ *         ContentInfo uriItems = split.get(true);
+ *         ContentInfo remainingItems = split.get(false);
+ *         if (uriItems != null) {
+ *             ClipData clip = uriItems.getClip();
  *             for (int i = 0; i < clip.getItemCount(); i++) {
  *                 Uri uri = clip.getItemAt(i).getUri();
  *                 // ... app-specific logic to handle the URI ...
@@ -59,7 +48,7 @@
  *         }
  *         // Return anything that we didn't handle ourselves. This preserves the default platform
  *         // behavior for text and anything else for which we are not implementing custom handling.
- *         return split.get(false);
+ *         return remainingItems;
  *     }
  * }
  *
@@ -83,8 +72,8 @@
      * handling. For example, an implementation may provide handling for content URIs (to provide
      * support for inserting images, etc) and delegate the processing of text to the platform to
      * preserve the common behavior for inserting text. See the class javadoc for a sample
-     * implementation and see {@link Payload#partition} for a convenient way to split the passed-in
-     * content.
+     * implementation and see {@link ContentInfo#partition} for a convenient way to split the
+     * passed-in content.
      *
      * <p>If implementing handling for text: if the view has a selection, the selection should
      * be overwritten by the passed-in content; if there's no selection, the passed-in content
@@ -103,314 +92,6 @@
      * succeed even if this method returns null. For example, an app may end up not inserting
      * an item if it exceeds the app's size limit for that type of content.
      */
-    @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload);
-
-    /**
-     * Holds all the relevant data for a request to {@link OnReceiveContentListener}.
-     */
-    final class Payload {
-
-        /**
-         * Specifies the UI through which content is being inserted. Future versions of Android may
-         * support additional values.
-         *
-         * @hide
-         */
-        @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD,
-                SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Source {}
-
-        /**
-         * Specifies that the operation was triggered by the app that contains the target view.
-         */
-        public static final int SOURCE_APP = 0;
-
-        /**
-         * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or
-         * "Paste as plain text" action in the insertion/selection menu).
-         */
-        public static final int SOURCE_CLIPBOARD = 1;
-
-        /**
-         * Specifies that the operation was triggered from the soft keyboard (also known as input
-         * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard
-         * for more info.
-         */
-        public static final int SOURCE_INPUT_METHOD = 2;
-
-        /**
-         * Specifies that the operation was triggered by the drag/drop framework. See
-         * https://developer.android.com/guide/topics/ui/drag-drop for more info.
-         */
-        public static final int SOURCE_DRAG_AND_DROP = 3;
-
-        /**
-         * Specifies that the operation was triggered by the autofill framework. See
-         * https://developer.android.com/guide/topics/text/autofill for more info.
-         */
-        public static final int SOURCE_AUTOFILL = 4;
-
-        /**
-         * Specifies that the operation was triggered by a result from a
-         * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection
-         * menu.
-         */
-        public static final int SOURCE_PROCESS_TEXT = 5;
-
-        /**
-         * Returns the symbolic name of the given source.
-         *
-         * @hide
-         */
-        static String sourceToString(@Source int source) {
-            switch (source) {
-                case SOURCE_APP: return "SOURCE_APP";
-                case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD";
-                case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD";
-                case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP";
-                case SOURCE_AUTOFILL: return "SOURCE_AUTOFILL";
-                case SOURCE_PROCESS_TEXT: return "SOURCE_PROCESS_TEXT";
-            }
-            return String.valueOf(source);
-        }
-
-        /**
-         * Flags to configure the insertion behavior.
-         *
-         * @hide
-         */
-        @IntDef(flag = true, prefix = {"FLAG_"}, value = {FLAG_CONVERT_TO_PLAIN_TEXT})
-        @Retention(RetentionPolicy.SOURCE)
-        public @interface Flags {}
-
-        /**
-         * Flag requesting that the content should be converted to plain text prior to inserting.
-         */
-        public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1 << 0;
-
-        /**
-         * Returns the symbolic names of the set flags or {@code "0"} if no flags are set.
-         *
-         * @hide
-         */
-        static String flagsToString(@Flags int flags) {
-            if ((flags & FLAG_CONVERT_TO_PLAIN_TEXT) != 0) {
-                return "FLAG_CONVERT_TO_PLAIN_TEXT";
-            }
-            return String.valueOf(flags);
-        }
-
-        @NonNull private final ClipData mClip;
-        private final @Source int mSource;
-        private final @Flags int mFlags;
-        @Nullable private final Uri mLinkUri;
-        @Nullable private final Bundle mExtras;
-
-        private Payload(Builder b) {
-            this.mClip = Objects.requireNonNull(b.mClip);
-            this.mSource = Preconditions.checkArgumentInRange(b.mSource, 0, SOURCE_PROCESS_TEXT,
-                    "source");
-            this.mFlags = Preconditions.checkFlagsArgument(b.mFlags, FLAG_CONVERT_TO_PLAIN_TEXT);
-            this.mLinkUri = b.mLinkUri;
-            this.mExtras = b.mExtras;
-        }
-
-        @NonNull
-        @Override
-        public String toString() {
-            return "Payload{"
-                    + "clip=" + mClip
-                    + ", source=" + sourceToString(mSource)
-                    + ", flags=" + flagsToString(mFlags)
-                    + ", linkUri=" + mLinkUri
-                    + ", extras=" + mExtras
-                    + "}";
-        }
-
-        /**
-         * The data to be inserted.
-         */
-        public @NonNull ClipData getClip() {
-            return mClip;
-        }
-
-        /**
-         * The source of the operation. See {@code SOURCE_} constants. Future versions of Android
-         * may pass additional values.
-         */
-        public @Source int getSource() {
-            return mSource;
-        }
-
-        /**
-         * Optional flags that control the insertion behavior. See {@code FLAG_} constants.
-         */
-        public @Flags int getFlags() {
-            return mFlags;
-        }
-
-        /**
-         * Optional http/https URI for the content that may be provided by the IME. This is only
-         * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty
-         * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the
-         * IME.
-         */
-        public @Nullable Uri getLinkUri() {
-            return mLinkUri;
-        }
-
-        /**
-         * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will
-         * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by
-         * the IME.
-         */
-        public @Nullable Bundle getExtras() {
-            return mExtras;
-        }
-
-        /**
-         * Partitions this payload based on the given predicate.
-         *
-         * <p>Similar to a
-         * {@link java.util.stream.Collectors#partitioningBy(Predicate) partitioning collector},
-         * this function classifies the content in this payload and organizes it into a map,
-         * grouping the content that matched vs didn't match the predicate.
-         *
-         * <p>Except for the {@link ClipData} items, the returned payloads will contain all the same
-         * metadata as the original payload.
-         *
-         * @param itemPredicate The predicate to test each {@link ClipData.Item} to determine which
-         * partition to place it into.
-         * @return A map containing the partitioned content. The map will contain a single entry if
-         * all items were classified into the same partition (all matched or all didn't match the
-         * predicate) or two entries (if there's at least one item that matched the predicate and at
-         * least one item that didn't match the predicate).
-         */
-        public @NonNull Map<Boolean, Payload> partition(
-                @NonNull Predicate<ClipData.Item> itemPredicate) {
-            if (mClip.getItemCount() == 1) {
-                Map<Boolean, Payload> result = new ArrayMap<>(1);
-                result.put(itemPredicate.test(mClip.getItemAt(0)), this);
-                return result;
-            }
-            ArrayList<ClipData.Item> accepted = new ArrayList<>();
-            ArrayList<ClipData.Item> remaining = new ArrayList<>();
-            for (int i = 0; i < mClip.getItemCount(); i++) {
-                ClipData.Item item = mClip.getItemAt(i);
-                if (itemPredicate.test(item)) {
-                    accepted.add(item);
-                } else {
-                    remaining.add(item);
-                }
-            }
-            Map<Boolean, Payload> result = new ArrayMap<>(2);
-            if (!accepted.isEmpty()) {
-                ClipData acceptedClip = new ClipData(mClip.getDescription(), accepted);
-                result.put(true, new Builder(this).setClip(acceptedClip).build());
-            }
-            if (!remaining.isEmpty()) {
-                ClipData remainingClip = new ClipData(mClip.getDescription(), remaining);
-                result.put(false, new Builder(this).setClip(remainingClip).build());
-            }
-            return result;
-        }
-
-        /**
-         * Builder for {@link Payload}.
-         */
-        public static final class Builder {
-            @NonNull private ClipData mClip;
-            private @Source int mSource;
-            private @Flags int mFlags;
-            @Nullable private Uri mLinkUri;
-            @Nullable private Bundle mExtras;
-
-            /**
-             * Creates a new builder initialized with the data from the given builder.
-             */
-            public Builder(@NonNull Payload payload) {
-                mClip = payload.mClip;
-                mSource = payload.mSource;
-                mFlags = payload.mFlags;
-                mLinkUri = payload.mLinkUri;
-                mExtras = payload.mExtras;
-            }
-
-            /**
-             * Creates a new builder.
-             * @param clip   The data to insert.
-             * @param source The source of the operation. See {@code SOURCE_} constants.
-             */
-            public Builder(@NonNull ClipData clip, @Source int source) {
-                mClip = clip;
-                mSource = source;
-            }
-
-            /**
-             * Sets the data to be inserted.
-             * @param clip The data to insert.
-             * @return this builder
-             */
-            @NonNull
-            public Builder setClip(@NonNull ClipData clip) {
-                mClip = clip;
-                return this;
-            }
-
-            /**
-             * Sets the source of the operation.
-             * @param source The source of the operation. See {@code SOURCE_} constants.
-             * @return this builder
-             */
-            @NonNull
-            public Builder setSource(@Source int source) {
-                mSource = source;
-                return this;
-            }
-
-            /**
-             * Sets flags that control content insertion behavior.
-             * @param flags Optional flags to configure the insertion behavior. Use 0 for default
-             *              behavior. See {@code FLAG_} constants.
-             * @return this builder
-             */
-            @NonNull
-            public Builder setFlags(@Flags int flags) {
-                mFlags = flags;
-                return this;
-            }
-
-            /**
-             * Sets the http/https URI for the content. See
-             * {@link android.view.inputmethod.InputContentInfo#getLinkUri} for more info.
-             * @param linkUri Optional http/https URI for the content.
-             * @return this builder
-             */
-            @NonNull
-            public Builder setLinkUri(@Nullable Uri linkUri) {
-                mLinkUri = linkUri;
-                return this;
-            }
-
-            /**
-             * Sets additional metadata.
-             * @param extras Optional bundle with additional metadata.
-             * @return this builder
-             */
-            @NonNull
-            public Builder setExtras(@Nullable Bundle extras) {
-                mExtras = extras;
-                return this;
-            }
-
-            /**
-             * @return A new {@link Payload} instance with the data from this builder.
-             */
-            @NonNull
-            public Payload build() {
-                return new Payload(this);
-            }
-        }
-    }
+    @Nullable
+    ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload);
 }
diff --git a/core/java/android/view/TEST_MAPPING b/core/java/android/view/TEST_MAPPING
index 4ea4310..c8b746f 100644
--- a/core/java/android/view/TEST_MAPPING
+++ b/core/java/android/view/TEST_MAPPING
@@ -2,6 +2,24 @@
   "presubmit": [
     {
       "name": "CtsAccelerationTestCases"
+    },
+    {
+      "name": "CtsOsTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+          "include-filter": "android.os.cts.StrictModeTest"
+        }
+      ],
+      "file_patterns": ["(/|^)ViewConfiguration.java", "(/|^)GestureDetector.java"]
     }
   ],
   "imports": [
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 30ec2b0..a5c6653 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -112,7 +112,6 @@
 import android.view.AccessibilityIterators.WordTextSegmentIterator;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.InputDevice.InputSourceClass;
-import android.view.OnReceiveContentListener.Payload;
 import android.view.Window.OnContentApplyWindowInsetsListener;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsetsAnimation.Bounds;
@@ -9063,11 +9062,12 @@
      * @return The portion of the passed-in content that was not accepted (may be all, some, or none
      * of the passed-in content).
      */
-    public @Nullable Payload performReceiveContent(@NonNull Payload payload) {
+    @Nullable
+    public ContentInfo performReceiveContent(@NonNull ContentInfo payload) {
         final OnReceiveContentListener listener = (mListenerInfo == null) ? null
                 : getListenerInfo().mOnReceiveContentListener;
         if (listener != null) {
-            final Payload remaining = listener.onReceiveContent(this, payload);
+            final ContentInfo remaining = listener.onReceiveContent(this, payload);
             return (remaining == null) ? null : onReceiveContent(remaining);
         }
         return onReceiveContent(payload);
@@ -9088,7 +9088,8 @@
      * @return The portion of the passed-in content that was not handled (may be all, some, or none
      * of the passed-in content).
      */
-    public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+    @Nullable
+    public ContentInfo onReceiveContent(@NonNull ContentInfo payload) {
         return payload;
     }
 
@@ -9113,7 +9114,8 @@
      * @return The MIME types accepted by {@link #performReceiveContent} for this view (may
      * include patterns such as "image/*").
      */
-    public @Nullable String[] getOnReceiveContentMimeTypes() {
+    @Nullable
+    public String[] getOnReceiveContentMimeTypes() {
         return mOnReceiveContentMimeTypes;
     }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 8c8ea00..b9afbc9 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -104,6 +104,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.Gravity.GravityFlags;
 import android.view.View.OnApplyWindowInsetsListener;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Side.InsetsSide;
@@ -2157,15 +2158,6 @@
         public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
 
         /**
-         * Flag to indicate that this window should be considered a screen decoration similar to the
-         * nav bar and status bar. This will cause this window to affect the window insets reported
-         * to other windows when it is visible.
-         * @hide
-         */
-        @RequiresPermission(permission.STATUS_BAR_SERVICE)
-        public static final int PRIVATE_FLAG_IS_SCREEN_DECOR = 0x00400000;
-
-        /**
          * Flag to indicate that the status bar window is in a state such that it forces showing
          * the navigation bar unless the navigation bar window is explicitly set to
          * {@link View#GONE}.
@@ -2270,7 +2262,6 @@
                 PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE,
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
                 PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
-                PRIVATE_FLAG_IS_SCREEN_DECOR,
                 PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
                 PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC,
                 PRIVATE_FLAG_USE_BLAST,
@@ -2358,10 +2349,6 @@
                         equals = PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY,
                         name = "IS_ROUNDED_CORNERS_OVERLAY"),
                 @ViewDebug.FlagToString(
-                        mask = PRIVATE_FLAG_IS_SCREEN_DECOR,
-                        equals = PRIVATE_FLAG_IS_SCREEN_DECOR,
-                        name = "IS_SCREEN_DECOR"),
-                @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
                         equals = PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION,
                         name = "STATUS_FORCE_SHOW_NAVIGATION"),
@@ -2586,6 +2573,7 @@
          *
          * @see Gravity
          */
+        @GravityFlags
         public int gravity;
 
         /**
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0ed7ca7..673073e 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,6 +24,7 @@
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.service.attestation.ImpressionToken;
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.window.ClientWindowFrames;
@@ -460,4 +461,10 @@
     public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
                                          boolean grantFocus) {
     }
+
+    @Override
+    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+            String hashAlgorithm) {
+        return null;
+    }
 }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 8fdcac7..364ae818 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -19,7 +19,7 @@
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
 import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE;
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
+import static android.view.ContentInfo.SOURCE_AUTOFILL;
 import static android.view.autofill.Helper.sDebug;
 import static android.view.autofill.Helper.sVerbose;
 import static android.view.autofill.Helper.toList;
@@ -61,8 +61,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Choreographer;
+import android.view.ContentInfo;
 import android.view.KeyEvent;
-import android.view.OnReceiveContentListener.Payload;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -2371,8 +2371,8 @@
                 reportAutofillContentFailure(id);
                 return;
             }
-            Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
-            Payload result = view.performReceiveContent(payload);
+            ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
+            ContentInfo result = view.performReceiveContent(payload);
             if (result != null) {
                 Log.w(TAG, "autofillContent(): receiver could not insert content: id=" + id
                         + ", view=" + view + ", clip=" + clip);
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 1ab9edf..f057c12 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -16,7 +16,7 @@
 
 package android.view.inputmethod;
 
-import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
+import static android.view.ContentInfo.SOURCE_INPUT_METHOD;
 
 import android.annotation.CallSuper;
 import android.annotation.IntRange;
@@ -38,9 +38,9 @@
 import android.text.method.MetaKeyKeyListener;
 import android.util.Log;
 import android.util.LogPrinter;
+import android.view.ContentInfo;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
-import android.view.OnReceiveContentListener;
 import android.view.View;
 
 import com.android.internal.util.Preconditions;
@@ -952,8 +952,8 @@
         }
         final ClipData clip = new ClipData(inputContentInfo.getDescription(),
                 new ClipData.Item(inputContentInfo.getContentUri()));
-        final OnReceiveContentListener.Payload payload =
-                new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD)
+        final ContentInfo payload =
+                new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD)
                 .setLinkUri(inputContentInfo.getLinkUri())
                 .setExtras(opts)
                 .build();
diff --git a/core/java/android/view/inputmethod/DumpableInputConnection.java b/core/java/android/view/inputmethod/DumpableInputConnection.java
new file mode 100644
index 0000000..9819a57
--- /dev/null
+++ b/core/java/android/view/inputmethod/DumpableInputConnection.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 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.view.inputmethod;
+
+import android.annotation.NonNull;
+import android.util.proto.ProtoOutputStream;
+
+/** @hide */
+public interface DumpableInputConnection {
+
+    /**
+     * Method used to dump state of InputConnection implementations of interest.
+     *
+     * @param proto Stream to write the state to
+     * @param fieldId FieldId of DumpableInputConnection as defined in the parent message
+     */
+    void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId);
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 315d446..8d2c2d9 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -22,6 +22,7 @@
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.DISPLAY_ID;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.EDITOR_INFO;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_INSETS_SOURCE_CONSUMER;
+import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_CONNECTION;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INPUT_METHOD_MANAGER;
 import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.VIEW_ROOT_IMPL;
 import static android.view.inputmethod.InputMethodManagerProto.ACTIVE;
@@ -87,8 +88,10 @@
 import android.view.autofill.AutofillManager;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.Completable;
 import com.android.internal.inputmethod.InputMethodDebug;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
+import com.android.internal.inputmethod.ResultCallbacks;
 import com.android.internal.inputmethod.StartInputFlags;
 import com.android.internal.inputmethod.StartInputReason;
 import com.android.internal.inputmethod.UnbindReason;
@@ -665,6 +668,7 @@
                     final int startInputReason =
                             nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION
                                     : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION;
+                    final Completable.InputBindResult value = Completable.createInputBindResult();
                     mService.startInputOrWindowGainedFocus(
                             startInputReason, mClient,
                             focusedView.getWindowToken(), startInputFlags, softInputMode,
@@ -672,7 +676,9 @@
                             null,
                             null,
                             0 /* missingMethodFlags */,
-                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion);
+                            mCurRootView.mContext.getApplicationInfo().targetSdkVersion,
+                            ResultCallbacks.of(value));
+                    Completable.getResult(value); // ignore the result
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -998,9 +1004,9 @@
         private final InputMethodManager mParentInputMethodManager;
         private final WeakReference<View> mServedView;
 
-        ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn,
+        ControlledInputConnectionWrapper(Looper icLooper, InputConnection conn,
                 InputMethodManager inputMethodManager, View servedView) {
-            super(mainLooper, conn);
+            super(icLooper, conn);
             mParentInputMethodManager = inputMethodManager;
             mServedView = new WeakReference<>(servedView);
         }
@@ -1046,6 +1052,18 @@
                     + " mServedView=" + mServedView.get()
                     + "}";
         }
+
+        void dumpDebug(ProtoOutputStream proto, long fieldId) {
+            // Check that the call is initiated in the main thread of the current InputConnection
+            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+            // executed on this thread. Otherwise the messages are dispatched to the correct thread
+            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
+            // reasons.
+            if (getInputConnection() instanceof DumpableInputConnection && Looper.myLooper()
+                    == getLooper()) {
+                ((DumpableInputConnection) getInputConnection()).dumpDebug(proto, fieldId);
+            }
+        }
     }
 
     private static class ImeThreadFactory implements ThreadFactory {
@@ -2026,10 +2044,13 @@
                 if (DEBUG) Log.v(TAG, "START INPUT: view=" + dumpViewInfo(view) + " ic="
                         + ic + " tba=" + tba + " startInputFlags="
                         + InputMethodDebug.startInputFlagsToString(startInputFlags));
-                res = mService.startInputOrWindowGainedFocus(
+                final Completable.InputBindResult value = Completable.createInputBindResult();
+                mService.startInputOrWindowGainedFocus(
                         startInputReason, mClient, windowGainingFocus, startInputFlags,
                         softInputMode, windowFlags, tba, servedContext, missingMethodFlags,
-                        view.getContext().getApplicationInfo().targetSdkVersion);
+                        view.getContext().getApplicationInfo().targetSdkVersion,
+                        ResultCallbacks.of(value));
+                res = Completable.getResult(value);
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res == null) {
                     Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
@@ -2207,6 +2228,7 @@
      * @hide
      */
     public void notifyImeHidden(IBinder windowToken) {
+        ImeTracing.getInstance().triggerClientDump("InputMethodManager#notifyImeHidden", this);
         synchronized (mH) {
             try {
                 if (mCurMethod != null && mCurRootView != null
@@ -3312,6 +3334,9 @@
             if (mImeInsetsConsumer != null) {
                 mImeInsetsConsumer.dumpDebug(proto, IME_INSETS_SOURCE_CONSUMER);
             }
+            if (mServedInputConnectionWrapper != null) {
+                mServedInputConnectionWrapper.dumpDebug(proto, INPUT_CONNECTION);
+            }
         }
     }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 14abbdb..9c63d56 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -71,7 +71,8 @@
     // TODO: remove this
     private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
             "UntranslatableReplacementStringInSubtypeName";
-    private static final int SUBTYPE_ID_NONE = 0;
+    /** {@hide} */
+    public static final int SUBTYPE_ID_NONE = 0;
 
     private final boolean mIsAuxiliary;
     private final boolean mOverridesImplicitlyEnabledSubtype;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 00ba326..0025d1e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,7 +16,7 @@
 
 package android.widget;
 
-import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
+import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
 
 import android.R;
 import android.animation.ValueAnimator;
@@ -87,6 +87,7 @@
 import android.util.TypedValue;
 import android.view.ActionMode;
 import android.view.ActionMode.Callback;
+import android.view.ContentInfo;
 import android.view.ContextMenu;
 import android.view.ContextThemeWrapper;
 import android.view.DragAndDropPermissions;
@@ -98,7 +99,6 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
-import android.view.OnReceiveContentListener;
 import android.view.SubMenu;
 import android.view.View;
 import android.view.View.DragShadowBuilder;
@@ -2869,8 +2869,8 @@
             final int originalLength = mTextView.getText().length();
             Selection.setSelection((Spannable) mTextView.getText(), offset);
             final ClipData clip = event.getClipData();
-            final OnReceiveContentListener.Payload payload =
-                    new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP)
+            final ContentInfo payload =
+                    new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP)
                     .build();
             mTextView.performReceiveContent(payload);
             if (dragDropIntoItself) {
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 7c20472..4f1c40a 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -2773,6 +2773,7 @@
                 int left, int top, int right, int bottom) {
             AccessibilityNodeInfo info = mInputText.createAccessibilityNodeInfo();
             info.setSource(NumberPicker.this, VIRTUAL_VIEW_ID_INPUT);
+            info.setAccessibilityFocused(mAccessibilityFocusedView == VIRTUAL_VIEW_ID_INPUT);
             if (mAccessibilityFocusedView != VIRTUAL_VIEW_ID_INPUT) {
                 info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
             }
@@ -2802,6 +2803,7 @@
             info.setClickable(true);
             info.setLongClickable(true);
             info.setEnabled(NumberPicker.this.isEnabled());
+            info.setAccessibilityFocused(mAccessibilityFocusedView == virtualViewId);
             Rect boundsInParent = mTempRect;
             boundsInParent.set(left, top, right, bottom);
             info.setVisibleToUser(isVisibleToUser(boundsInParent));
@@ -2843,6 +2845,7 @@
             info.setParent((View) getParentForAccessibility());
             info.setEnabled(NumberPicker.this.isEnabled());
             info.setScrollable(true);
+            info.setAccessibilityFocused(mAccessibilityFocusedView == View.NO_ID);
 
             final float applicationScale =
                 getContext().getResources().getCompatibilityInfo().applicationScale;
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index a034a7c..e08ccfd 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -156,8 +156,7 @@
                     mSmartSelectSprite != null
                             ? this::startSelectionActionModeWithSmartSelectAnimation
                             : this::startSelectionActionMode,
-                    mTextClassificationHelper::getOriginalSelection,
-                    mTextClassificationHelper::isTextClassifierDestroyed)
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -179,8 +178,7 @@
                     mTextClassificationHelper.getTimeoutDuration(),
                     mTextClassificationHelper::classifyText,
                     this::startLinkActionMode,
-                    mTextClassificationHelper::getOriginalSelection,
-                    mTextClassificationHelper::isTextClassifierDestroyed)
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -196,8 +194,7 @@
                     mTextClassificationHelper.getTimeoutDuration(),
                     mTextClassificationHelper::classifyText,
                     this::invalidateActionMode,
-                    mTextClassificationHelper::getOriginalSelection,
-                    mTextClassificationHelper::isTextClassifierDestroyed)
+                    mTextClassificationHelper::getOriginalSelection)
                     .execute();
         }
     }
@@ -995,7 +992,6 @@
         private final Supplier<SelectionResult> mSelectionResultSupplier;
         private final Consumer<SelectionResult> mSelectionResultCallback;
         private final Supplier<SelectionResult> mTimeOutResultSupplier;
-        private final Supplier<Boolean> mIsTextClassifierDestroyedSupplier;
         private final TextView mTextView;
         private final String mOriginalText;
 
@@ -1010,16 +1006,13 @@
                 @NonNull TextView textView, int timeOut,
                 @NonNull Supplier<SelectionResult> selectionResultSupplier,
                 @NonNull Consumer<SelectionResult> selectionResultCallback,
-                @NonNull Supplier<SelectionResult> timeOutResultSupplier,
-                @NonNull Supplier<Boolean> isTextClassifierDestroyedSupplier) {
+                @NonNull Supplier<SelectionResult> timeOutResultSupplier) {
             super(textView != null ? textView.getHandler() : null);
             mTextView = Objects.requireNonNull(textView);
             mTimeOutDuration = timeOut;
             mSelectionResultSupplier = Objects.requireNonNull(selectionResultSupplier);
             mSelectionResultCallback = Objects.requireNonNull(selectionResultCallback);
             mTimeOutResultSupplier = Objects.requireNonNull(timeOutResultSupplier);
-            mIsTextClassifierDestroyedSupplier =
-                    Objects.requireNonNull(isTextClassifierDestroyedSupplier);
             // Make a copy of the original text.
             mOriginalText = getText(mTextView).toString();
         }
@@ -1033,14 +1026,8 @@
             try {
                 result = mSelectionResultSupplier.get();
             } catch (IllegalStateException e) {
-                // Swallows the exception if the text classifier session is destroyed
-                if (mIsTextClassifierDestroyedSupplier.get()) {
-                    Log.w(LOG_TAG,
-                          "TextClassificationAsyncTask failed because TextClassifier destroyed",
-                          e);
-                } else {
-                    throw e;
-                }
+                // TODO(b/174300371): Only swallows the exception if the TCSession is destroyed
+                Log.w(LOG_TAG, "TextClassificationAsyncTask failed.", e);
             }
             mTextView.removeCallbacks(onTimeOut);
             return result;
@@ -1173,10 +1160,6 @@
             }
         }
 
-        public boolean isTextClassifierDestroyed() {
-          return mTextClassifier.get().isDestroyed();
-        }
-
         private boolean isDarkLaunchEnabled() {
             return TextClassificationManager.getSettings(mContext).isModelDarkLaunchEnabled();
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 98f8087..2357f36 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,10 +17,10 @@
 package android.widget;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT;
+import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.ContentInfo.SOURCE_AUTOFILL;
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+import static android.view.ContentInfo.SOURCE_PROCESS_TEXT;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_RENDERING_INFO_KEY;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
 import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
@@ -146,6 +146,7 @@
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.ActionMode;
 import android.view.Choreographer;
+import android.view.ContentInfo;
 import android.view.ContextMenu;
 import android.view.DragEvent;
 import android.view.Gravity;
@@ -154,7 +155,6 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.OnReceiveContentListener.Payload;
 import android.view.PointerIcon;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -2151,7 +2151,8 @@
                 if (result != null) {
                     if (isTextEditable()) {
                         ClipData clip = ClipData.newPlainText("", result);
-                        Payload payload = new Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
+                        ContentInfo payload =
+                                new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
                         performReceiveContent(payload);
                         if (mEditor != null) {
                             mEditor.refreshTextActionMode();
@@ -11857,7 +11858,7 @@
                     + " cannot be autofilled into " + this);
             return;
         }
-        final Payload payload = new Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         performReceiveContent(payload);
     }
 
@@ -12924,7 +12925,7 @@
         if (clip == null) {
             return;
         }
-        final Payload payload = new Payload.Builder(clip, SOURCE_CLIPBOARD)
+        final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_CLIPBOARD)
                 .setFlags(withFormatting ? 0 : FLAG_CONVERT_TO_PLAIN_TEXT)
                 .build();
         performReceiveContent(payload);
@@ -13740,8 +13741,9 @@
      * @return The portion of the passed-in content that was not handled (may be all, some, or none
      * of the passed-in content).
      */
+    @Nullable
     @Override
-    public @Nullable Payload onReceiveContent(@NonNull Payload payload) {
+    public ContentInfo onReceiveContent(@NonNull ContentInfo payload) {
         if (mEditor != null) {
             return mEditor.getDefaultOnReceiveContentListener().onReceiveContent(this, payload);
         }
diff --git a/core/java/android/widget/TextViewOnReceiveContentListener.java b/core/java/android/widget/TextViewOnReceiveContentListener.java
index 7ef68ec..8cef106 100644
--- a/core/java/android/widget/TextViewOnReceiveContentListener.java
+++ b/core/java/android/widget/TextViewOnReceiveContentListener.java
@@ -17,10 +17,10 @@
 package android.widget;
 
 import static android.content.ContentResolver.SCHEME_CONTENT;
-import static android.view.OnReceiveContentListener.Payload.FLAG_CONVERT_TO_PLAIN_TEXT;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
+import static android.view.ContentInfo.FLAG_CONVERT_TO_PLAIN_TEXT;
+import static android.view.ContentInfo.SOURCE_AUTOFILL;
+import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.ContentInfo.SOURCE_INPUT_METHOD;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -38,9 +38,10 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.util.Log;
+import android.view.ContentInfo;
+import android.view.ContentInfo.Flags;
+import android.view.ContentInfo.Source;
 import android.view.OnReceiveContentListener;
-import android.view.OnReceiveContentListener.Payload.Flags;
-import android.view.OnReceiveContentListener.Payload.Source;
 import android.view.View;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -53,20 +54,21 @@
 import java.util.Arrays;
 
 /**
- * Default implementation of {@link OnReceiveContentListener} for editable
- * {@link TextView} components. This class handles insertion of text (plain text, styled text, HTML,
- * etc) but not images or other content.
+ * Default implementation for {@link View#onReceiveContent} for editable {@link TextView}
+ * components. This class handles insertion of text (plain text, styled text, HTML, etc) but not
+ * images or other content.
  *
  * @hide
  */
 @VisibleForTesting
 public final class TextViewOnReceiveContentListener implements OnReceiveContentListener {
-    private static final String LOG_TAG = "OnReceiveContent";
+    private static final String LOG_TAG = "ReceiveContent";
 
     @Nullable private InputConnectionInfo mInputConnectionInfo;
 
+    @Nullable
     @Override
-    public @Nullable Payload onReceiveContent(@NonNull View view, @NonNull Payload payload) {
+    public ContentInfo onReceiveContent(@NonNull View view, @NonNull ContentInfo payload) {
         if (Log.isLoggable(LOG_TAG, Log.DEBUG)) {
             Log.d(LOG_TAG, "onReceive: " + payload);
         }
@@ -126,7 +128,7 @@
         editable.replace(start, end, replacement);
     }
 
-    private void onReceiveForAutofill(@NonNull TextView view, @NonNull Payload payload) {
+    private void onReceiveForAutofill(@NonNull TextView view, @NonNull ContentInfo payload) {
         ClipData clip = payload.getClip();
         if (isUsageOfImeCommitContentEnabled(view)) {
             clip = handleNonTextViaImeCommitContent(clip);
@@ -145,7 +147,8 @@
         Selection.setSelection(editable, editable.length());
     }
 
-    private static void onReceiveForDragAndDrop(@NonNull TextView view, @NonNull Payload payload) {
+    private static void onReceiveForDragAndDrop(@NonNull TextView view,
+            @NonNull ContentInfo payload) {
         final CharSequence text = coerceToText(payload.getClip(), view.getContext(),
                 payload.getFlags());
         replaceSelection((Editable) view.getText(), text);
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
new file mode 100644
index 0000000..ec67792
--- /dev/null
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.os.RemoteException;
+
+import com.android.internal.view.InputBindResult;
+
+import java.util.function.Supplier;
+
+/**
+ * Defines a set of helper methods to callback corresponding results in {@link ResultCallbacks}.
+ */
+public final class CallbackUtils {
+
+    /**
+     * Not intended to be instantiated.
+     */
+    private CallbackUtils() {
+    }
+
+    /**
+     * A utility method using given {@link IInputBindResultResultCallback} to callback the
+     * {@link InputBindResult}.
+     *
+     * @param callback {@link IInputBindResultResultCallback} to be called back.
+     * @param resultSupplier the supplier from which {@link InputBindResult} is provided.
+     */
+    @AnyThread
+    public static void onResult(@NonNull IInputBindResultResultCallback callback,
+            @NonNull Supplier<InputBindResult> resultSupplier) {
+        try {
+            callback.onResult(resultSupplier.get());
+        } catch (RemoteException ignored) { }
+    }
+}
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index d8d1a7d..b9e1cf0 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -124,6 +124,16 @@
                 return true;
             }
         }
+
+        /**
+         * Blocks the calling thread until this object becomes ready to return the value.
+         */
+        @AnyThread
+        public void await() {
+            try {
+                mLatch.await();
+            } catch (InterruptedException ignored) { }
+        }
     }
 
     /**
@@ -250,6 +260,13 @@
     }
 
     /**
+     * @return an instance of {@link Completable.InputBindResult}.
+     */
+    public static Completable.InputBindResult createInputBindResult() {
+        return new Completable.InputBindResult();
+    }
+
+    /**
      * Completable object of {@link java.lang.Boolean}.
      */
     public static final class Boolean extends Values<java.lang.Boolean> { }
@@ -278,6 +295,18 @@
             extends Values<com.android.internal.view.InputBindResult> { }
 
     /**
+     * Await the result by the {@link Completable.Values}.
+     *
+     * @return the result once {@link ValueBase#onComplete()}
+     */
+    @AnyThread
+    @Nullable
+    public static <T> T getResult(@NonNull Completable.Values<T> value) {
+        value.await();
+        return value.getValue();
+    }
+
+    /**
      * Await the result by the {@link Completable.Int}, and log it if there is no result after
      * given timeout.
      *
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
similarity index 69%
copy from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
copy to core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
index 45e4c69..b52b3b1 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputBindResultResultCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package com.android.internal.inputmethod;
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
-}
+import com.android.internal.view.InputBindResult;
+
+oneway interface IInputBindResultResultCallback {
+    void onResult(in InputBindResult result);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 7131284..c59dcf4 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -21,6 +21,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import com.android.internal.view.InputBindResult;
+
 import java.lang.ref.WeakReference;
 import java.util.concurrent.atomic.AtomicReference;
 
@@ -154,4 +156,31 @@
             }
         };
     }
+
+    /**
+     * Creates {@link IInputBindResultResultCallback.Stub} that is to set
+     * {@link Completable.InputBindResult} when receiving the result.
+     *
+     * @param value {@link Completable.InputBindResult} to be set when receiving the result.
+     * @return {@link IInputBindResultResultCallback.Stub} that can be passed as a binder IPC
+     *         parameter.
+     */
+    @AnyThread
+    public static IInputBindResultResultCallback.Stub of(
+            @NonNull Completable.InputBindResult value) {
+        final AtomicReference<WeakReference<Completable.InputBindResult>>
+                atomicRef = new AtomicReference<>(new WeakReference<>(value));
+
+        return new IInputBindResultResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult(InputBindResult result) {
+                final Completable.InputBindResult value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete(result);
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
index ffc7f05..1553e2e 100644
--- a/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
+++ b/core/java/com/android/internal/inputmethod/SoftInputShowHideReason.java
@@ -48,7 +48,8 @@
         SoftInputShowHideReason.HIDE_DOCKED_STACK_ATTACHED,
         SoftInputShowHideReason.HIDE_RECENTS_ANIMATION,
         SoftInputShowHideReason.HIDE_BUBBLES,
-        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR})
+        SoftInputShowHideReason.HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR,
+        SoftInputShowHideReason.HIDE_REMOVE_CLIENT})
 public @interface SoftInputShowHideReason {
     /** Show soft input by {@link android.view.inputmethod.InputMethodManager#showSoftInput}. */
     int SHOW_SOFT_INPUT = 0;
@@ -161,4 +162,9 @@
      * soft-input when the same window focused again to align with the same behavior prior to R.
      */
     int HIDE_SAME_WINDOW_FOCUSED_WITHOUT_EDITOR = 20;
+
+    /**
+     * Hide soft input when a {@link com.android.internal.view.IInputMethodClient} is removed.
+     */
+    int HIDE_REMOVE_CLIENT = 21;
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 7571f5d..dcd74fd 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -38,6 +38,7 @@
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
+import android.os.BatteryProperty;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
@@ -54,6 +55,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
@@ -144,6 +146,14 @@
 public class BatteryStatsImpl extends BatteryStats {
     private static final String TAG = "BatteryStatsImpl";
     private static final boolean DEBUG = false;
+
+    // TODO(b/169376495): STOPSHIP if true
+    private static final boolean DEBUG_FOREGROUND_STATS = true;
+
+    private static final boolean ENABLE_FOREGROUND_STATS_COLLECTION =
+            DEBUG_FOREGROUND_STATS && SystemProperties.getBoolean(
+                    "debug.battery_foreground_stats_collection", false);
+
     public static final boolean DEBUG_ENERGY = false;
     private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
     private static final boolean DEBUG_BINDER_STATS = false;
@@ -739,6 +749,37 @@
     long mTrackRunningHistoryElapsedRealtimeMs = 0;
     long mTrackRunningHistoryUptimeMs = 0;
 
+    private static final int FOREGROUND_UID_INITIAL_CAPACITY = 10;
+    private static final int INVALID_UID = -1;
+
+    private final IntArray mForegroundUids = ENABLE_FOREGROUND_STATS_COLLECTION
+            ? new IntArray(FOREGROUND_UID_INITIAL_CAPACITY) :  null;
+
+    // Last recorded battery energy capacity.
+    // This is used for computing foregrund power per application.
+    // See: PowerForUid below
+    private long mLastBatteryEnergyCapacityNWh = 0;
+
+    private static final class PowerForUid {
+        public long energyNwh = 0;
+        // Same as energyNwh, but not tracked for the first 2 minutes;
+        public long filteredEnergyNwh = 0;
+        public double totalHours = 0;
+        public long baseTimeMs = 0;
+
+        double computePower() {
+            // units in nW
+            return totalHours != 0 ? energyNwh / totalHours : -1.0;
+        }
+
+        double computeFilteredPower() {
+            // units in nW
+            return totalHours != 0 ? filteredEnergyNwh / totalHours : -1.0;
+        }
+    }
+    private final HashMap<Integer, PowerForUid> mUidToPower = ENABLE_FOREGROUND_STATS_COLLECTION
+            ? new HashMap<>() : null;
+
     final BatteryStatsHistory mBatteryStatsHistory;
 
     final HistoryItem mHistoryCur = new HistoryItem();
@@ -1026,6 +1067,8 @@
 
     private int mNumConnectivityChange;
 
+    private int mBatteryVolt = -1;
+    private int mBatteryCharge = -1;
     private int mEstimatedBatteryCapacity = -1;
 
     private int mMinLearnedBatteryCapacity = -1;
@@ -4154,6 +4197,49 @@
         // TODO(b/155216561): It is possible for isolated uids to be in a higher
         // state than its parent uid. We should track the highest state within the union of host
         // and isolated uids rather than only the parent uid.
+
+
+        int uidState = mapToInternalProcessState(state);
+
+        boolean isForeground = (uidState == Uid.PROCESS_STATE_TOP)
+                ||  (uidState == Uid.PROCESS_STATE_FOREGROUND);
+
+
+        if (ENABLE_FOREGROUND_STATS_COLLECTION) {
+            boolean previouslyInForegrond = false;
+            for (int i = 0; i < mForegroundUids.size(); i++) {
+                if (mForegroundUids.get(i) == uid) {
+                    previouslyInForegrond = true;
+                    if (!isForeground) {
+                        // If we were previously in the foreground, remove the uid
+                        // from the foreground set and dirty the slot.
+                        mForegroundUids.set(i, INVALID_UID);
+                        final PowerForUid pfu =
+                                mUidToPower.computeIfAbsent(uid, unused -> new PowerForUid());
+                        pfu.baseTimeMs = 0;
+                        break;
+                    }
+                }
+            }
+
+            if (!previouslyInForegrond && isForeground) {
+                boolean addedToForeground = false;
+                // Check if we have a free slot to clobber...
+                for (int i = 0; i < mForegroundUids.size(); i++) {
+                    if (mForegroundUids.get(i) == INVALID_UID) {
+                        addedToForeground = true;
+                        mForegroundUids.set(i, uid);
+                        break;
+                    }
+                }
+
+                // ...if not, append to the end of the array.
+                if (!addedToForeground) {
+                    mForegroundUids.add(uid);
+                }
+            }
+        }
+
         FrameworkStatsLog.write(FrameworkStatsLog.UID_PROCESS_STATE_CHANGED, uid,
                 ActivityManager.processStateAmToProto(state));
         getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
@@ -12968,6 +13054,7 @@
                 doWrite = true;
                 resetAllStatsLocked(mSecUptime, mSecRealtime);
                 if (chargeUAh > 0 && level > 0) {
+                    mBatteryCharge = chargeUAh;
                     // Only use the reported coulomb charge value if it is supported and reported.
                     mEstimatedBatteryCapacity = (int) ((chargeUAh / 1000) / (level / 100.0));
                 }
@@ -13140,6 +13227,47 @@
                 startRecordingHistory(elapsedRealtimeMs, uptimeMs, true);
             }
         }
+
+        if (ENABLE_FOREGROUND_STATS_COLLECTION) {
+            mBatteryVolt = volt;
+            if (onBattery) {
+                final long energyNwh = (volt * (long) chargeUAh);
+                final long energyDelta = mLastBatteryEnergyCapacityNWh - energyNwh;
+                for (int i = 0; i < mForegroundUids.size(); i++) {
+                    final int uid = mForegroundUids.get(i);
+                    if (uid == INVALID_UID) {
+                        continue;
+                    }
+                    final PowerForUid pfu = mUidToPower
+                            .computeIfAbsent(uid, unused -> new PowerForUid());
+                    if (pfu.baseTimeMs <= 0) {
+                        pfu.baseTimeMs = currentTimeMs;
+                    } else {
+                        // Check if mLastBatteryEnergyCapacityNWh > energyNwh,
+                        // to make sure we only count discharges
+                        if (energyDelta > 0) {
+                            pfu.energyNwh += energyDelta;
+                            // Convert from milliseconds to hours
+                            // 1000 ms per second * 3600 seconds per hour
+                            pfu.totalHours += ((double) (currentTimeMs - pfu.baseTimeMs)
+                                    / (1.0 * 1000 * 60 * 60));
+                            // Now convert from 2 minutes to hours
+                            // 2 minutes = 1/30 of an hour
+                            if (pfu.totalHours > (2.0 / 60)) {
+                                pfu.filteredEnergyNwh += energyDelta;
+                            }
+
+                        }
+                        pfu.baseTimeMs = currentTimeMs;
+                    }
+                }
+                mLastBatteryEnergyCapacityNWh = energyNwh;
+            } else if (onBattery != mOnBattery) {
+                // Transition to onBattery = false
+                mUidToPower.values().forEach(v -> v.baseTimeMs = 0);
+            }
+        }
+
         mCurrentBatteryLevel = level;
         if (mDischargePlugLevel < 0) {
             mDischargePlugLevel = level;
@@ -16056,6 +16184,48 @@
     }
 
     public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+        if (ENABLE_FOREGROUND_STATS_COLLECTION) {
+            long actualCharge = -1;
+            long actualEnergy = -1;
+            try {
+                IBatteryPropertiesRegistrar registrar =
+                        IBatteryPropertiesRegistrar.Stub.asInterface(
+                                ServiceManager.getService("batteryproperties"));
+                if (registrar != null) {
+                    BatteryProperty prop = new BatteryProperty();
+                    if (registrar.getProperty(
+                                BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER, prop) == 0) {
+                        actualCharge = prop.getLong();
+                    }
+                    prop = new BatteryProperty();
+                    if (registrar.getProperty(
+                                BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER, prop) == 0) {
+                        actualEnergy = prop.getLong();
+                    }
+                }
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+            pw.printf("ActualCharge (uAh): %d\n", (int) actualCharge);
+            pw.printf("ActualEnergy (nWh): %d\n", actualEnergy);
+            pw.printf("mBatteryCharge (uAh): %d\n", mBatteryCharge);
+            pw.printf("mBatteryVolts (mV): %d\n", mBatteryVolt);
+            pw.printf("est energy (nWh): %d\n", mBatteryVolt * (long) mBatteryCharge);
+            pw.printf("mEstimatedBatteryCapacity (mAh): %d\n", mEstimatedBatteryCapacity);
+            pw.printf("mMinLearnedBatteryCapacity (uAh): %d\n", mMinLearnedBatteryCapacity);
+            pw.printf("mMaxLearnedBatteryCapacity (uAh): %d\n", mMaxLearnedBatteryCapacity);
+            pw.printf("est. capacity: %f\n",
+                    (float) actualCharge / (mEstimatedBatteryCapacity * 1000));
+            pw.printf("mCurrentBatteryLevel: %d\n", mCurrentBatteryLevel);
+            pw.println("Total Power per app:");
+            mUidToPower.entrySet().forEach(e ->
+                    pw.printf("Uid: %d, Total watts (nW): %f\n",
+                            e.getKey(), e.getValue().computePower()));
+            pw.println("Total Power per app after first 2 minutes initial launch:");
+            mUidToPower.entrySet().forEach(e ->
+                    pw.printf("Uid: %d, Total watts (nW): %f\n",
+                            e.getKey(), e.getValue().computeFilteredPower()));
+        }
         if (DEBUG) {
             pw.println("mOnBatteryTimeBase:");
             mOnBatteryTimeBase.dump(pw, "  ");
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 33ebe43..4deb40a 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.Trace;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
@@ -111,6 +112,12 @@
         }
     }
 
+    protected Looper getLooper() {
+        synchronized (mMainLooper) {
+            return mMainLooper;
+        }
+    }
+
     protected boolean isFinished() {
         synchronized (mLock) {
             return mFinished;
@@ -259,61 +266,80 @@
     void executeMessage(Message msg) {
         switch (msg.what) {
             case DO_GET_TEXT_AFTER_CURSOR: {
-                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
-                final InputConnection ic = getInputConnection();
-                final CharSequence result;
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
-                    result = null;
-                } else {
-                    result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
-                }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
                 try {
-                    callback.onResult(result);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
+                    final ICharSequenceResultCallback callback =
+                            (ICharSequenceResultCallback) msg.obj;
+                    final InputConnection ic = getInputConnection();
+                    final CharSequence result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getTextAfterCursor on inactive InputConnection");
+                        result = null;
+                    } else {
+                        result = ic.getTextAfterCursor(msg.arg1, msg.arg2);
+                    }
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getTextAfterCursor()."
                             + " result=" + result, e);
+                    }
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
             case DO_GET_TEXT_BEFORE_CURSOR: {
-                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
-                final InputConnection ic = getInputConnection();
-                final CharSequence result;
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
-                    result = null;
-                } else {
-                    result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
-                }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
                 try {
-                    callback.onResult(result);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
+                    final ICharSequenceResultCallback callback =
+                            (ICharSequenceResultCallback) msg.obj;
+                    final InputConnection ic = getInputConnection();
+                    final CharSequence result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
+                        result = null;
+                    } else {
+                        result = ic.getTextBeforeCursor(msg.arg1, msg.arg2);
+                    }
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getTextBeforeCursor()."
                             + " result=" + result, e);
+                    }
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
             case DO_GET_SELECTED_TEXT: {
-                final ICharSequenceResultCallback callback = (ICharSequenceResultCallback) msg.obj;
-                final InputConnection ic = getInputConnection();
-                final CharSequence result;
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "getSelectedText on inactive InputConnection");
-                    result = null;
-                } else {
-                    result = ic.getSelectedText(msg.arg1);
-                }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
                 try {
-                    callback.onResult(result);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to return the result to getSelectedText()."
-                            + " result=" + result, e);
+                    final ICharSequenceResultCallback callback =
+                            (ICharSequenceResultCallback) msg.obj;
+                    final InputConnection ic = getInputConnection();
+                    final CharSequence result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getSelectedText on inactive InputConnection");
+                        result = null;
+                    } else {
+                        result = ic.getSelectedText(msg.arg1);
+                    }
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getSelectedText()."
+                                + " result=" + result, e);
+                    }
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
             case DO_GET_SURROUNDING_TEXT: {
                 final SomeArgs args = (SomeArgs) msg.obj;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
                 try {
                     int beforeLength = (int) args.arg1;
                     int afterLength  = (int) args.arg2;
@@ -335,30 +361,37 @@
                                 + " result=" + result, e);
                     }
                 } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                     args.recycle();
                 }
                 return;
             }
             case DO_GET_CURSOR_CAPS_MODE: {
-                final IIntResultCallback callback = (IIntResultCallback) msg.obj;
-                final InputConnection ic = getInputConnection();
-                final int result;
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
-                    result = 0;
-                } else {
-                    result = ic.getCursorCapsMode(msg.arg1);
-                }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
                 try {
-                    callback.onResult(result);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
+                    final IIntResultCallback callback = (IIntResultCallback) msg.obj;
+                    final InputConnection ic = getInputConnection();
+                    final int result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "getCursorCapsMode on inactive InputConnection");
+                        result = 0;
+                    } else {
+                        result = ic.getCursorCapsMode(msg.arg1);
+                    }
+                    try {
+                        callback.onResult(result);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to getCursorCapsMode()."
                             + " result=" + result, e);
+                    }
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
             case DO_GET_EXTRACTED_TEXT: {
                 final SomeArgs args = (SomeArgs) msg.obj;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
                 try {
                     final ExtractedTextRequest request = (ExtractedTextRequest) args.arg1;
                     final IExtractedTextResultCallback callback =
@@ -378,159 +411,237 @@
                                 + " result=" + result, e);
                     }
                 } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                     args.recycle();
                 }
                 return;
             }
             case DO_COMMIT_TEXT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "commitText on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitText");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "commitText on inactive InputConnection");
+                        return;
+                    }
+                    ic.commitText((CharSequence) msg.obj, msg.arg1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.commitText((CharSequence)msg.obj, msg.arg1);
                 return;
             }
             case DO_SET_SELECTION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "setSelection on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setSelection");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "setSelection on inactive InputConnection");
+                        return;
+                    }
+                    ic.setSelection(msg.arg1, msg.arg2);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.setSelection(msg.arg1, msg.arg2);
                 return;
             }
             case DO_PERFORM_EDITOR_ACTION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "performEditorAction on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performEditorAction");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "performEditorAction on inactive InputConnection");
+                        return;
+                    }
+                    ic.performEditorAction(msg.arg1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.performEditorAction(msg.arg1);
                 return;
             }
             case DO_PERFORM_CONTEXT_MENU_ACTION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "performContextMenuAction on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performContextMenuAction");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "performContextMenuAction on inactive InputConnection");
+                        return;
+                    }
+                    ic.performContextMenuAction(msg.arg1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.performContextMenuAction(msg.arg1);
                 return;
             }
             case DO_COMMIT_COMPLETION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "commitCompletion on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCompletion");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "commitCompletion on inactive InputConnection");
+                        return;
+                    }
+                    ic.commitCompletion((CompletionInfo) msg.obj);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.commitCompletion((CompletionInfo)msg.obj);
                 return;
             }
             case DO_COMMIT_CORRECTION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "commitCorrection on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitCorrection");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "commitCorrection on inactive InputConnection");
+                        return;
+                    }
+                    ic.commitCorrection((CorrectionInfo) msg.obj);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.commitCorrection((CorrectionInfo)msg.obj);
                 return;
             }
             case DO_SET_COMPOSING_TEXT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "setComposingText on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingText");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "setComposingText on inactive InputConnection");
+                        return;
+                    }
+                    ic.setComposingText((CharSequence) msg.obj, msg.arg1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.setComposingText((CharSequence)msg.obj, msg.arg1);
                 return;
             }
             case DO_SET_COMPOSING_REGION: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "setComposingRegion on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#setComposingRegion");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "setComposingRegion on inactive InputConnection");
+                        return;
+                    }
+                    ic.setComposingRegion(msg.arg1, msg.arg2);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.setComposingRegion(msg.arg1, msg.arg2);
                 return;
             }
             case DO_FINISH_COMPOSING_TEXT: {
-                if (isFinished()) {
-                    // In this case, #finishComposingText() is guaranteed to be called already.
-                    // There should be no negative impact if we ignore this call silently.
-                    if (DEBUG) {
-                        Log.w(TAG, "Bug 35301295: Redundant finishComposingText.");
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#finishComposingText");
+                try {
+                    if (isFinished()) {
+                        // In this case, #finishComposingText() is guaranteed to be called already.
+                        // There should be no negative impact if we ignore this call silently.
+                        if (DEBUG) {
+                            Log.w(TAG, "Bug 35301295: Redundant finishComposingText.");
+                        }
+                        return;
                     }
-                    return;
+                    InputConnection ic = getInputConnection();
+                    // Note we do NOT check isActive() here, because this is safe
+                    // for an IME to call at any time, and we need to allow it
+                    // through to clean up our state after the IME has switched to
+                    // another client.
+                    if (ic == null) {
+                        Log.w(TAG, "finishComposingText on inactive InputConnection");
+                        return;
+                    }
+                    ic.finishComposingText();
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                InputConnection ic = getInputConnection();
-                // Note we do NOT check isActive() here, because this is safe
-                // for an IME to call at any time, and we need to allow it
-                // through to clean up our state after the IME has switched to
-                // another client.
-                if (ic == null) {
-                    Log.w(TAG, "finishComposingText on inactive InputConnection");
-                    return;
-                }
-                ic.finishComposingText();
                 return;
             }
             case DO_SEND_KEY_EVENT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "sendKeyEvent on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#sendKeyEvent");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "sendKeyEvent on inactive InputConnection");
+                        return;
+                    }
+                    ic.sendKeyEvent((KeyEvent) msg.obj);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.sendKeyEvent((KeyEvent)msg.obj);
                 return;
             }
             case DO_CLEAR_META_KEY_STATES: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#clearMetaKeyStates");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "clearMetaKeyStates on inactive InputConnection");
+                        return;
+                    }
+                    ic.clearMetaKeyStates(msg.arg1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.clearMetaKeyStates(msg.arg1);
                 return;
             }
             case DO_DELETE_SURROUNDING_TEXT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#deleteSurroundingText");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "deleteSurroundingText on inactive InputConnection");
+                        return;
+                    }
+                    ic.deleteSurroundingText(msg.arg1, msg.arg2);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.deleteSurroundingText(msg.arg1, msg.arg2);
                 return;
             }
             case DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT,
+                        "InputConnection#deleteSurroundingTextInCodePoints");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "deleteSurroundingTextInCodePoints on inactive InputConnection");
+                        return;
+                    }
+                    ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.deleteSurroundingTextInCodePoints(msg.arg1, msg.arg2);
                 return;
             }
             case DO_BEGIN_BATCH_EDIT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "beginBatchEdit on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#beginBatchEdit");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "beginBatchEdit on inactive InputConnection");
+                        return;
+                    }
+                    ic.beginBatchEdit();
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.beginBatchEdit();
                 return;
             }
             case DO_END_BATCH_EDIT: {
-                InputConnection ic = getInputConnection();
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "endBatchEdit on inactive InputConnection");
-                    return;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#endBatchEdit");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "endBatchEdit on inactive InputConnection");
+                        return;
+                    }
+                    ic.endBatchEdit();
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
-                ic.endBatchEdit();
                 return;
             }
             case DO_PERFORM_PRIVATE_COMMAND: {
                 final SomeArgs args = (SomeArgs) msg.obj;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#performPrivateCommand");
                 try {
                     final String action = (String) args.arg1;
                     final Bundle data = (Bundle) args.arg2;
@@ -541,25 +652,31 @@
                     }
                     ic.performPrivateCommand(action, data);
                 } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                     args.recycle();
                 }
                 return;
             }
             case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: {
-                final IIntResultCallback callback = (IIntResultCallback) msg.obj;
-                final InputConnection ic = getInputConnection();
-                final boolean result;
-                if (ic == null || !isActive()) {
-                    Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
-                    result = false;
-                } else {
-                    result = ic.requestCursorUpdates(msg.arg1);
-                }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
                 try {
-                    callback.onResult(result ? 1 : 0);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
-                            + " result=" + result, e);
+                    final IIntResultCallback callback = (IIntResultCallback) msg.obj;
+                    final InputConnection ic = getInputConnection();
+                    final boolean result;
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
+                        result = false;
+                    } else {
+                        result = ic.requestCursorUpdates(msg.arg1);
+                    }
+                    try {
+                        callback.onResult(result ? 1 : 0);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Failed to return the result to requestCursorUpdates()."
+                                + " result=" + result, e);
+                    }
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
@@ -571,6 +688,7 @@
                 if (isFinished()) {
                     return;
                 }
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#closeConnection");
                 try {
                     InputConnection ic = getInputConnection();
                     // Note we do NOT check isActive() here, because this is safe
@@ -590,12 +708,14 @@
                         mInputConnection = null;
                         mFinished = true;
                     }
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                 }
                 return;
             }
             case DO_COMMIT_CONTENT: {
                 final int flags = msg.arg1;
                 SomeArgs args = (SomeArgs) msg.obj;
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
                 try {
                     final IIntResultCallback callback = (IIntResultCallback) args.arg3;
                     final InputConnection ic = getInputConnection();
@@ -620,6 +740,7 @@
                                 + " result=" + result, e);
                     }
                 } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
                     args.recycle();
                 }
                 return;
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 33abbe8..e78ed4e 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -24,6 +24,7 @@
 import com.android.internal.view.InputBindResult;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 
 /**
  * Public interface to the global input method manager, used by all client
@@ -48,14 +49,15 @@
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'attribute' is non-null then also does startInput.
     // @NonNull
-    InputBindResult startInputOrWindowGainedFocus(
+    void startInputOrWindowGainedFocus(
             /* @StartInputReason */ int startInputReason,
             in IInputMethodClient client, in IBinder windowToken,
             /* @StartInputFlags */ int startInputFlags,
             /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
             int windowFlags, in EditorInfo attribute, IInputContext inputContext,
             /* @InputConnectionInspector.MissingMethodFlags */ int missingMethodFlags,
-            int unverifiedTargetSdkVersion);
+            int unverifiedTargetSdkVersion,
+            in IInputBindResultResultCallback inputBindResult);
 
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index ff3543c8..d30d662 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -16,21 +16,36 @@
 
 package com.android.internal.widget;
 
+import static android.view.inputmethod.InputConnectionProto.CURSOR_CAPS_MODE;
+import static android.view.inputmethod.InputConnectionProto.EDITABLE_TEXT;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_END;
+import static android.view.inputmethod.InputConnectionProto.SELECTED_TEXT_START;
+
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
 import android.text.Editable;
+import android.text.Selection;
 import android.text.method.KeyListener;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.DumpableInputConnection;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.widget.TextView;
 
-public class EditableInputConnection extends BaseInputConnection {
+/**
+ * Base class for an editable InputConnection instance. This is created by {@link TextView} or
+ * {@link EditText}.
+ */
+public class EditableInputConnection extends BaseInputConnection
+        implements DumpableInputConnection {
     private static final boolean DEBUG = false;
+    private static final boolean DUMP_TEXT = false;
     private static final String TAG = "EditableInputConnection";
 
     private final TextView mTextView;
@@ -222,4 +237,28 @@
         }
         return true;
     }
+
+    @Override
+    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        CharSequence editableText = mTextView.getText();
+        CharSequence selectedText = getSelectedText(0 /* flags */);
+        if (DUMP_TEXT) {
+            if (editableText != null) {
+                proto.write(EDITABLE_TEXT, editableText.toString());
+            }
+            if (selectedText != null) {
+                proto.write(SELECTED_TEXT, selectedText.toString());
+            }
+        }
+        final Editable content = getEditable();
+        if (content != null) {
+            int start = Selection.getSelectionStart(content);
+            int end = Selection.getSelectionEnd(content);
+            proto.write(SELECTED_TEXT_START, start);
+            proto.write(SELECTED_TEXT_END, end);
+        }
+        proto.write(CURSOR_CAPS_MODE, getCursorCapsMode(0));
+        proto.end(token);
+    }
 }
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index b4e108f..3f205c78 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -16,69 +16,61 @@
 
 package com.android.internal.widget;
 
-import android.annotation.Nullable;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.ImageDecoder;
+import android.graphics.drawable.AnimatedImageDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.util.Log;
+import android.util.Size;
 
 import java.io.IOException;
-import java.io.InputStream;
 
-/**
- * A class to extract Bitmaps from a MessagingStyle message.
- */
+/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
 public class LocalImageResolver {
     private static final String TAG = LocalImageResolver.class.getSimpleName();
 
     private static final int MAX_SAFE_ICON_SIZE_PX = 480;
 
-    @Nullable
     public static Drawable resolveImage(Uri uri, Context context) throws IOException {
-        BitmapFactory.Options onlyBoundsOptions = getBoundsOptionsForImage(uri, context);
-        if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) {
-            return null;
-        }
-
-        int originalSize =
-                (onlyBoundsOptions.outHeight > onlyBoundsOptions.outWidth)
-                        ? onlyBoundsOptions.outHeight
-                        : onlyBoundsOptions.outWidth;
-
-        double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
-                        ? (originalSize / MAX_SAFE_ICON_SIZE_PX)
-                        : 1.0;
-
-        BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
-        bitmapOptions.inSampleSize = getPowerOfTwoForSampleRatio(ratio);
-        InputStream input = context.getContentResolver().openInputStream(uri);
-        Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
-        input.close();
-        return new BitmapDrawable(context.getResources(), bitmap);
+        final ImageDecoder.Source source =
+                ImageDecoder.createSource(context.getContentResolver(), uri);
+        final Drawable drawable =
+                ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded);
+        return drawable;
     }
 
-    private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context)
+    public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight)
             throws IOException {
-        BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
-        try (InputStream input = context.getContentResolver().openInputStream(uri)) {
-            if (input == null) {
-                throw new IllegalArgumentException();
+        final ImageDecoder.Source source =
+                ImageDecoder.createSource(context.getContentResolver(), uri);
+        return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
+            final Size size = info.getSize();
+            if (size.getWidth() > size.getHeight()) {
+                if (size.getWidth() > maxWidth) {
+                    final int targetHeight = size.getHeight() * maxWidth / size.getWidth();
+                    decoder.setTargetSize(maxWidth, targetHeight);
+                }
+            } else {
+                if (size.getHeight() > maxHeight) {
+                    final int targetWidth = size.getWidth() * maxHeight / size.getHeight();
+                    decoder.setTargetSize(targetWidth, maxHeight);
+                }
             }
-            onlyBoundsOptions.inJustDecodeBounds = true;
-            BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
-        } catch (IllegalArgumentException iae) {
-            onlyBoundsOptions.outWidth = -1;
-            onlyBoundsOptions.outHeight = -1;
-            Log.e(TAG, "error loading image", iae);
-        }
-        return onlyBoundsOptions;
+        });
     }
 
     private static int getPowerOfTwoForSampleRatio(double ratio) {
-        int k = Integer.highestOneBit((int) Math.floor(ratio));
+        final int k = Integer.highestOneBit((int) Math.floor(ratio));
         return Math.max(1, k);
     }
+
+    private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
+            ImageDecoder.Source source) {
+        final Size size = info.getSize();
+        final int originalSize = Math.max(size.getHeight(), size.getWidth());
+        final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
+                ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX
+                : 1.0;
+        decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
+    }
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index b562ef8..9712b4e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -311,6 +311,15 @@
         return getDevicePolicyManager().getPasswordMinimumMetrics(userId);
     }
 
+    /**
+     * Returns the effective complexity for the user.
+     * @param userId  The user to return the complexity for.
+     * @return complexity level for the user.
+     */
+    public @DevicePolicyManager.PasswordComplexity int getRequestedPasswordComplexity(int userId) {
+        return getDevicePolicyManager().getAggregatedPasswordComplexityForUser(userId);
+    }
+
     public int getRequestedPasswordQuality(int userId) {
         return getDevicePolicyManager().getPasswordQuality(null, userId);
     }
diff --git a/core/jni/android_media_MicrophoneInfo.cpp b/core/jni/android_media_MicrophoneInfo.cpp
index 5bd808b..b70190f 100644
--- a/core/jni/android_media_MicrophoneInfo.cpp
+++ b/core/jni/android_media_MicrophoneInfo.cpp
@@ -56,8 +56,8 @@
     jobject jFrequencyResponses = NULL;
     jobject jChannelMappings = NULL;
 
-    jDeviceId = env->NewStringUTF(String8(microphoneInfo->getDeviceId()).string());
-    jAddress = env->NewStringUTF(String8(microphoneInfo->getAddress()).string());
+    jDeviceId = env->NewStringUTF(microphoneInfo->getDeviceId().c_str());
+    jAddress = env->NewStringUTF(microphoneInfo->getAddress().c_str());
     if (microphoneInfo->getGeometricLocation().size() != 3 ||
             microphoneInfo->getOrientation().size() != 3) {
         jStatus = nativeToJavaStatus(BAD_VALUE);
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index 5eeeb04..748b4b4 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -26,17 +26,7 @@
 # Graphics stats
 jreck@google.com
 
-# Temporary Block to assist in migration
-# Bug: 143080132
-per-file *enums.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *media_output_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *networkcapabilities.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *data_stall_event.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *procstats_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *usb.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *network_stack.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *tethering.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *dns_resolver.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *device_policy.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *launcher.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
-per-file *mediametrics.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com
+# Accessibility
+pweaver@google.com
+hongmingjin@google.com
+cbrower@google.com
diff --git a/core/proto/android/view/inputmethod/inputconnection.proto b/core/proto/android/view/inputmethod/inputconnection.proto
new file mode 100644
index 0000000..ad9a95a
--- /dev/null
+++ b/core/proto/android/view/inputmethod/inputconnection.proto
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+package android.view.inputmethod;
+
+option java_multiple_files = true;
+
+/**
+ * Represents a {@link android.view.inputmethod.InputConnection} object.
+ */
+message InputConnectionProto {
+  optional string editable_text = 1 [(.android.privacy).dest = DEST_LOCAL];
+  optional string selected_text = 2 [(.android.privacy).dest = DEST_LOCAL];
+  optional int32 selected_text_start = 3;
+  optional int32 selected_text_end = 4;
+  optional int32 cursor_caps_mode = 5;
+}
\ No newline at end of file
diff --git a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
index 5c0f341..c1dce6f 100644
--- a/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
+++ b/core/proto/android/view/inputmethod/inputmethodeditortrace.proto
@@ -24,6 +24,7 @@
 import "frameworks/base/core/proto/android/view/insetscontroller.proto";
 import "frameworks/base/core/proto/android/view/imeinsetssourceconsumer.proto";
 import "frameworks/base/core/proto/android/view/inputmethod/editorinfo.proto";
+import "frameworks/base/core/proto/android/view/inputmethod/inputconnection.proto";
 import "frameworks/base/core/proto/android/view/imefocuscontroller.proto";
 
 import "frameworks/base/core/proto/android/server/inputmethod/inputmethodmanagerservice.proto";
@@ -70,6 +71,7 @@
         optional ImeInsetsSourceConsumerProto ime_insets_source_consumer = 5;
         optional EditorInfoProto editor_info = 6;
         optional ImeFocusControllerProto ime_focus_controller = 7;
+        optional InputConnectionProto input_connection = 8;
     }
 }
 
diff --git a/core/proto/android/view/windowlayoutparams.proto b/core/proto/android/view/windowlayoutparams.proto
index 4bb56f8..062485d 100644
--- a/core/proto/android/view/windowlayoutparams.proto
+++ b/core/proto/android/view/windowlayoutparams.proto
@@ -35,7 +35,7 @@
     optional int32 height = 5;
     optional float horizontal_margin = 6;
     optional float vertical_margin = 7;
-    optional int32 gravity = 8;
+    optional int32 gravity = 8 [(.android.typedef) = "android.view.Gravity.GravityFlags"];
     optional int32 soft_input_mode = 9 [(.android.typedef) = "android.view.WindowManager.LayoutParams.SoftInputModeFlags"];
     optional .android.graphics.PixelFormatProto.Format format = 10;
     optional int32 window_animations = 11;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 11a8214..5a69056 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -120,6 +120,12 @@
     <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
 
+    <protected-broadcast android:name="android.app.action.USER_ADDED" />
+    <protected-broadcast android:name="android.app.action.USER_REMOVED" />
+    <protected-broadcast android:name="android.app.action.USER_STARTED" />
+    <protected-broadcast android:name="android.app.action.USER_STOPPED" />
+    <protected-broadcast android:name="android.app.action.USER_SWITCHED" />
+
     <protected-broadcast android:name="android.app.action.BUGREPORT_SHARING_DECLINED" />
     <protected-broadcast android:name="android.app.action.BUGREPORT_FAILED" />
     <protected-broadcast android:name="android.app.action.BUGREPORT_SHARE" />
@@ -3104,6 +3110,12 @@
     <permission android:name="android.permission.DUMP"
         android:protectionLevel="signature|privileged|development" />
 
+    <!-- Allows an application to start tracing for InputMethod and WindowManager.
+    <p>Not for use by third-party applications.
+    @hide -->
+    <permission android:name="android.permission.CONTROL_UI_TRACING"
+        android:protectionLevel="signature|privileged|development" />
+
     <!-- Allows an application to read the low-level system log files.
     <p>Not for use by third-party applications, because
     Log entries can contain the user's private information. -->
@@ -4190,6 +4202,14 @@
     <permission android:name="android.permission.FACTORY_TEST"
         android:protectionLevel="signature" />
 
+    <!-- @hide @TestApi Allows an application to broadcast the intent {@link
+         android.content.Intent#ACTION_CLOSE_SYSTEM_DIALOGS}.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"
+        android:protectionLevel="signature|recents" />
+    <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+
     <!-- Allows an application to broadcast a notification that an application
          package has been removed.
          <p>Not for use by third-party applications.
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index b0ee12a..88998f2 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -26,6 +26,18 @@
     android:theme="@style/Theme.DeviceDefault.Notification"
     >
 
+    <ImageView
+        android:id="@+id/left_icon"
+        android:layout_width="@dimen/notification_left_icon_size"
+        android:layout_height="@dimen/notification_left_icon_size"
+        android:layout_gravity="center_vertical|start"
+        android:layout_marginStart="@dimen/notification_left_icon_start"
+        android:background="@drawable/notification_large_icon_outline"
+        android:importantForAccessibility="no"
+        android:scaleType="centerCrop"
+        android:visibility="gone"
+        />
+
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
         android:layout_width="@dimen/notification_icon_circle_size"
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index 69d4a12..0006384 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -23,6 +23,18 @@
     android:tag="base"
     >
 
+    <ImageView
+        android:id="@+id/left_icon"
+        android:layout_width="@dimen/notification_left_icon_size"
+        android:layout_height="@dimen/notification_left_icon_size"
+        android:layout_gravity="center_vertical|start"
+        android:layout_marginStart="@dimen/notification_left_icon_start"
+        android:background="@drawable/notification_large_icon_outline"
+        android:importantForAccessibility="no"
+        android:scaleType="centerCrop"
+        android:visibility="gone"
+        />
+
     <com.android.internal.widget.CachingIconView
         android:id="@+id/icon"
         android:layout_width="@dimen/notification_icon_circle_size"
@@ -34,7 +46,7 @@
         />
 
     <LinearLayout
-        android:id="@+id/notification_standard_view_column"
+        android:id="@+id/notification_headerless_view_column"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_vertical"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c9f140d..f8cbfeb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4239,6 +4239,35 @@
          If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
     <integer name="config_defaultRefreshRateInZone">0</integer>
 
+    <!-- The display uses different gamma curves for different refresh rates. It's hard for panel
+         vendor to tune the curves to have exact same brightness for different refresh rate. So
+         flicker could be observed at switch time. The issue can be observed on the screen with
+         even full white content at the high brightness. To prevent flickering, we support fixed
+         refresh rates if the display and ambient brightness are equal to or above the provided
+         thresholds. You can define multiple threshold levels as higher brightness environments
+         may have lower display brightness requirements for the flickering is visible. And the
+         high brightness environment could have higher threshold.
+         For example, fixed refresh rate if
+             display brightness >= disp0 && ambient brightness >= amb0
+             || display brightness >= disp1 && ambient brightness >= amb1 -->
+    <integer-array translatable="false" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>disp0</item>
+           <item>disp1</item>
+        -->
+    </integer-array>
+
+    <integer-array translatable="false" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate">
+         <!--
+           <item>amb0</item>
+           <item>amb1</item>
+        -->
+    </integer-array>
+
+    <!-- Default refresh rate in the high zone defined by brightness and ambient thresholds.
+         If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
+    <integer name="config_fixedRefreshRateInHighZone">0</integer>
+
     <!-- The type of the light sensor to be used by the display framework for things like
          auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
     <string name="config_displayLightSensorType" translatable="false" />
@@ -4522,4 +4551,7 @@
 
     <!-- If true, hide the display cutout with display area -->
     <bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
+
+    <!-- Indicates that default fitness tracker app needs to request sensor and location permissions. -->
+    <bool name="config_trackerAppNeedsPermissions">false</bool>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 19591f6..4bcabff 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -714,6 +714,10 @@
     <dimen name="notification_right_icon_headerless_margin">12dp</dimen>
     <!-- The top margin of the right icon in the "big" notification states -->
     <dimen name="notification_right_icon_big_margin_top">16dp</dimen>
+    <!-- The size of the left icon -->
+    <dimen name="notification_left_icon_size">@dimen/notification_icon_circle_size</dimen>
+    <!-- The left padding of the left icon -->
+    <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen>
     <!-- The alpha of a disabled notification button -->
     <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item>
 
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index f77c6f9..a12d2a9 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -194,6 +194,12 @@
   <!-- A tag used to save the index where the custom view is stored -->
   <item type="id" name="notification_custom_view_index_tag" />
 
+  <!-- A tag used to store the margin end for this view when the right icon is visible -->
+  <item type="id" name="tag_margin_end_when_icon_gone" />
+
+  <!-- A tag used to store the margin end for this view when the right icon is gone -->
+  <item type="id" name="tag_margin_end_when_icon_visible" />
+
   <!-- Marks the "copy to clipboard" button in the ChooserActivity -->
   <item type="id" name="chooser_copy_button" />
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 40aae9e..84556d4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -218,7 +218,7 @@
   <java-symbol type="id" name="inbox_text6" />
   <java-symbol type="id" name="status_bar_latest_event_content" />
   <java-symbol type="id" name="notification_main_column" />
-  <java-symbol type="id" name="notification_standard_view_column" />
+  <java-symbol type="id" name="notification_headerless_view_column" />
   <java-symbol type="id" name="sms_short_code_confirm_message" />
   <java-symbol type="id" name="sms_short_code_detail_layout" />
   <java-symbol type="id" name="sms_short_code_detail_message" />
@@ -3070,6 +3070,8 @@
   <java-symbol type="dimen" name="notification_media_image_margin_end" />
   <java-symbol type="id" name="notification_action_list_margin_target" />
   <java-symbol type="dimen" name="notification_action_disabled_alpha" />
+  <java-symbol type="id" name="tag_margin_end_when_icon_visible" />
+  <java-symbol type="id" name="tag_margin_end_when_icon_gone" />
 
   <!-- Override Wake Key Behavior When Screen is Off -->
   <java-symbol type="bool" name="config_wakeOnDpadKeyPress" />
@@ -3811,6 +3813,11 @@
   <java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
   <java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
 
+  <!-- For fixed refresh rate displays in high brightness-->
+  <java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
+  <java-symbol type="array" name="config_highDisplayBrightnessThresholdsOfFixedRefreshRate" />
+  <java-symbol type="array" name="config_highAmbientBrightnessThresholdsOfFixedRefreshRate" />
+
   <!-- For Auto-Brightness -->
   <java-symbol type="string" name="config_displayLightSensorType" />
 
@@ -4103,4 +4110,7 @@
   <java-symbol type="string" name="window_magnification_prompt_content" />
   <java-symbol type="string" name="turn_on_magnification_settings_action" />
   <java-symbol type="string" name="dismiss_action" />
+
+  <java-symbol type="bool" name="config_trackerAppNeedsPermissions"/>
+
 </resources>
diff --git a/core/res/res/xml/config_user_types.xml b/core/res/res/xml/config_user_types.xml
index 5fd8a95..71dfc55 100644
--- a/core/res/res/xml/config_user_types.xml
+++ b/core/res/res/xml/config_user_types.xml
@@ -40,7 +40,7 @@
 and the PROFILE user android.os.usertype.profile.MANAGED) and creates a new PROFILE user type
 (com.example.profilename):
 
-<user-types>
+<user-types version="0">
     <full-type name="android.os.usertype.full.SECONDARY" >
         <default-restrictions no_sms="true" />
     </full-type>
@@ -65,6 +65,11 @@
     <profile-type
         name="com.example.profilename"
         max-allowed-per-parent="2" />
+
+    <change-user-type
+        from="android.os.usertype.profile.MANAGED"
+        to="com.example.profilename"
+        whenVersionLeq="1" />
 </user-types>
 
 Mandatory attributes:
@@ -93,6 +98,10 @@
 Note, however, that default-restrictions refers to the restrictions applied at the time of user
 creation; therefore, the active restrictions of any pre-existing users will not be updated.
 
+The 'change-user-type' tag should be used in conjunction with the 'version' property of
+'user-types'. It defines a type change for all pre-existing users of 'from' type to the new 'to'
+type, if the former 'user-type's version of device is less than or equal to 'whenVersionLeq'.
+
 -->
 <user-types>
 </user-types>
diff --git a/core/tests/coretests/src/android/widget/NumberPickerTest.java b/core/tests/coretests/src/android/widget/NumberPickerTest.java
new file mode 100644
index 0000000..cab7c89
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/NumberPickerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class NumberPickerTest {
+    @Test
+    public void testAccessibilityFocusedProperty() {
+        final int virtualViewIdIncrement = 1;
+        final int VirtualViewIdInput = 2;
+        final int VirtualViewIdDecrement = 3;
+        final NumberPicker np =
+                new NumberPicker(InstrumentationRegistry.getInstrumentation().getContext());
+        final AccessibilityNodeProvider provider = np.getAccessibilityNodeProvider();
+
+        AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(View.NO_ID);
+        assertFalse(info.isAccessibilityFocused());
+        info.recycle();
+        provider.performAction(View.NO_ID, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+        info = provider.createAccessibilityNodeInfo(View.NO_ID);
+        assertTrue(info.isAccessibilityFocused());
+        info.recycle();
+
+        info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement);
+        assertFalse(info.isAccessibilityFocused());
+        info.recycle();
+        provider.performAction(
+                virtualViewIdIncrement,
+                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS,
+                null
+        );
+        info = provider.createAccessibilityNodeInfo(virtualViewIdIncrement);
+        assertTrue(info.isAccessibilityFocused());
+        info.recycle();
+
+        info = provider.createAccessibilityNodeInfo(VirtualViewIdInput);
+        assertFalse(info.isAccessibilityFocused());
+        info.recycle();
+        provider.performAction(
+                VirtualViewIdInput,
+                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS,
+                null
+        );
+        info = provider.createAccessibilityNodeInfo(VirtualViewIdInput);
+        assertTrue(info.isAccessibilityFocused());
+        info.recycle();
+
+        info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement);
+        assertFalse(info.isAccessibilityFocused());
+        info.recycle();
+        provider.performAction(
+                VirtualViewIdDecrement,
+                AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS,
+                null
+        );
+        info = provider.createAccessibilityNodeInfo(VirtualViewIdDecrement);
+        assertTrue(info.isAccessibilityFocused());
+        info.recycle();
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
index 7b9283b..9978648 100644
--- a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentTest.java
@@ -16,11 +16,11 @@
 
 package android.widget;
 
-import static android.view.OnReceiveContentListener.Payload.SOURCE_AUTOFILL;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_CLIPBOARD;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_DRAG_AND_DROP;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_INPUT_METHOD;
-import static android.view.OnReceiveContentListener.Payload.SOURCE_PROCESS_TEXT;
+import static android.view.ContentInfo.SOURCE_AUTOFILL;
+import static android.view.ContentInfo.SOURCE_CLIPBOARD;
+import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
+import static android.view.ContentInfo.SOURCE_INPUT_METHOD;
+import static android.view.ContentInfo.SOURCE_PROCESS_TEXT;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 
 import static androidx.test.espresso.Espresso.onView;
@@ -41,7 +41,7 @@
 import android.content.ClipDescription;
 import android.net.Uri;
 import android.os.Bundle;
-import android.view.OnReceiveContentListener;
+import android.view.ContentInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
 import android.view.inputmethod.InputContentInfo;
@@ -133,8 +133,8 @@
         // InputConnection.commitContent.
         ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
         ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
-        OnReceiveContentListener.Payload payload =
-                new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        ContentInfo payload =
+                new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verify(ic.mMock, times(1))
                 .commitContent(any(InputContentInfo.class), eq(0), eq(null));
@@ -155,8 +155,8 @@
         // Invoke the listener and assert that the InputConnection is not invoked.
         ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
         ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
-        OnReceiveContentListener.Payload payload =
-                new OnReceiveContentListener.Payload.Builder(clip, SOURCE_AUTOFILL).build();
+        ContentInfo payload =
+                new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verifyZeroInteractions(ic.mMock);
     }
@@ -176,20 +176,20 @@
         // trigger calls to InputConnection.commitContent.
         ClipDescription description = new ClipDescription("", new String[] {"image/gif"});
         ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI));
-        OnReceiveContentListener.Payload payload =
-                new OnReceiveContentListener.Payload.Builder(clip, SOURCE_CLIPBOARD).build();
+        ContentInfo payload =
+                new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verifyZeroInteractions(ic.mMock);
 
-        payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_INPUT_METHOD).build();
+        payload = new ContentInfo.Builder(clip, SOURCE_INPUT_METHOD).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verifyZeroInteractions(ic.mMock);
 
-        payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build();
+        payload = new ContentInfo.Builder(clip, SOURCE_DRAG_AND_DROP).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verifyZeroInteractions(ic.mMock);
 
-        payload = new OnReceiveContentListener.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build();
+        payload = new ContentInfo.Builder(clip, SOURCE_PROCESS_TEXT).build();
         mDefaultReceiver.onReceiveContent(mEditText, payload);
         verifyZeroInteractions(ic.mMock);
     }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8406fdf..1fb63f0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -322,6 +322,7 @@
         <permission name="android.permission.DELETE_CACHE_FILES"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.CONTROL_UI_TRACING"/>
         <permission name="android.permission.ACTIVITY_EMBEDDING"/>
         <permission name="android.permission.FORCE_STOP_PACKAGES"/>
         <permission name="android.permission.GET_APP_OPS_STATS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index a52eca7..6bcab8a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2749,6 +2749,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1105210816": {
+      "message": "Skipping config check in destroyed state %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1112047265": {
       "message": "finishDrawingWindow: %s mDrawState=%s",
       "level": "DEBUG",
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 97da3cc..1ae6a63 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -46,6 +46,7 @@
     boolean installKeyPair(
         in byte[] privateKey, in byte[] userCert, in byte[] certChain, String alias, int uid);
     boolean removeKeyPair(String alias);
+    boolean containsKeyPair(String alias);
 
     // APIs used by Settings
     boolean deleteCaCertificate(String alias);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
index 8a547b4..7b3b5db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java
@@ -18,12 +18,10 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.ClipDescription.EXTRA_ACTIVITY_OPTIONS;
 import static android.content.ClipDescription.EXTRA_PENDING_INTENT;
-import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
 import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
 import static android.content.Intent.EXTRA_PACKAGE_NAME;
@@ -78,7 +76,7 @@
     private static final String TAG = DragAndDropPolicy.class.getSimpleName();
 
     private final Context mContext;
-    private final IActivityTaskManager mIActivityTaskManager;
+    private final ActivityTaskManager mActivityTaskManager;
     private final Starter mStarter;
     private final SplitScreen mSplitScreen;
     private final ArrayList<DragAndDropPolicy.Target> mTargets = new ArrayList<>();
@@ -86,15 +84,15 @@
     private DragSession mSession;
 
     public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
-        this(context, ActivityTaskManager.getService(), splitScreen,
+        this(context, ActivityTaskManager.getInstance(), splitScreen,
                 new DefaultStarter(context, splitScreen));
     }
 
     @VisibleForTesting
-    DragAndDropPolicy(Context context, IActivityTaskManager activityTaskManager,
+    DragAndDropPolicy(Context context, ActivityTaskManager activityTaskManager,
             SplitScreen splitScreen, Starter starter) {
         mContext = context;
-        mIActivityTaskManager = activityTaskManager;
+        mActivityTaskManager = activityTaskManager;
         mSplitScreen = splitScreen;
         mStarter = starter;
     }
@@ -103,7 +101,7 @@
      * Starts a new drag session with the given initial drag data.
      */
     void start(DisplayLayout displayLayout, ClipData data) {
-        mSession = new DragSession(mContext, mIActivityTaskManager, displayLayout, data);
+        mSession = new DragSession(mContext, mActivityTaskManager, displayLayout, data);
         // TODO(b/169894807): Also update the session data with task stack changes
         mSession.update();
     }
@@ -271,7 +269,7 @@
      */
     private static class DragSession {
         private final Context mContext;
-        private final IActivityTaskManager mIActivityTaskManager;
+        private final ActivityTaskManager mActivityTaskManager;
         private final ClipData mInitialDragData;
 
         final DisplayLayout displayLayout;
@@ -285,10 +283,10 @@
         boolean dragItemSupportsSplitscreen;
         boolean isPhone;
 
-        DragSession(Context context, IActivityTaskManager activityTaskManager,
+        DragSession(Context context, ActivityTaskManager activityTaskManager,
                 DisplayLayout dispLayout, ClipData data) {
             mContext = context;
-            mIActivityTaskManager = activityTaskManager;
+            mActivityTaskManager = activityTaskManager;
             mInitialDragData = data;
             displayLayout = dispLayout;
         }
@@ -298,19 +296,14 @@
          */
         void update() {
 
-            try {
-                List<ActivityManager.RunningTaskInfo> tasks =
-                        mIActivityTaskManager.getFilteredTasks(1,
-                                false /* filterOnlyVisibleRecents */);
-                if (!tasks.isEmpty()) {
-                    final ActivityManager.RunningTaskInfo task = tasks.get(0);
-                    runningTaskWinMode = task.getWindowingMode();
-                    runningTaskActType = task.getActivityType();
-                    runningTaskId = task.taskId;
-                    runningTaskIsResizeable = task.isResizeable;
-                }
-            } catch (RemoteException e) {
-                // Fall through
+            List<ActivityManager.RunningTaskInfo> tasks =
+                    mActivityTaskManager.getTasks(1, false /* filterOnlyVisibleRecents */);
+            if (!tasks.isEmpty()) {
+                final ActivityManager.RunningTaskInfo task = tasks.get(0);
+                runningTaskWinMode = task.getWindowingMode();
+                runningTaskActType = task.getActivityType();
+                runningTaskId = task.taskId;
+                runningTaskIsResizeable = task.isResizeable;
             }
 
             final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index 090d227..4e62ea6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -272,8 +272,9 @@
     @VisibleForTesting
     void applyBoundsAndOffsets(WindowContainerToken token, SurfaceControl leash,
             WindowContainerTransaction wct, SurfaceControl.Transaction t) {
-        wct.setBounds(token, mCurrentDisplayBounds.isEmpty() ? null : mCurrentDisplayBounds);
+        wct.setBounds(token, mCurrentDisplayBounds);
         t.setPosition(leash, mOffsetX,  mOffsetY);
+        t.setWindowCrop(leash, mCurrentDisplayBounds.width(), mCurrentDisplayBounds.height());
     }
 
     @VisibleForTesting
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
index 061d3f8..6e87f13 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java
@@ -107,6 +107,7 @@
         transaction.setWindowCrop(leash, crop);
     }
 
+    // TODO(b/173440321): Correct presentation of letterboxed activities in One-handed mode.
     private void resolveTaskPositionAndCrop(
                 ActivityManager.RunningTaskInfo taskInfo,
                 Point positionInParent,
@@ -125,15 +126,18 @@
         final Rect activityBounds = taskInfo.letterboxActivityBounds;
 
         Insets insets = getInsets();
+        Rect displayBoundsWithInsets =
+                new Rect(mWindowManager.getMaximumWindowMetrics().getBounds());
+        displayBoundsWithInsets.inset(insets);
 
         Rect taskBoundsWithInsets = new Rect(taskBounds);
-        applyInsets(taskBoundsWithInsets, insets, taskInfo.parentBounds);
+        taskBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         Rect activityBoundsWithInsets = new Rect(activityBounds);
-        applyInsets(activityBoundsWithInsets, insets, taskInfo.parentBounds);
+        activityBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         Rect parentBoundsWithInsets = new Rect(parentBounds);
-        applyInsets(parentBoundsWithInsets, insets, parentBounds);
+        parentBoundsWithInsets.intersect(displayBoundsWithInsets);
 
         // Crop need to be in the task coordinates.
         crop.set(activityBoundsWithInsets);
@@ -217,10 +221,4 @@
                                 | WindowInsets.Type.displayCutout());
     }
 
-    private void applyInsets(Rect innerBounds, Insets insets, Rect outerBounds) {
-        Rect outerBoundsWithInsets = new Rect(outerBounds);
-        outerBoundsWithInsets.inset(insets);
-        innerBounds.intersect(outerBoundsWithInsets);
-    }
-
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 07af289..6d6c761 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -506,7 +506,7 @@
 
             // Try fetching the top running task.
             final List<RunningTaskInfo> runningTasks =
-                    ActivityTaskManager.getService().getTasks(1 /* maxNum */);
+                    ActivityTaskManager.getInstance().getTasks(1 /* maxNum */);
             if (runningTasks == null || runningTasks.isEmpty()) {
                 return false;
             }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
index 2fc6944..ced99de 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.apppairs
 
+import android.platform.test.annotations.Presubmit
 import android.os.SystemClock
 import android.util.Log
 import android.view.Surface
@@ -43,6 +44,7 @@
  * Test AppPairs launch.
  * To run this test: `atest WMShellFlickerTests:AppPairsTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 6c4e658..6b44ce6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -36,6 +37,7 @@
  * Test Pip launch.
  * To run this test: `atest WMShellFlickerTests:PipKeyboardTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
index a0056df..c61a0f1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.splitscreen
 
+import android.platform.test.annotations.Presubmit
 import android.view.Surface
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -38,6 +39,7 @@
  * Test SplitScreen launch.
  * To run this test: `atest WMShellFlickerTests:EnterSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
index 32e112d..bf92869 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/ExitSplitScreenTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.splitscreen
 
+import android.platform.test.annotations.Presubmit
 import android.util.Rational
 import android.view.Surface
 import androidx.test.filters.FlakyTest
@@ -40,6 +41,7 @@
  * Test exit SplitScreen mode.
  * To run this test: `atest WMShellFlickerTests:ExitSplitScreenTest`
  */
+@Presubmit
 @RequiresDevice
 @RunWith(Parameterized::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
index fad1f05..92d4bee 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java
@@ -42,7 +42,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
-import android.app.IActivityTaskManager;
+import android.app.ActivityTaskManager;
 import android.app.PendingIntent;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -69,7 +69,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
@@ -88,7 +87,7 @@
     private Context mContext;
 
     @Mock
-    private IActivityTaskManager mIActivityTaskManager;
+    private ActivityTaskManager mActivityTaskManager;
 
     @Mock
     private SplitScreen mSplitScreen;
@@ -134,7 +133,7 @@
             return null;
         }).when(mSplitScreen).registerInSplitScreenListener(any());
 
-        mPolicy = new DragAndDropPolicy(mContext, mIActivityTaskManager, mSplitScreen, mStarter);
+        mPolicy = new DragAndDropPolicy(mContext, mActivityTaskManager, mSplitScreen, mStarter);
         mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -188,9 +187,9 @@
         return info;
     }
 
-    private void setRunningTask(ActivityManager.RunningTaskInfo task) throws RemoteException {
-        doReturn(Collections.singletonList(task)).when(mIActivityTaskManager)
-                .getFilteredTasks(anyInt(), anyBoolean());
+    private void setRunningTask(ActivityManager.RunningTaskInfo task) {
+        doReturn(Collections.singletonList(task)).when(mActivityTaskManager)
+                .getTasks(anyInt(), anyBoolean());
     }
 
     private void setClipDataResizeable(ClipData data, boolean resizeable) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
index 0f719af..fc0e20b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java
@@ -96,6 +96,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(75, 0, 125, 75),
                         /* taskBounds */ new Rect(50, 0, 125, 100)),
@@ -109,6 +110,7 @@
         mLetterboxTaskListener.onTaskInfoChanged(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         // Activity is offset by 25 to the left
                         /* activityBounds */ new Rect(50, 0, 100, 75),
@@ -130,6 +132,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(150, 0, 200, 75),
                         /* taskBounds */ new Rect(125, 0, 200, 100)),
@@ -150,6 +153,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(150, 0, 200, 75),
                         /* taskBounds */ new Rect(125, 0, 200, 100)),
@@ -170,6 +174,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 100),
                         /* activityBounds */ new Rect(50, 0, 100, 75),
                         /* taskBounds */ new Rect(25, 0, 100, 100)),
@@ -190,6 +195,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -210,6 +216,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -230,6 +237,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 100, 150),
                         /* activityBounds */ new Rect(0, 75, 50, 125),
                         /* taskBounds */ new Rect(0, 50, 100, 125)),
@@ -250,6 +258,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds
                         /* parentBounds */ new Rect(0, 0, 200, 125),
                         /* activityBounds */ new Rect(15, 0, 175, 120),
                         /* taskBounds */ new Rect(0, 0, 100, 125)), // equal to parent bounds
@@ -272,6 +281,7 @@
         mLetterboxTaskListener.onTaskAppeared(
                 createTaskInfo(
                         /* taskId */ 1,
+                        /* maxBounds= */ new Rect(0, 0, 100, 150),
                         /* parentBounds */ new Rect(0, 75, 100, 225),
                         /* activityBounds */ new Rect(25, 75, 75, 125),
                         /* taskBounds */ new Rect(0, 75, 100, 125)),
@@ -285,7 +295,7 @@
     public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() {
         setWindowBoundsAndInsets(new Rect(),  Insets.NONE);
         RunningTaskInfo taskInfo =
-                createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect());
+                createTaskInfo(/* taskId */ 1, new Rect(),  new Rect(), new Rect(), new Rect());
         mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
         mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash);
     }
@@ -306,11 +316,13 @@
 
     private static RunningTaskInfo createTaskInfo(
                 int taskId,
+                final Rect maxBounds,
                 final Rect parentBounds,
                 final Rect activityBounds,
                 final Rect taskBounds) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = taskId;
+        taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds);
         taskInfo.parentBounds = parentBounds;
         taskInfo.configuration.windowConfiguration.setBounds(taskBounds);
         taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 4ed5457..cd53217 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -429,6 +429,7 @@
     whole_static_libs: ["libskia"],
 
     srcs: [
+        "canvas/CanvasFrontend.cpp",
         "canvas/CanvasOpBuffer.cpp",
         "canvas/CanvasOpRasterizer.cpp",
         "pipeline/skia/SkiaDisplayList.cpp",
@@ -607,6 +608,7 @@
         "tests/unit/CacheManagerTests.cpp",
         "tests/unit/CanvasContextTests.cpp",
         "tests/unit/CanvasOpTests.cpp",
+        "tests/unit/CanvasFrontendTests.cpp",
         "tests/unit/CommonPoolTests.cpp",
         "tests/unit/DamageAccumulatorTests.cpp",
         "tests/unit/DeferredLayerUpdaterTests.cpp",
diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index 4981792..c6c4ba8 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -19,7 +19,6 @@
 X(Restore)
 X(SaveLayer)
 X(SaveBehind)
-X(Concat44)
 X(Concat)
 X(SetMatrix)
 X(Scale)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 473dc53d..a495ec4 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -125,24 +125,18 @@
     }
 };
 
-struct Concat44 final : Op {
-    static const auto kType = Type::Concat44;
-    Concat44(const SkM44& m) : matrix(m) {}
-    SkM44 matrix;
-    void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); }
-};
 struct Concat final : Op {
     static const auto kType = Type::Concat;
-    Concat(const SkMatrix& matrix) : matrix(matrix) {}
-    SkMatrix matrix;
+    Concat(const SkM44& matrix) : matrix(matrix) {}
+    SkM44 matrix;
     void draw(SkCanvas* c, const SkMatrix&) const { c->concat(matrix); }
 };
 struct SetMatrix final : Op {
     static const auto kType = Type::SetMatrix;
-    SetMatrix(const SkMatrix& matrix) : matrix(matrix) {}
-    SkMatrix matrix;
+    SetMatrix(const SkM44& matrix) : matrix(matrix) {}
+    SkM44 matrix;
     void draw(SkCanvas* c, const SkMatrix& original) const {
-        c->setMatrix(SkMatrix::Concat(original, matrix));
+        c->setMatrix(SkM44(original) * matrix);
     }
 };
 struct Scale final : Op {
@@ -569,12 +563,9 @@
 }
 
 void DisplayListData::concat(const SkM44& m) {
-    this->push<Concat44>(0, m);
+    this->push<Concat>(0, m);
 }
-void DisplayListData::concat(const SkMatrix& matrix) {
-    this->push<Concat>(0, matrix);
-}
-void DisplayListData::setMatrix(const SkMatrix& matrix) {
+void DisplayListData::setMatrix(const SkM44& matrix) {
     this->push<SetMatrix>(0, matrix);
 }
 void DisplayListData::scale(SkScalar sx, SkScalar sy) {
@@ -834,10 +825,7 @@
 void RecordingCanvas::didConcat44(const SkM44& m) {
     fDL->concat(m);
 }
-void RecordingCanvas::didConcat(const SkMatrix& matrix) {
-    fDL->concat(matrix);
-}
-void RecordingCanvas::didSetMatrix(const SkMatrix& matrix) {
+void RecordingCanvas::didSetM44(const SkM44& matrix) {
     fDL->setMatrix(matrix);
 }
 void RecordingCanvas::didScale(SkScalar sx, SkScalar sy) {
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 63d120c..4851148 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -82,8 +82,7 @@
     void restore();
 
     void concat(const SkM44&);
-    void concat(const SkMatrix&);
-    void setMatrix(const SkMatrix&);
+    void setMatrix(const SkM44&);
     void scale(SkScalar, SkScalar);
     void translate(SkScalar, SkScalar);
     void translateZ(SkScalar);
@@ -154,8 +153,7 @@
     void onFlush() override;
 
     void didConcat44(const SkM44&) override;
-    void didConcat(const SkMatrix&) override;
-    void didSetMatrix(const SkMatrix&) override;
+    void didSetM44(const SkM44&) override;
     void didScale(SkScalar, SkScalar) override;
     void didTranslate(SkScalar, SkScalar) override;
 
diff --git a/libs/hwui/SaveFlags.h b/libs/hwui/SaveFlags.h
new file mode 100644
index 0000000..f3579a8
--- /dev/null
+++ b/libs/hwui/SaveFlags.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+// TODO: Move this to an enum class
+namespace android::SaveFlags {
+
+// These must match the corresponding Canvas API constants.
+enum {
+    Matrix = 0x01,
+    Clip = 0x02,
+    HasAlphaLayer = 0x04,
+    ClipToLayer = 0x10,
+
+    // Helper constant
+    MatrixClip = Matrix | Clip,
+};
+typedef uint32_t Flags;
+
+}  // namespace android::SaveFlags
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index cd908354..6030c36 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -505,13 +505,11 @@
     SkPaint paint = inPaint;
     paint.setAlpha(mProperties.getRootAlpha() * 255);
 
-    Bitmap& bitmap = getBitmapUpdateIfDirty();
-    SkBitmap skiaBitmap;
-    bitmap.getSkBitmap(&skiaBitmap);
+    sk_sp<SkImage> cachedBitmap = getBitmapUpdateIfDirty().makeImage();
 
     int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
     int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
-    canvas->drawBitmapRect(skiaBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
+    canvas->drawImageRect(cachedBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
                            &paint, SkCanvas::kFast_SrcRectConstraint);
 }
 
diff --git a/libs/hwui/canvas/CanvasFrontend.cpp b/libs/hwui/canvas/CanvasFrontend.cpp
new file mode 100644
index 0000000..2c839b0
--- /dev/null
+++ b/libs/hwui/canvas/CanvasFrontend.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include "CanvasFrontend.h"
+#include "CanvasOps.h"
+#include "CanvasOpBuffer.h"
+
+namespace android::uirenderer {
+
+CanvasStateHelper::CanvasStateHelper(int width, int height) {
+    mInitialBounds = SkIRect::MakeWH(width, height);
+    mSaveStack.emplace_back();
+    mClipStack.emplace_back().setRect(mInitialBounds);
+    mTransformStack.emplace_back();
+    mCurrentClipIndex = 0;
+    mCurrentTransformIndex = 0;
+}
+
+bool CanvasStateHelper::internalSave(SaveEntry saveEntry) {
+    mSaveStack.push_back(saveEntry);
+    if (saveEntry.matrix) {
+        // We need to push before accessing transform() to ensure the reference doesn't move
+        // across vector resizes
+        mTransformStack.emplace_back() = transform();
+        mCurrentTransformIndex += 1;
+    }
+    if (saveEntry.clip) {
+        // We need to push before accessing clip() to ensure the reference doesn't move
+        // across vector resizes
+        mClipStack.emplace_back() = clip();
+        mCurrentClipIndex += 1;
+        return true;
+    }
+    return false;
+}
+
+// Assert that the cast from SkClipOp to SkRegion::Op is valid
+static_assert(static_cast<int>(SkClipOp::kDifference) == SkRegion::Op::kDifference_Op);
+static_assert(static_cast<int>(SkClipOp::kIntersect) == SkRegion::Op::kIntersect_Op);
+static_assert(static_cast<int>(SkClipOp::kUnion_deprecated) == SkRegion::Op::kUnion_Op);
+static_assert(static_cast<int>(SkClipOp::kXOR_deprecated) == SkRegion::Op::kXOR_Op);
+static_assert(static_cast<int>(SkClipOp::kReverseDifference_deprecated) == SkRegion::Op::kReverseDifference_Op);
+static_assert(static_cast<int>(SkClipOp::kReplace_deprecated) == SkRegion::Op::kReplace_Op);
+
+void CanvasStateHelper::internalClipRect(const SkRect& rect, SkClipOp op) {
+    clip().opRect(rect, transform(), mInitialBounds, (SkRegion::Op)op, false);
+}
+
+void CanvasStateHelper::internalClipPath(const SkPath& path, SkClipOp op) {
+    clip().opPath(path, transform(), mInitialBounds, (SkRegion::Op)op, true);
+}
+
+bool CanvasStateHelper::internalRestore() {
+    // Prevent underflows
+    if (saveCount() <= 1) {
+        return false;
+    }
+
+    SaveEntry entry = mSaveStack[mSaveStack.size() - 1];
+    mSaveStack.pop_back();
+    bool needsRestorePropagation = entry.layer;
+    if (entry.matrix) {
+        mTransformStack.pop_back();
+        mCurrentTransformIndex -= 1;
+    }
+    if (entry.clip) {
+        // We need to push before accessing clip() to ensure the reference doesn't move
+        // across vector resizes
+        mClipStack.pop_back();
+        mCurrentClipIndex -= 1;
+        needsRestorePropagation = true;
+    }
+    return needsRestorePropagation;
+}
+
+SkRect CanvasStateHelper::getClipBounds() const {
+    SkIRect ibounds = clip().getBounds();
+
+    if (ibounds.isEmpty()) {
+        return SkRect::MakeEmpty();
+    }
+
+    SkMatrix inverse;
+    // if we can't invert the CTM, we can't return local clip bounds
+    if (!transform().invert(&inverse)) {
+        return SkRect::MakeEmpty();
+    }
+
+    SkRect ret = SkRect::MakeEmpty();
+    inverse.mapRect(&ret, SkRect::Make(ibounds));
+    return ret;
+}
+
+bool CanvasStateHelper::quickRejectRect(float left, float top, float right, float bottom) const {
+    // TODO: Implement
+    return false;
+}
+
+bool CanvasStateHelper::quickRejectPath(const SkPath& path) const {
+    // TODO: Implement
+    return false;
+}
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasFrontend.h b/libs/hwui/canvas/CanvasFrontend.h
new file mode 100644
index 0000000..5fccccb
--- /dev/null
+++ b/libs/hwui/canvas/CanvasFrontend.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+// TODO: Can we get the dependencies scoped down more?
+#include "CanvasOps.h"
+#include "CanvasOpBuffer.h"
+#include <SaveFlags.h>
+
+#include <SkRasterClip.h>
+#include <ui/FatVector.h>
+
+#include <optional>
+
+namespace android::uirenderer {
+
+// Exists to avoid forcing all this common logic into the templated class
+class CanvasStateHelper {
+protected:
+    CanvasStateHelper(int width, int height);
+    ~CanvasStateHelper() = default;
+
+    struct SaveEntry {
+        bool clip : 1 = false;
+        bool matrix : 1 = false;
+        bool layer : 1 = false;
+    };
+
+    constexpr SaveEntry saveEntryForLayer() {
+        return {
+            .clip = true,
+            .matrix = true,
+            .layer = true,
+        };
+    }
+
+    constexpr SaveEntry flagsToSaveEntry(SaveFlags::Flags flags) {
+        return SaveEntry {
+            .clip = static_cast<bool>(flags & SaveFlags::Clip),
+            .matrix = static_cast<bool>(flags & SaveFlags::Matrix),
+            .layer = false
+        };
+    }
+
+    bool internalSave(SaveEntry saveEntry);
+    bool internalSave(SaveFlags::Flags flags) {
+        return internalSave(flagsToSaveEntry(flags));
+    }
+    void internalSaveLayer(const SkCanvas::SaveLayerRec& layerRec) {
+        internalSave({
+            .clip = true,
+            .matrix = true,
+            .layer = true
+        });
+        internalClipRect(*layerRec.fBounds, SkClipOp::kIntersect);
+    }
+
+    bool internalRestore();
+
+    void internalClipRect(const SkRect& rect, SkClipOp op);
+    void internalClipPath(const SkPath& path, SkClipOp op);
+
+    SkIRect mInitialBounds;
+    FatVector<SaveEntry, 6> mSaveStack;
+    FatVector<SkMatrix, 6> mTransformStack;
+    FatVector<SkConservativeClip, 6> mClipStack;
+
+    size_t mCurrentTransformIndex;
+    size_t mCurrentClipIndex;
+
+    const SkConservativeClip& clip() const {
+        return mClipStack[mCurrentClipIndex];
+    }
+
+    SkConservativeClip& clip() {
+        return mClipStack[mCurrentClipIndex];
+    }
+
+public:
+    int saveCount() const { return mSaveStack.size(); }
+
+    SkRect getClipBounds() const;
+    bool quickRejectRect(float left, float top, float right, float bottom) const;
+    bool quickRejectPath(const SkPath& path) const;
+
+    const SkMatrix& transform() const {
+        return mTransformStack[mCurrentTransformIndex];
+    }
+
+    SkMatrix& transform() {
+        return mTransformStack[mCurrentTransformIndex];
+    }
+
+    // For compat with existing HWUI Canvas interface
+    void getMatrix(SkMatrix* outMatrix) const {
+        *outMatrix = transform();
+    }
+
+    void setMatrix(const SkMatrix& matrix) {
+        transform() = matrix;
+    }
+
+    void concat(const SkMatrix& matrix) {
+        transform().preConcat(matrix);
+    }
+
+    void rotate(float degrees) {
+        SkMatrix m;
+        m.setRotate(degrees);
+        concat(m);
+    }
+
+    void scale(float sx, float sy) {
+        SkMatrix m;
+        m.setScale(sx, sy);
+        concat(m);
+    }
+
+    void skew(float sx, float sy) {
+        SkMatrix m;
+        m.setSkew(sx, sy);
+        concat(m);
+    }
+
+    void translate(float dx, float dy) {
+        transform().preTranslate(dx, dy);
+    }
+};
+
+// Front-end canvas that handles queries, up-front state, and produces CanvasOp<> output downstream
+template <typename CanvasOpReceiver>
+class CanvasFrontend final : public CanvasStateHelper {
+public:
+    template<class... Args>
+    CanvasFrontend(int width, int height, Args&&... args) : CanvasStateHelper(width, height),
+            mReceiver(std::forward<Args>(args)...) { }
+    ~CanvasFrontend() = default;
+
+    void save(SaveFlags::Flags flags = SaveFlags::MatrixClip) {
+        if (internalSave(flagsToSaveEntry(flags))) {
+            submit<CanvasOpType::Save>({});
+        }
+    }
+
+    void restore() {
+        if (internalRestore()) {
+            submit<CanvasOpType::Restore>({});
+        }
+    }
+
+    template <CanvasOpType T>
+    void draw(CanvasOp<T>&& op) {
+        // The front-end requires going through certain front-doors, which these aren't.
+        static_assert(T != CanvasOpType::Save, "Must use CanvasFrontend::save() call instead");
+        static_assert(T != CanvasOpType::Restore, "Must use CanvasFrontend::restore() call instead");
+
+        if constexpr (T == CanvasOpType::SaveLayer) {
+            internalSaveLayer(op.saveLayerRec);
+        }
+        if constexpr (T == CanvasOpType::SaveBehind) {
+            // Don't use internalSaveLayer as this doesn't apply clipping, it's a "regular" save
+            // But we do want to flag it as a layer, such that restore is Definitely Required
+            internalSave(saveEntryForLayer());
+        }
+        if constexpr (T == CanvasOpType::ClipRect) {
+            internalClipRect(op.rect, op.op);
+        }
+        if constexpr (T == CanvasOpType::ClipPath) {
+            internalClipPath(op.path, op.op);
+        }
+
+        submit(std::move(op));
+    }
+
+    const CanvasOpReceiver& receiver() const { return mReceiver; }
+
+private:
+    CanvasOpReceiver mReceiver;
+
+    template <CanvasOpType T>
+    void submit(CanvasOp<T>&& op) {
+        mReceiver.push_container(CanvasOpContainer(std::move(op), transform()));
+    }
+};
+
+} // namespace android::uirenderer
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/libs/hwui/canvas/CanvasOpRecorder.cpp
similarity index 72%
rename from services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
rename to libs/hwui/canvas/CanvasOpRecorder.cpp
index 45e4c69..bb968ee8 100644
--- a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
+++ b/libs/hwui/canvas/CanvasOpRecorder.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+#include "CanvasOpRecorder.h"
 
-/** @hide */
-parcelable WifiApiServiceInfo {
-    String name;
-    IBinder binder;
-}
+#include "CanvasOpBuffer.h"
+#include "CanvasOps.h"
+
+namespace android::uirenderer {}  // namespace android::uirenderer
diff --git a/libs/hwui/canvas/CanvasOpRecorder.h b/libs/hwui/canvas/CanvasOpRecorder.h
new file mode 100644
index 0000000..7d95bc4
--- /dev/null
+++ b/libs/hwui/canvas/CanvasOpRecorder.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include "hwui/Canvas.h"
+#include "CanvasOpBuffer.h"
+
+#include <vector>
+
+namespace android::uirenderer {
+
+// Interop with existing HWUI Canvas
+class CanvasOpRecorder final : /* todo: public Canvas */ {
+public:
+    // Transform ops
+private:
+    struct SaveEntry {
+
+    };
+
+    std::vector<SaveEntry> mSaveStack;
+};
+
+}  // namespace android::uirenderer
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index f94bae274..4d67166 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -18,6 +18,7 @@
 
 #include <cutils/compiler.h>
 #include <utils/Functor.h>
+#include <SaveFlags.h>
 
 #include <androidfw/ResourceTypes.h>
 #include "Properties.h"
@@ -57,22 +58,6 @@
 using DisplayList = skiapipeline::SkiaDisplayList;
 }
 
-namespace SaveFlags {
-
-// These must match the corresponding Canvas API constants.
-enum {
-    Matrix = 0x01,
-    Clip = 0x02,
-    HasAlphaLayer = 0x04,
-    ClipToLayer = 0x10,
-
-    // Helper constant
-    MatrixClip = Matrix | Clip,
-};
-typedef uint32_t Flags;
-
-}  // namespace SaveFlags
-
 namespace uirenderer {
 namespace VectorDrawable {
 class Tree;
diff --git a/libs/hwui/tests/unit/CanvasFrontendTests.cpp b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
new file mode 100644
index 0000000..05b1179
--- /dev/null
+++ b/libs/hwui/tests/unit/CanvasFrontendTests.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <canvas/CanvasFrontend.h>
+#include <canvas/CanvasOpBuffer.h>
+#include <canvas/CanvasOps.h>
+#include <canvas/CanvasOpRasterizer.h>
+
+#include <tests/common/CallCountingCanvas.h>
+
+#include "SkPictureRecorder.h"
+#include "SkColor.h"
+#include "SkLatticeIter.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkNoDrawCanvas.h>
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::test;
+
+class CanvasOpCountingReceiver {
+public:
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        mOpCounts[static_cast<size_t>(T)] += 1;
+    }
+
+    int operator[](CanvasOpType op) const {
+        return mOpCounts[static_cast<size_t>(op)];
+    }
+
+private:
+    std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts;
+};
+
+TEST(CanvasFrontend, saveCount) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.save();
+    opCanvas.save(SaveFlags::MatrixClip);
+    EXPECT_EQ(2, skiaCanvas.getSaveCount());
+    EXPECT_EQ(2, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    EXPECT_EQ(1, receiver[CanvasOpType::Save]);
+    EXPECT_EQ(1, receiver[CanvasOpType::Restore]);
+}
+
+TEST(CanvasFrontend, transform) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+
+    skiaCanvas.translate(10, 10);
+    opCanvas.translate(10, 10);
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+    {
+        skiaCanvas.save();
+        opCanvas.save(SaveFlags::Matrix);
+        skiaCanvas.scale(2.0f, 1.125f);
+        opCanvas.scale(2.0f, 1.125f);
+
+        EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+        skiaCanvas.restore();
+        opCanvas.restore();
+    }
+
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+    {
+        skiaCanvas.save();
+        opCanvas.save(SaveFlags::Matrix);
+        skiaCanvas.rotate(90.f);
+        opCanvas.rotate(90.f);
+
+        EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+
+        {
+            skiaCanvas.save();
+            opCanvas.save(SaveFlags::Matrix);
+            skiaCanvas.skew(5.0f, 2.25f);
+            opCanvas.skew(5.0f, 2.25f);
+
+            EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+            skiaCanvas.restore();
+            opCanvas.restore();
+        }
+
+        skiaCanvas.restore();
+        opCanvas.restore();
+    }
+
+    EXPECT_EQ(skiaCanvas.getTotalMatrix(), opCanvas.transform());
+}
+
+TEST(CanvasFrontend, drawOpTransform) {
+    CanvasFrontend<CanvasOpBuffer> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    auto makeDrawRect = [] {
+        return CanvasOp<CanvasOpType::DrawRect>{
+            .rect = SkRect::MakeWH(50, 50),
+            .paint = SkPaint(SkColors::kBlack),
+        };
+    };
+
+    opCanvas.draw(makeDrawRect());
+
+    opCanvas.translate(10, 10);
+    opCanvas.draw(makeDrawRect());
+
+    opCanvas.save();
+    opCanvas.scale(2.0f, 4.0f);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.restore();
+
+    opCanvas.save();
+    opCanvas.translate(20, 15);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.save();
+    opCanvas.rotate(90.f);
+    opCanvas.draw(makeDrawRect());
+    opCanvas.restore();
+    opCanvas.restore();
+
+    // Validate the results
+    std::vector<SkMatrix> transforms;
+    transforms.reserve(5);
+    receiver.for_each([&](auto op) {
+        // Filter for the DrawRect calls; ignore the save & restores
+        // (TODO: Add a filtered for_each variant to OpBuffer?)
+        if (op->type() == CanvasOpType::DrawRect) {
+            transforms.push_back(op->transform());
+        }
+    });
+
+    EXPECT_EQ(transforms.size(), 5);
+
+    {
+        // First result should be identity
+        const auto& result = transforms[0];
+        EXPECT_EQ(SkMatrix::kIdentity_Mask, result.getType());
+        EXPECT_EQ(SkMatrix::I(), result);
+    }
+
+    {
+        // Should be translate 10, 10
+        const auto& result = transforms[1];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(10, 10);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + scale 2, 4
+        const auto& result = transforms[2];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(10, 10);
+        m.preScale(2.0f, 4.0f);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + translate 20, 15
+        const auto& result = transforms[3];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask, result.getType());
+        SkMatrix m;
+        m.setTranslate(30, 25);
+        EXPECT_EQ(m, result);
+    }
+
+    {
+        // Should be translate 10, 10 + translate 20, 15 + rotate 90
+        const auto& result = transforms[4];
+        EXPECT_EQ(SkMatrix::kTranslate_Mask | SkMatrix::kAffine_Mask | SkMatrix::kScale_Mask,
+                result.getType());
+        SkMatrix m;
+        m.setTranslate(30, 25);
+        m.preRotate(90.f);
+        EXPECT_EQ(m, result);
+    }
+}
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index b15c322..f186e55 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <canvas/CanvasFrontend.h>
 #include <canvas/CanvasOpBuffer.h>
 #include <canvas/CanvasOps.h>
 #include <canvas/CanvasOpRasterizer.h>
@@ -26,6 +27,7 @@
 #include "SkColor.h"
 #include "SkLatticeIter.h"
 #include "pipeline/skia/AnimatedDrawables.h"
+#include <SkNoDrawCanvas.h>
 
 using namespace android;
 using namespace android::uirenderer;
@@ -78,6 +80,21 @@
 
 using MockBuffer = OpBuffer<MockTypes, MockOpContainer>;
 
+class CanvasOpCountingReceiver {
+public:
+    template <CanvasOpType T>
+    void push_container(CanvasOpContainer<T>&& op) {
+        mOpCounts[static_cast<size_t>(T)] += 1;
+    }
+
+    int operator[](CanvasOpType op) const {
+        return mOpCounts[static_cast<size_t>(op)];
+    }
+
+private:
+    std::array<int, static_cast<size_t>(CanvasOpType::COUNT)> mOpCounts;
+};
+
 template<typename T>
 static int countItems(const T& t) {
     int count = 0;
@@ -614,4 +631,35 @@
     rasterizer.draw(op);
     EXPECT_EQ(1, canvas->drawRectCount);
     EXPECT_EQ(1, canvas->sumTotalDrawCalls());
+}
+
+TEST(CanvasOp, frontendSaveCount) {
+    SkNoDrawCanvas skiaCanvas(100, 100);
+    CanvasFrontend<CanvasOpCountingReceiver> opCanvas(100, 100);
+    const auto& receiver = opCanvas.receiver();
+
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.save();
+    opCanvas.save(SaveFlags::MatrixClip);
+    EXPECT_EQ(2, skiaCanvas.getSaveCount());
+    EXPECT_EQ(2, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    skiaCanvas.restore();
+    opCanvas.restore();
+    EXPECT_EQ(1, skiaCanvas.getSaveCount());
+    EXPECT_EQ(1, opCanvas.saveCount());
+
+    EXPECT_EQ(1, receiver[Op::Save]);
+    EXPECT_EQ(1, receiver[Op::Restore]);
+}
+
+TEST(CanvasOp, frontendTransform) {
+
 }
\ No newline at end of file
diff --git a/libs/hwui/tests/unit/CommonPoolTests.cpp b/libs/hwui/tests/unit/CommonPoolTests.cpp
index da6a260..bffdeca 100644
--- a/libs/hwui/tests/unit/CommonPoolTests.cpp
+++ b/libs/hwui/tests/unit/CommonPoolTests.cpp
@@ -54,7 +54,9 @@
     EXPECT_EQ(0, threads.count(gettid()));
 }
 
-TEST(CommonPool, singleThread) {
+// Disabled since this is flaky. This isn't a necessarily useful functional test, so being
+// disabled isn't that significant. However it may be good to resurrect this somehow.
+TEST(CommonPool, DISABLED_singleThread) {
     std::mutex mutex;
     std::condition_variable fence;
     bool isProcessing = false;
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index accf0f4..26bc659 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -1107,26 +1107,26 @@
             EXPECT_EQ(dy, TRANSLATE_Y);
         }
 
-        virtual void didSetMatrix(const SkMatrix& matrix) override {
+        virtual void didSetM44(const SkM44& matrix) override {
             mDrawCounter++;
             // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
             // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
-            EXPECT_TRUE(matrix.isIdentity());
+            EXPECT_TRUE(matrix == SkM44());
             EXPECT_TRUE(getTotalMatrix().isIdentity());
         }
 
-        virtual void didConcat(const SkMatrix& matrix) override {
+        virtual void didConcat44(const SkM44& matrix) override {
             mDrawCounter++;
             if (mFirstDidConcat) {
                 // First invocation is EndReorderBarrierDrawable::drawShadow to apply shadow matrix.
                 mFirstDidConcat = false;
-                EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
+                EXPECT_EQ(SkM44::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
                           matrix);
                 EXPECT_EQ(SkMatrix::Translate(CASTER_X + TRANSLATE_X, CASTER_Y + TRANSLATE_Y),
                           getTotalMatrix());
             } else {
                 // Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
-                EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), matrix);
+                EXPECT_EQ(SkM44::Translate(TRANSLATE_X, TRANSLATE_Y), matrix);
                 EXPECT_EQ(SkMatrix::Translate(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
             }
         }
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index c19e1ed..4659a92 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -264,6 +264,8 @@
     TestUtils::runOnRenderThreadUnmanaged([&] (RenderThread&) {
         TestUtils::syncHierarchyPropertiesAndDisplayList(node);
     });
+    // Fence on any remaining post'd work
+    TestUtils::runOnRenderThreadUnmanaged([] (RenderThread&) {});
     EXPECT_EQ(2, counts.sync);
     EXPECT_EQ(1, counts.destroyed);
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index a9c754d..c6c7142 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -410,7 +410,7 @@
                         InstallInstalling.this,
                         mInstallId,
                         broadcastIntent,
-                        PendingIntent.FLAG_UPDATE_CURRENT);
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
                 session.commit(pendingIntent.getIntentSender());
                 mCancelButton.setEnabled(false);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java
index caf9718..eea12ec 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledNotificationUtils.java
@@ -243,8 +243,8 @@
         }
 
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return PendingIntent.getActivity(mContext,
-                0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getActivity(mContext, 0 /* request code */, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     /**
@@ -260,8 +260,8 @@
         }
 
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return PendingIntent.getActivity(mContext,
-                0 /* request code */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getActivity(mContext, 0 /* request code */, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 
     /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index c4dceb4..7bf27df 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -95,7 +95,8 @@
                 broadcastIntent.setPackage(getPackageName());
 
                 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
-                        broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+                        broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT
+                                | PendingIntent.FLAG_MUTABLE);
 
                 int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
                 flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
index bf4b03c..7e0490a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
@@ -269,7 +269,8 @@
         // Create a matching PendingIntent and use it to generate the IntentSender
         Intent broadcastIntent = new Intent(action);
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(),
-                broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+                broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT
+                        | PendingIntent.FLAG_MUTABLE);
         return pendingIntent.getIntentSender();
     }
 
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 2b30e0a..41af185 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> oor tot battery gelaai is"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot battery gelaai is"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Battery word tydelik beperk"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laai"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 0bebfab..8e4e402 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ኃይል እስከሚሞላ ድረስ ይቀራል"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ኃይል እስከሚሞላ ድረስ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ባትሪ ለጊዜው ተገድቧል"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ያልታወቀ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ኃይል በመሙላት ላይ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 6eaf3a9..691ec046 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - تأثير محدود على البطارية مؤقتًا"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e207e1c..61214e0 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"চাৰ্জ হ’বলৈ <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> চাৰ্জ হ\'বলৈ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - বেটাৰী সাময়িকভাৱে সীমিত কৰা হৈছে"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"অজ্ঞাত"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"চাৰ্জ কৰি থকা হৈছে"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index ff1209c..db61527 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Enerjinin dolmasına <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - Enerjinin dolmasına <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batareya müvəqqəti məhdudlaşdırılıb"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Naməlum"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Enerji doldurma"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index eb099c1..70fb363 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je trenutno ograničena"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e6e49ab..8a0db82 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Засталося <xliff:g id="TIME">%1$s</xliff:g> да поўнай зарадкі"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарад акумулятара часова абмежаваны"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Невядома"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарадка"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 251c4dd..27dcf10 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Батерията е временно ограничена"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 6a99a84..29862e6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -211,9 +211,9 @@
     <string name="adb_wireless_error" msgid="721958772149779856">"সমস্যা"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"ওয়্যারলেস ডিবাগিং"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"কোন কোন ডিভাইস উপলভ্য আছে তা দেখে নিয়ে ব্যবহার করার জন্য, ওয়্যারলেস ডিবাগিং চালু করুন"</string>
-    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইস যোগ করুন"</string>
+    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"QR কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string>
     <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"QR কোড স্ক্যানার ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string>
-    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"যোগ করার কোড ব্যবহার করে ডিভাইস যোগ করুন"</string>
+    <string name="adb_pair_method_code_title" msgid="1122590300445142904">"পেয়ারিং কোড ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string>
     <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"ছয় সংখ্যার কোড ব্যবহার করে নতুন ডিভাইস যোগ করুন"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"যোগ করা ডিভাইস"</string>
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"এখন কানেক্ট রয়েছে"</string>
@@ -222,16 +222,16 @@
     <string name="adb_device_fingerprint_title_format" msgid="291504822917843701">"ডিভাইসে আঙ্গুলের ছাপ: <xliff:g id="FINGERPRINT_PARAM">%1$s</xliff:g>"</string>
     <string name="adb_wireless_connection_failed_title" msgid="664211177427438438">"কানেক্ট করা যায়নি"</string>
     <string name="adb_wireless_connection_failed_message" msgid="9213896700171602073">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>টি সঠিক নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন"</string>
-    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে যোগ করুন"</string>
-    <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাই যোগ করার কোড"</string>
-    <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"যোগ করা যায়নি"</string>
+    <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"ডিভাইসের সাথে পেয়ার করুন"</string>
+    <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"ওয়াই-ফাইয়ের সাথে পেয়ার করার কোড"</string>
+    <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"পেয়ার করা যায়নি"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"ডিভাইসটি একই নেটওয়ার্কে কানেক্ট আছে কিনা দেখে নিন।"</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইস যোগ করুন"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"ডিভাইস যোগ করা হচ্ছে…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"ডিভাইস যোগ করা যায়নি। এটি দুটি কারণে হয়ে থাকে - QR কোডটি সঠিক নয় বা ডিভাইসটি একই নেটওয়ার্কে কানেক্ট করা নেই।"</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP অ্যাড্রেস ও পোর্ট"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"QR কোড স্ক্যান করুন"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাইয়ের সাহায্যে ডিভাইস যোগ করুন"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"QR কোড স্ক্যান করে ওয়াই-ফাই ব্যবহার করে ডিভাইসের সাথে পেয়ার করুন"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"একটি ওয়াই-ফাই নেটওয়ার্কের সাথে কানেক্ট করুন"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, debug, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"ত্রুটি প্রতিবেদনের শর্টকাট"</string>
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"সম্পূর্ণ চার্জ হতে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>-এ সম্পূর্ণ চার্জ হয়ে যাবে"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ব্যাটারি কিছুক্ষণের জন্য সীমিত করা হয়েছে"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"অজানা"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index aeb7f2e..4704ec8 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index df4c99e..5d04963 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -66,7 +66,7 @@
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Desconnectat"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"S\'està desconnectant..."</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"S\'està connectant…"</string>
-    <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat"</string>
+    <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connectat"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"S\'està vinculant..."</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés al telèfon)"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense contingut multimèdia)"</string>
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria limitada temporalment"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index e563ca5..a99a4da 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do nabití zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do nabití"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterie dočasně omezena"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznámé"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíjí se"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 8a3d054..c90155e 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Opladet om <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – opladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidigt begrænset"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukendt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Oplader"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 624b299..b5b9fc4 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"Inaktiv. Zum Wechseln tippen."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"Aktiv. Zum Wechseln tippen."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"Standby-Status der App:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"Einstellungen für Medientranscodierung"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"Transcodierung deaktivieren"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"Transcodierung für Apps aktivieren"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"Aktive Dienste"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"Momentan ausgeführte Dienste anzeigen und steuern"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView-Implementierung"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Noch <xliff:g id="TIME">%1$s</xliff:g> bis zur Aufladung"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> bis zur Aufladung"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akku vorübergehend eingeschränkt"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Unbekannt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Wird aufgeladen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 7ea9050..203ec40 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Η μπαταρία περιορίστηκε προσωρινά."</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 7affb5c..08ff550 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -196,7 +196,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Elegir perfil"</string>
     <string name="category_personal" msgid="6236798763159385225">"Personal"</string>
     <string name="category_work" msgid="4014193632325996115">"Trabajo"</string>
-    <string name="development_settings_title" msgid="140296922921597393">"Opciones para programadores"</string>
+    <string name="development_settings_title" msgid="140296922921597393">"Opciones para desarrolladores"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Activar opciones para programador"</string>
     <string name="development_settings_summary" msgid="8718917813868735095">"Establecer opciones para desarrollar aplicaciones"</string>
     <string name="development_settings_not_available" msgid="355070198089140951">"Las opciones de programador no están disponibles para este usuario."</string>
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar la carga"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batería limitada temporalmente"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápido"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ccdc1c4..6f6a1a1 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> hasta cargarse completamente"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> hasta cargarse completamente)"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: batería limitada temporalmente"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 852d6df..9d6326e 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Täislaadimiseni on jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täislaadimiseni"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akutase on ajutiselt piiratud"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tundmatu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laadimine"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 6b6b9b6..01afa17 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>: bateria mugatuta egongo da aldi batez"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index af1e55b..dd7ab96 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> مانده تا شارژ کامل"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - باتری موقتاً محدود شده است"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ناشناس"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"در حال شارژ شدن"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 8e819ef..b86b02d 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä täyteen lataukseen"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> täyteen lataukseen"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akun käyttöä rajoitettu tilapäisesti"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tuntematon"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ladataan"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 4754d15..0bca1db 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pile limitée temporairement"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index a3863f59..ebde43b 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à ce que la batterie soit chargée"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterie limitée temporairement"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Batterie en charge"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 81e79e4..b040817 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> (batería limitada temporalmente)"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 9bf0970..c1bd7ca 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"નિષ્ક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"સક્રિય. ટોગલ કરવા માટે ટૅપ કરો."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ઍપ સ્ટૅન્ડબાયની સ્થિતિ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"મીડિયાનું ફૉર્મેટ બદલવાની પ્રક્રિયાના સેટિંગ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ફૉર્મેટ બદલવાની પ્રક્રિયા બંધ કરો"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ઍપ માટે ફૉર્મેટ બદલવાની પ્રક્રિયા ચાલુ કરો"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ચાલુ સેવાઓ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"હાલમાં ચાલતી સેવાઓ જુઓ અને નિયંત્રિત કરો"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView અમલીકરણ"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ચાર્જ થવામાં <xliff:g id="TIME">%1$s</xliff:g> બાકી છે"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ચાર્જ થવા માટે <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - બૅટરીનો વપરાશ હંગામી રૂપે મર્યાદિત છે"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"અજાણ્યું"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ચાર્જ થઈ રહ્યું છે"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 151b861..d9153a1 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"चार्ज पूरा होने में <xliff:g id="TIME">%1$s</xliff:g> बचा है"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> में पूरा चार्ज हो जाएगा"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - कुछ समय के लिए, बैटरी का सीमित इस्तेमाल होगा"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हो रही है"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index d37879f..b51e096 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napunit će se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napunit će se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Baterija je privremeno ograničena"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Punjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 4ac5308..33770fc 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> van hátra a feltöltésből"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Akkumulátor ideiglenesen korlátozva"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ismeretlen"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Töltés"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index b0734fd..06d70f8 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Մարտկոցը ժամանակավորապես սահմանափակված է"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 9db21c7..4123d9e 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Sisa <xliff:g id="TIME">%1$s</xliff:g> hingga terisi penuh"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lagi terisi penuh"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Daya baterai terbatas untuk sementara"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengisi daya"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d9ee03a..3b9bef8 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> að fullri hleðslu"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> að fullri hleðslu"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Rafhlaða takmörkuð tímabundið"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 57378e7..1ab6b24 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -66,7 +66,7 @@
     <string name="bluetooth_disconnected" msgid="7739366554710388701">"Disconnesso"</string>
     <string name="bluetooth_disconnecting" msgid="7638892134401574338">"Disconnessione…"</string>
     <string name="bluetooth_connecting" msgid="5871702668260192755">"Connessione…"</string>
-    <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso"</string>
+    <string name="bluetooth_connected" msgid="8065345572198502293">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> Connesso"</string>
     <string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string>
     <string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string>
     <string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index fd53f14..15868d5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"אפליקציה לא פעילה. הקש כדי להחליף מצב."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"אפליקציה פעילה. הקש כדי להחליף מצב."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"אפליקציה במצב המתנה:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"הגדרות של המרת קידוד למדיה"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"השבתה של המרת קידוד"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"הפעלה של המרת קידוד לאפליקציות"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"שירותים פועלים"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"הצגת השירותים הפועלים כעת ושליטה בהם"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"‏יישום WebView"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"נשארו <xliff:g id="TIME">%1$s</xliff:g> עד הטעינה"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> עד הטעינה"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - הסוללה מוגבלת באופן זמני"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"לא ידוע"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"בטעינה"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index c0aa74b..89ee98e 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"充電完了まであと <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 充電完了まで <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 電池の使用が一時的に制限されています"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 09f3ff5..2bc1c4d 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысы уақытша шектелген"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 42e6a53..7306cf8 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ទៀតទើបសាកថ្មពេញ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ទៀតទើប​សាកថ្មពេញ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - បានដាក់កម្រិតថ្ម​ជាបណ្ដោះអាសន្ន"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"មិន​ស្គាល់"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"កំពុងបញ្ចូល​ថ្ម"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 658b987..0c1502c 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿ ತಾತ್ಕಾಲಿಕವಾಗಿ ಸೀಮಿತವಾಗಿದೆ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 2b0843d..1322344 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"충전 완료까지 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 충전 완료까지 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 일시적으로 배터리 사용 제한"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"알 수 없음"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"충전 중"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 499fd88..9697aa7 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> кийин толук кубатталып бүтөт"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> кийин толук кубатталып бүтөт"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяны колдонуу убактлуу чектелген"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгисиз"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Кубатталууда"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index a5be601..7201222 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ບໍ່ໄດ້ນຳໃຊ້. ແຕະບໍ່ສັບປ່ຽນ."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ນຳໃຊ້ຢູ່. ແຕະເພື່ອສັບປ່ຽນ."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ສະຖານະສະແຕນບາຍແອັບ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດມີເດຍ"</string>
-    <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດ"</string>
-    <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການ​ປ່ຽນ​ຮູບ​ແບບ​ລະ​ຫັດສຳລັບແອັບ"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ການຕັ້ງຄ່າການປ່ຽນຮູບແບບລະຫັດມີເດຍ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ປິດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ເປີດການນຳໃຊ້ການປ່ຽນຮູບແບບລະຫັດສຳລັບແອັບ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ບໍລິການທີ່ເຮັດວຽກຢູ່"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ເບິ່ງ ແລະຈັດການບໍລິການທີ່ກຳລັງເຮັດວຽກຢູ່ໃນປັດຈຸບັນ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ການຈັດຕັ້ງປະຕິບັດ WebView"</string>
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈົນກວ່າຈະສາກເຕັມ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ຈຳກັດແບັດເຕີຣີຊົ່ວຄາວ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ບໍ່ຮູ້ຈັກ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ກຳລັງສາກໄຟ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 181fb10..e62eeb2 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Iki visiškos įkrovos liko <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – iki visiškos įkrovos liko <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – akumuliatorius laikinai apribotas"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nežinomas"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Kraunasi..."</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 717e2b0..2f30561 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Vēl <xliff:g id="TIME">%1$s</xliff:g> līdz pilnai uzlādei"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>, akumulatora uzlāde pagaidām ierobežota"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nezināms"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Uzlāde"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 2ccb150..1936fe2 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Уште <xliff:g id="TIME">%1$s</xliff:g> до целосно полнење"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> до целосно полнење"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батеријата е привремено ограничена"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Се полни"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 768ad5e..de4a49a 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"നിഷ്‌ക്രിയം. മാറ്റുന്നതിനു ടാപ്പുചെയ്യുക."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"സജീവം. മാറ്റുന്നതിന് ടാപ്പുചെയ്യുക."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ആപ്പ് സ്‌റ്റാൻഡ്‌ബൈ നില:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"മീഡിയ ട്രാൻസ്കോഡ് ചെയ്യൽ ക്രമീകരണം"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ട്രാൻസ്കോഡ് ചെയ്യൽ പ്രവർത്തനരഹിതമാക്കുക"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ആപ്പുകൾക്കായി ട്രാൻസ്കോഡ് ചെയ്യുന്നത് പ്രവർത്തനക്ഷമമാക്കുക"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"നിലവിൽ പ്രവർത്തിക്കുന്ന സേവനങ്ങൾ കാണുക, നിയന്ത്രിക്കുക"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView നടപ്പാക്കൽ"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായി ചാർജാവാൻ <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ബാറ്ററി താൽക്കാലം പരിമിതപ്പെടുത്തി"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"അജ്ഞാതം"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ചാർജ് ചെയ്യുന്നു"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 61ebca5..dee8add 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейг түр хугацаанд хязгаарласан"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index ef319c3..60ad68e 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय. टॉगल करण्यासाठी टॅप करा."</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय. टॉगल करण्यासाठी टॅप करा."</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"अ‍ॅप स्टँडबाय स्थिती: <xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"मीडिया ट्रान्सकोडिंगची सेटिंग्ज"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिंग बंद करा"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ॲप्ससाठी ट्रान्सकोडिंग सुरू करा"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"सुरू सेवा"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"सध्या सुरू असलेल्या सेवा पहा आणि नियंत्रित करा"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"वेबदृश्य अंमलबजावणी"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पर्यंत पूर्ण चार्ज होईल"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - बॅटरी तात्पुरती मर्यादित आहे"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज होत आहे"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 83c0384..e4d5912 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> lagi sehingga dicas penuh"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga dicas"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateri terhad untuk sementara"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Mengecas"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index f398487..09a8bf8 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"အားပြည့်ရန် <xliff:g id="TIME">%1$s</xliff:g> ကျန်သည်"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - အားပြည့်ရန် <xliff:g id="TIME">%2$s</xliff:g> ကျန်သည်"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ဘက်ထရီ ယာယီကန့်သတ်ထားသည်"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"မသိ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"အားသွင်းနေပါသည်"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0ef5809..d1af289 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batteriet er midlertidig begrenset"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index b956e21..0c6114b 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"निष्क्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"सक्रिय। टगल गर्न ट्याप गर्नुहोस्।"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"एपको स्ट्यान्डबाई अवस्था:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"मिडिया ट्रान्सकोडिङ सेटिङ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ट्रान्सकोडिङ अफ गर्नुहोस्"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"एपहरूमा ट्रान्सकोडिङ अन गर्नुहोस्"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"चलिरहेका सेवाहरू"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"हाल चालु भइरहेका सेवाहरू हेर्नुहोस् र नियन्त्रण गर्नुहोस्"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView कार्यान्वयन"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"पूर्ण चार्ज हुन <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"पूर्ण चार्ज हुन <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> लाग्छ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - केही समयका लागि ब्याट्री प्रयोग सीमित गरिएको छ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"चार्ज हुँदै"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 3223857..a409756 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Nog <xliff:g id="TIME">%1$s</xliff:g> tot opgeladen"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot opgeladen"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Batterij tijdelijk beperkt"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Opladen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 11bb550..0a0eeff 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ନିଷ୍କ୍ରିୟ। ଟୋଗଲ୍‌ କରିବାକୁ ଟାପ୍‌ କରନ୍ତୁ।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ସକ୍ରିୟ। ବଦଳାଇବା ପାଇଁ ଟାପ୍‌ କରନ୍ତୁ"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ଆପ୍ ଷ୍ଟାଣ୍ଡବାଏ ଅବସ୍ଥା:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ମିଡିଆ ଟ୍ରାନ୍ସକୋଡିଂ ସେଟିଂସ୍"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ଟ୍ରାନ୍ସକୋଡିଂ ଅକ୍ଷମ କରନ୍ତୁ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ଆପଗୁଡ଼ିକ ପାଇଁ ଟ୍ରାନ୍ସକୋଡିଂ ସକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ଏବେ ଚାଲୁଥିବା ସେବାଗୁଡ଼ିକୁ ଦେଖନ୍ତୁ ଓ ନିୟନ୍ତ୍ରଣ କରନ୍ତୁ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"ୱେବ୍‌ଭ୍ୟୁ ପ୍ରୟୋଗ"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ଚାର୍ଜ ହେବା ପାଇଁ <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ଚାର୍ଜ ହେବା ପର୍ଯ୍ୟନ୍ତ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ବ୍ୟାଟେରୀ ଅସ୍ଥାୟୀ ଭାବେ ସୀମିତ ଅଛି"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ଅଜ୍ଞାତ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ଚାର୍ଜ ହେଉଛି"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 8d66f3c..c9c37d9 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"ਅਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"ਕਿਰਿਆਸ਼ੀਲ। ਟੌਗਲ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ਐਪ ਸਟੈਂਡਬਾਈ ਸਥਿਤੀ:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"ਮੀਡੀਆ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਸੈਟਿੰਗਾਂ"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਬੰਦ ਕਰੋ"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ਐਪਾਂ ਲਈ ਟ੍ਰਾਂਸਕੋਡਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"ਇਸ ਵੇਲੇ ਚੱਲ ਰਹੀਆਂ ਸੇਵਾਵਾਂ ਦੇਖੋ ਅਤੇ ਇਹਨਾਂ ਨੂੰ ਕੰਟਰੋਲ ਕਰੋ"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView ਅਮਲ"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਕੁਝ ਸਮੇਂ ਲਈ ਸੀਮਤ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index ed96c46..e894c2c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – bateria tymczasowo ograniczona"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 46d6d0a..57a0454 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> până la încărcare"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> până la încărcare"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterie limitată temporar"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Necunoscut"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Se încarcă"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 2a79ad3..09b8de0 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до полной зарядки"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> • Уровень заряда временно ограничен"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Идет зарядка"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 78f3558..d36140f 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරිය තාවකාලිකව සීමිතයි"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 152c7a8..096e95e 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Zostávajúci čas do úplného nabitia: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batéria je dočasne obmedzená"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznáme"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nabíja sa"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index a2f093e..345fa36 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – baterija je začasno omejena"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index b458917..3953a6a4f 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura deri në karikim"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të karikohet"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Bateria e kufizuar përkohësisht"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"I panjohur"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Po karikohet"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Po ngarkon me shpejtësi"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 970c70f..70b2879 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – батерија је тренутно ограничена"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 42d9077..f466e88 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> kvar till full laddning"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till full laddning"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – batteriet är tillfälligt begränsat"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Okänd"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Laddar"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index f1a4199..e1060fec 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Imebakisha <xliff:g id="TIME">%1$s</xliff:g> ijae chaji"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ijae chaji"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Betri imedhibitiwa kwa muda"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Haijulikani"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Inachaji"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 2d696c5..ba98d79 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"முழு சார்ஜாக <xliff:g id="TIME">%1$s</xliff:g> ஆகும்"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - முழு சார்ஜாக <xliff:g id="TIME">%2$s</xliff:g> ஆகும்"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g>-பேட்டரி தற்காலிகக் கட்டுப்பாட்டிலுள்ளது"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"அறியப்படாத"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"சார்ஜ் ஆகிறது"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 8e0f0b6..0b93979 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> -బ్యాటరీ తాత్కాలికంగా పరిమితం చేయబడింది"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 81895b9..a31e146 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"เหลือ <xliff:g id="TIME">%1$s</xliff:g> จนกว่าจะชาร์จเต็ม"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะชาร์จ"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - การชาร์จแบตเตอรี่จำกัดชั่วคราว"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ไม่ทราบ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"กำลังชาร์จ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 5d1630f..bbb7779 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ang natitira bago matapos mag-charge"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> hanggang matapos mag-charge"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pansamantalang limitado ang baterya"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Hindi Kilala"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Nagcha-charge"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 34c96a8..fba7f41 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Şarj olmaya <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - şarj olmaya <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Pil geçici olarak sınırlı"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index de2456c..b633752 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – дані акумулятора тимчасово недоступні"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 7842ba9..b4d8ad2 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -399,12 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"غیر فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"فعال۔ ٹوگل کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"ایپ اسٹینڈ بائی کی حالت:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <!-- no translation found for transcode_settings_title (2581975870429850549) -->
-    <skip />
-    <!-- no translation found for transcode_enable_all (9102460144086871903) -->
-    <skip />
-    <!-- no translation found for transcode_skip_apps (8249721984597390142) -->
-    <skip />
+    <string name="transcode_settings_title" msgid="2581975870429850549">"میڈیا ٹرانسکوڈنگ کی ترتیبات"</string>
+    <string name="transcode_enable_all" msgid="9102460144086871903">"ٹرانسکوڈنگ غیر فعال کریں"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"ایپس کے لئے ٹرانسکوڈنگ فعال کریں"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"چل رہی سروسز"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"فی الحال چل رہی سروسز دیکھیں اور انہیں کنٹرول کریں"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"‏WebView کا نفاذ"</string>
@@ -452,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"چارج ہونے میں <xliff:g id="TIME">%1$s</xliff:g> باقی"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> چارج ہونے تک"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - بیٹری عارضی طور پر محدود ہے"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"نامعلوم"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"چارج ہو رہا ہے"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 9457d90..5bb422e 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quvvat darajasi vaqtincha cheklangan"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 235987c..0acf941 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Còn <xliff:g id="TIME">%1$s</xliff:g> nữa là sạc đầy"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là sạc đầy"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> – Thời lượng pin bị hạn chế tạm thời"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 46188e9..ca2ad87 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"还剩 <xliff:g id="TIME">%1$s</xliff:g>充满电"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>后充满电"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暂时限用电池"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"正在充电"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 6f5c025..16ca19d 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -399,9 +399,9 @@
     <string name="inactive_app_inactive_summary" msgid="3161222402614236260">"未啟用。輕按即可切換。"</string>
     <string name="inactive_app_active_summary" msgid="8047630990208722344">"已啟用。輕按即可切換。"</string>
     <string name="standby_bucket_summary" msgid="5128193447550429600">"備用應用程式狀態:<xliff:g id="BUCKET"> %s</xliff:g>"</string>
-    <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼功能設定"</string>
+    <string name="transcode_settings_title" msgid="2581975870429850549">"媒體轉碼設定"</string>
     <string name="transcode_enable_all" msgid="9102460144086871903">"停用轉碼功能"</string>
-    <string name="transcode_skip_apps" msgid="8249721984597390142">"替應用程式啟用轉碼功能"</string>
+    <string name="transcode_skip_apps" msgid="8249721984597390142">"為應用程式啟用轉碼功能"</string>
     <string name="runningservices_settings_title" msgid="6460099290493086515">"執行中的服務"</string>
     <string name="runningservices_settings_summary" msgid="1046080643262665743">"查看並控制目前正在執行中的服務"</string>
     <string name="select_webview_provider_title" msgid="3917815648099445503">"WebView 設置"</string>
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池充電"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index f966df6..810119b 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - 暫時限制電池用量"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 8ad37d3..23beeb1 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -449,8 +449,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> esele ize ishaje"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ize igcwale"</string>
-    <!-- no translation found for power_charging_limited (5902301801611726210) -->
-    <skip />
+    <string name="power_charging_limited" msgid="5902301801611726210">"<xliff:g id="LEVEL">%1$s</xliff:g> - Ibhethri ikhawulelwe okwesikhashana"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Akwaziwa"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Iyashaja"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 9f16d03..ac20ee1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -408,7 +408,8 @@
     }
 
     /**
-     * Checks if an admin has enforced minimum password quality requirements on the given user.
+     * Checks if an admin has enforced minimum password quality or complexity requirements on the
+     * given user.
      *
      * @return EnforcedAdmin Object containing the enforced admin component and admin user details,
      * or {@code null} if no quality requirements are set. If the requirements are set by
@@ -428,6 +429,30 @@
         }
 
         LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
+        final int aggregatedComplexity = dpm.getAggregatedPasswordComplexityForUser(userId);
+        if (aggregatedComplexity > DevicePolicyManager.PASSWORD_COMPLEXITY_NONE) {
+            // First, check if there's a Device Owner. If so, then only it can apply password
+            // complexity requiremnts (there can be no secondary profiles).
+            final UserHandle deviceOwnerUser = dpm.getDeviceOwnerUser();
+            if (deviceOwnerUser != null) {
+                return new EnforcedAdmin(dpm.getDeviceOwnerComponentOnAnyUser(), deviceOwnerUser);
+            }
+
+            // The complexity could be enforced by a Profile Owner - either in the current user
+            // or the current user is the parent user that is affected by the profile owner.
+            for (UserInfo userInfo : UserManager.get(context).getProfiles(userId)) {
+                final ComponentName profileOwnerComponent = dpm.getProfileOwnerAsUser(userInfo.id);
+                if (profileOwnerComponent != null) {
+                    return new EnforcedAdmin(profileOwnerComponent, getUserHandleOf(userInfo.id));
+                }
+            }
+
+            // Should not get here: A Device Owner or Profile Owner should be found.
+            throw new IllegalStateException(
+                    String.format("Could not find admin enforcing complexity %d for user %d",
+                            aggregatedComplexity, userId));
+        }
+
         if (sProxy.isSeparateProfileChallengeEnabled(lockPatternUtils, userId)) {
             // userId is managed profile and has a separate challenge, only consider
             // the admins in that user.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 59d8acb..8fd1910 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -49,6 +49,7 @@
  */
 public class BluetoothEventManager {
     private static final String TAG = "BluetoothEventManager";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final LocalBluetoothAdapter mLocalAdapter;
     private final CachedBluetoothDeviceManager mDeviceManager;
@@ -366,6 +367,9 @@
          *               BluetoothDevice.UNBOND_REASON_*
          */
         private void showUnbondMessage(Context context, String name, int reason) {
+            if (DEBUG) {
+                Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason);
+            }
             int errorMsg;
 
             switch (reason) {
@@ -382,6 +386,7 @@
                 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
                 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
                 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
+                case BluetoothDevice.UNBOND_REASON_REMOVED:
                     errorMsg = R.string.bluetooth_pairing_error_message;
                     break;
                 default:
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index ba1dc64..6a4d650 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -35,6 +35,8 @@
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
 
+import com.android.settingslib.R;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,6 +51,8 @@
 @RunWith(RobolectricTestRunner.class)
 public class BluetoothEventManagerTest {
 
+    private static final String DEVICE_NAME = "test_device_name";
+
     @Mock
     private LocalBluetoothAdapter mLocalAdapter;
     @Mock
@@ -71,6 +75,8 @@
     private BluetoothDevice mDevice2;
     @Mock
     private LocalBluetoothProfileManager mLocalProfileManager;
+    @Mock
+    private BluetoothUtils.ErrorListener mErrorListener;
 
     private Context mContext;
     private Intent mIntent;
@@ -92,6 +98,7 @@
 
         mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
         mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
+        BluetoothUtils.setErrorListener(mErrorListener);
     }
 
     @Test
@@ -344,4 +351,80 @@
         assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
         assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
     }
+
+    @Test
+    public void showUnbondMessage_reasonRemoved_showCorrectedErrorCode() {
+        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_REMOVED);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
+                eq(R.string.bluetooth_pairing_error_message));
+    }
+
+    @Test
+    public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
+        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
+                eq(R.string.bluetooth_pairing_error_message));
+    }
+
+    @Test
+    public void showUnbondMessage_reasonRemoteDeviceDown_showCorrectedErrorCode() {
+        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        mIntent.putExtra(BluetoothDevice.EXTRA_REASON,
+                BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
+                eq(R.string.bluetooth_pairing_device_down_error_message));
+    }
+
+    @Test
+    public void showUnbondMessage_reasonAuthRejected_showCorrectedErrorCode() {
+        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_REJECTED);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
+                eq(R.string.bluetooth_pairing_rejected_error_message));
+    }
+
+    @Test
+    public void showUnbondMessage_reasonAuthFailed_showCorrectedErrorCode() {
+        mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+        mIntent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE);
+        mIntent.putExtra(BluetoothDevice.EXTRA_REASON, BluetoothDevice.UNBOND_REASON_AUTH_FAILED);
+        when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice1);
+        when(mCachedDevice1.getName()).thenReturn(DEVICE_NAME);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mErrorListener).onShowError(any(Context.class), eq(DEVICE_NAME),
+                eq(R.string.bluetooth_pairing_pin_error_message));
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
index 5e5a9d9..c33f02df 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
@@ -82,6 +82,7 @@
     private static PendingIntent getPendingActivity(Context context) {
         Intent intent = new Intent("com.android.settings.WIFI_TETHER_SETTINGS")
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+        return PendingIntent.getActivity(context, 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index af12ddd..48c0dc4 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -319,7 +319,6 @@
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS,
                     Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST,
                     Settings.Global.LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST,
-                    Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                     Settings.Global.LOCATION_SETTINGS_LINK_TO_PERMISSIONS_ENABLED,
                     Settings.Global.LOCK_SOUND,
                     Settings.Global.LOOPER_STATS,
@@ -576,8 +575,6 @@
                     Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
                     Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
                     Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
-                    Settings.Global.ISOLATED_STORAGE_LOCAL,
-                    Settings.Global.ISOLATED_STORAGE_REMOTE,
                     Settings.Global.APPOP_HISTORY_PARAMETERS,
                     Settings.Global.APPOP_HISTORY_MODE,
                     Settings.Global.APPOP_HISTORY_INTERVAL_MULTIPLIER,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2a699ea..2e3ea24 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -70,6 +70,7 @@
     <uses-permission android:name="android.permission.SET_PROCESS_LIMIT" />
     <uses-permission android:name="android.permission.SET_ALWAYS_FINISH" />
     <uses-permission android:name="android.permission.DUMP" />
+    <uses-permission android:name="android.permission.CONTROL_UI_TRACING" />
     <uses-permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES" />
     <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
     <!-- Internal permissions granted to the shell. -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7120cc2..52b41a4 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -530,6 +530,14 @@
             androidprv:alwaysFocusable="true"
             android:excludeFromRecents="true" />
 
+        <!-- started from TvNotificationPanel -->
+        <activity
+            android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
+            android:excludeFromRecents="true"
+            android:launchMode="singleTask"
+            android:noHistory="true"
+            android:theme="@style/TvSidePanelTheme" />
+
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 17d2f9c..f884270 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -2,8 +2,11 @@
 
 dsandler@android.com
 
+aaliomer@google.com
 adamcohen@google.com
+alexflo@google.com
 asc@google.com
+awickham@google.com
 beverlyt@google.com
 brockman@google.com
 cinek@google.com
@@ -11,11 +14,16 @@
 dupin@google.com
 ethibodeau@google.com
 evanlaird@google.com
+gwasserman@google.com
 hwwang@google.com
 hyunyoungs@google.com
 jaggies@google.com
+jamesoleary@google.com
+jeffdq@google.com
 jjaggi@google.com
+jonmiranda@google.com
 joshmcgrath@google.com
+joshtrask@google.com
 juliacr@google.com
 juliatuttle@google.com
 kchyn@google.com
@@ -24,28 +32,38 @@
 lynhan@google.com
 madym@google.com
 mankoff@google.com
+mett@google.com
+mkephart@google.com
+mpietal@google.com
 mrcasey@google.com
 mrenouf@google.com
 nbenbernou@google.com
 nesciosquid@google.com
 ogunwale@google.com
 peanutbutter@google.com
+pinyaoting@google.com
 pixel@google.com
 roosa@google.com
+santie@google.com
 snoeberger@google.com
+sreyasr@google.com
 steell@google.com
+sfufa@google.com
 stwu@google.com
 sunnygoyal@google.com
 susikp@google.com
+thiruram@google.com
 tracyzhou@google.com
 tsuji@google.com
 twickham@google.com
+vadimt@google.com
+victortulias@google.com
 winsonc@google.com
+xuqiu@google.com
 zakcohen@google.com
 
 #Android Auto
 hseog@google.com
 
 #Android TV
-rgl@google.com
-
+rgl@google.com
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/circle_white.xml b/packages/SystemUI/res-keyguard/drawable/circle_white.xml
new file mode 100644
index 0000000..d1b2097
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/circle_white.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <solid android:color="#33FFFFFF" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b75c2c4..c82bda6 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -95,7 +95,7 @@
             android:layout_height="wrap_content"
             android:layout_gravity="center_horizontal"
             android:gravity="center_horizontal"
-            android:textSize="170dp"
+            android:textSize="180dp"
             android:letterSpacing="0.02"
             android:lineSpacingMultiplier=".8"
             android:includeFontPadding="false"
diff --git a/packages/SystemUI/res/layout/disabled_udfps_view.xml b/packages/SystemUI/res/layout/disabled_udfps_view.xml
new file mode 100644
index 0000000..aab8661
--- /dev/null
+++ b/packages/SystemUI/res/layout/disabled_udfps_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<com.android.keyguard.DisabledUdfpsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/disabled_udfps_view"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:background="@drawable/circle_white"
+/>
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 7d45de3f..30ffc32 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -1,67 +1,15 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/global_actions_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
 >
-  <com.android.systemui.globalactions.GlobalActionsFlatLayout
-      android:id="@id/global_actions_view"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:orientation="horizontal"
-      android:theme="@style/qs_theme"
-      android:clipChildren="false"
-      android:clipToPadding="false"
-      android:layout_marginStart="@dimen/global_actions_side_margin"
-  >
-    <LinearLayout
-        android:id="@android:id/list"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingTop="@dimen/global_actions_grid_vertical_padding"
-        android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
-        android:orientation="horizontal"
-        android:gravity="left | center_vertical"
-        android:translationZ="@dimen/global_actions_translate"
-    >
-      <RelativeLayout
-          android:id="@+id/global_actions_overflow_button"
-          android:contentDescription="@string/accessibility_menu"
-          android:layout_width="48dp"
-          android:layout_height="48dp"
-      >
-        <ImageView
-            android:src="@drawable/ic_more_vert"
-            android:layout_centerInParent="true"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:tint="@color/control_more_vert"
-        />
-      </RelativeLayout>
-    </LinearLayout>
-  </com.android.systemui.globalactions.GlobalActionsFlatLayout>
 
-  <androidx.constraintlayout.widget.ConstraintLayout
-      android:id="@+id/global_actions_lock_message_container"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent"
-      android:visibility="gone">
-    <TextView
-        android:id="@+id/global_actions_lock_message"
-        style="@style/TextAppearance.Control.Title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginHorizontal="@dimen/global_actions_side_margin"
-        android:drawablePadding="12dp"
-        android:gravity="center"
-        android:text="@string/global_action_lock_message"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="0.35"/>
-  </androidx.constraintlayout.widget.ConstraintLayout>
+  <include layout="@layout/global_actions_view" />
+
+  <include layout="@layout/global_actions_lock_view" />
 
   <com.android.systemui.globalactions.MinHeightScrollView
       android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/global_actions_lock_view.xml b/packages/SystemUI/res/layout/global_actions_lock_view.xml
new file mode 100644
index 0000000..eccc636
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_lock_view.xml
@@ -0,0 +1,35 @@
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/global_actions_lock_message_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:visibility="gone">
+  <TextView
+      android:id="@+id/global_actions_lock_message"
+      style="@style/TextAppearance.Control.Title"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_marginHorizontal="@dimen/global_actions_side_margin"
+      android:drawablePadding="12dp"
+      android:gravity="center"
+      android:text="@string/global_action_lock_message"
+      app:layout_constraintBottom_toBottomOf="parent"
+      app:layout_constraintTop_toTopOf="parent"
+      app:layout_constraintVertical_bias="0.35"/>
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_view.xml b/packages/SystemUI/res/layout/global_actions_view.xml
new file mode 100644
index 0000000..454707b
--- /dev/null
+++ b/packages/SystemUI/res/layout/global_actions_view.xml
@@ -0,0 +1,52 @@
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<com.android.systemui.globalactions.GlobalActionsFlatLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@id/global_actions_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:theme="@style/qs_theme"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:layout_marginStart="@dimen/global_actions_side_margin"
+    >
+  <LinearLayout
+      android:id="@android:id/list"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+      android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+      android:orientation="horizontal"
+      android:gravity="left | center_vertical"
+      android:translationZ="@dimen/global_actions_translate"
+      >
+    <RelativeLayout
+        android:id="@+id/global_actions_overflow_button"
+        android:contentDescription="@string/accessibility_menu"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        >
+      <ImageView
+          android:src="@drawable/ic_more_vert"
+          android:layout_centerInParent="true"
+          android:layout_width="24dp"
+          android:layout_height="24dp"
+          android:tint="@color/control_more_vert"
+          />
+    </RelativeLayout>
+  </LinearLayout>
+</com.android.systemui.globalactions.GlobalActionsFlatLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index e7c7b5f..13572fa 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -26,11 +26,19 @@
     android:layout_gravity="center_vertical|center_horizontal"
     android:background="@android:color/transparent">
 
+    <ImageView
+        android:id="@+id/primary_footer_icon"
+        android:layout_width="@dimen/qs_footer_icon_size"
+        android:layout_height="@dimen/qs_footer_icon_size"
+        android:gravity="start"
+        android:layout_marginEnd="8dp"
+        android:contentDescription="@null"
+        android:tint="?android:attr/textColorPrimary" />
+
     <com.android.systemui.util.AutoMarqueeTextView
         android:id="@+id/footer_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:gravity="start"
         android:layout_weight="1"
         android:singleLine="true"
         android:ellipsize="marquee"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
index 1a35676..3e40321 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog_parental_controls.xml
@@ -29,8 +29,8 @@
         android:orientation="vertical">
         <ImageView
             android:id="@+id/parental_controls_icon"
-            android:layout_width="36dip"
-            android:layout_height="36dip"
+            android:layout_width="24dip"
+            android:layout_height="24dip"
             android:layout_gravity="center_horizontal"
 
         />
diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml
new file mode 100644
index 0000000..711cd4e
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_notification_item.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/tv_notification_panel_width"
+    android:layout_height="wrap_content"
+    android:background="?android:attr/selectableItemBackground"
+    android:orientation="vertical"
+    android:padding="12dp">
+
+    <TextView
+        android:id="@+id/tv_notification_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingBottom="12dp"
+        android:textColor="@color/tv_notification_text_color"
+        android:textSize="18sp" />
+
+    <TextView
+        android:id="@+id/tv_notification_details"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textColor="@color/tv_notification_text_color" />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml
new file mode 100644
index 0000000..8f00a72
--- /dev/null
+++ b/packages/SystemUI/res/layout/tv_notification_panel.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tv_notification_panel"
+    android:layout_width="@dimen/tv_notification_panel_width"
+    android:layout_height="match_parent"
+    android:layout_gravity="end"
+    android:background="@color/tv_notification_background_color"
+    android:orientation="vertical">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="12dp"
+        android:paddingTop="24dp"
+        android:text="@string/tv_notification_panel_title"
+        android:textColor="@color/tv_notification_text_color"
+        android:textSize="24sp"
+        android:textStyle="bold" />
+
+    <TextView
+        android:id="@+id/no_tv_notifications"
+        style="?android:attr/titleTextStyle"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:gravity="top|center"
+        android:paddingTop="24dp"
+        android:text="@string/tv_notification_panel_no_notifications"
+        android:textColor="@color/tv_notification_text_color"
+        android:visibility="gone" />
+
+    <androidx.leanback.widget.VerticalGridView
+        android:id="@+id/notifications_list"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index ccd235d..c078805 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -1,4 +1,19 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
 <com.android.systemui.biometrics.UdfpsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 09ec439..0c8c5c4 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -29,7 +29,8 @@
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.volume.VolumeUI</item>
         <item>com.android.systemui.statusbar.tv.TvStatusBar</item>
-        <item>com.android.systemui.statusbar.tv.TvNotificationPanel</item>
+        <item>com.android.systemui.statusbar.tv.notifications.TvNotificationPanel</item>
+        <item>com.android.systemui.statusbar.tv.notifications.TvNotificationHandler</item>
         <item>com.android.systemui.statusbar.tv.VpnStatusObserver</item>
         <item>com.android.systemui.usb.StorageNotification</item>
         <item>com.android.systemui.power.PowerUI</item>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 9b0ae1d..0961f50 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -33,4 +33,7 @@
     <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color>
     <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color>
     <color name="tv_volume_dialog_accent">#FFDADCE0</color>
+
+    <color name="tv_notification_background_color">#383838</color>
+    <color name="tv_notification_text_color">#FFFFFF</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
new file mode 100644
index 0000000..9545bfd
--- /dev/null
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<resources>
+    <dimen name="tv_notification_panel_width">360dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index 13271d6..b51cb56 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -26,4 +26,6 @@
     <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] -->
     <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
 
+    <string name="tv_notification_panel_title">Notifications</string>
+    <string name="tv_notification_panel_no_notifications">No Notifications</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 0c4fd23..cb433f3 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -23,4 +23,12 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:windowDisablePreview">true</item>
      </style>
+
+    <style name="TvSidePanelTheme">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:backgroundDimEnabled">false</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index dd57af3..229d20b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
+import static android.app.ActivityTaskManager.getService;
 
 import android.annotation.NonNull;
 import android.app.Activity;
@@ -53,7 +54,6 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Consumer;
 
@@ -70,6 +70,7 @@
     // Should match the value in AssistManager
     private static final String INVOCATION_TIME_MS_KEY = "invocation_time_ms";
 
+    private final ActivityTaskManager mAtm = ActivityTaskManager.getInstance();
     private ActivityManagerWrapper() { }
 
     public static ActivityManagerWrapper getInstance() {
@@ -102,29 +103,19 @@
      */
     public ActivityManager.RunningTaskInfo getRunningTask(boolean filterOnlyVisibleRecents) {
         // Note: The set of running tasks from the system is ordered by recency
-        try {
-            List<ActivityManager.RunningTaskInfo> tasks =
-                    ActivityTaskManager.getService().getFilteredTasks(1, filterOnlyVisibleRecents);
-            if (tasks.isEmpty()) {
-                return null;
-            }
-            return tasks.get(0);
-        } catch (RemoteException e) {
+        List<ActivityManager.RunningTaskInfo> tasks =
+                mAtm.getTasks(1, filterOnlyVisibleRecents);
+        if (tasks.isEmpty()) {
             return null;
         }
+        return tasks.get(0);
     }
 
     /**
      * @return a list of the recents tasks.
      */
     public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
-        try {
-            return ActivityTaskManager.getService().getRecentTasks(numTasks,
-                            RECENT_IGNORE_UNAVAILABLE, userId).getList();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get recent tasks", e);
-            return new ArrayList<>();
-        }
+        return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId);
     }
 
     /**
@@ -133,7 +124,7 @@
     public @NonNull ThumbnailData getTaskThumbnail(int taskId, boolean isLowResolution) {
         ActivityManager.TaskSnapshot snapshot = null;
         try {
-            snapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, isLowResolution);
+            snapshot = getService().getTaskSnapshot(taskId, isLowResolution);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to retrieve task snapshot", e);
         }
@@ -149,8 +140,7 @@
      */
     public void invalidateHomeTaskSnapshot(final Activity homeActivity) {
         try {
-            ActivityTaskManager.getService().invalidateHomeTaskSnapshot(
-                    homeActivity.getActivityToken());
+            getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken());
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to invalidate home snapshot", e);
         }
@@ -208,7 +198,7 @@
                     }
                 };
             }
-            ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner);
+            getService().startRecentsActivity(intent, eventTime, runner);
             return true;
         } catch (Exception e) {
             return false;
@@ -220,7 +210,7 @@
      */
     public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) {
         try {
-            ActivityTaskManager.getService().cancelRecentsAnimation(restoreHomeRootTaskPosition);
+            getService().cancelRecentsAnimation(restoreHomeRootTaskPosition);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to cancel recents animation", e);
         }
@@ -259,7 +249,7 @@
     public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
         try {
             Bundle optsBundle = options == null ? null : options.toBundle();
-            ActivityTaskManager.getService().startActivityFromRecents(taskId, optsBundle);
+            getService().startActivityFromRecents(taskId, optsBundle);
             return true;
         } catch (Exception e) {
             return false;
@@ -296,7 +286,7 @@
      */
     public void removeTask(final int taskId) {
         try {
-            ActivityTaskManager.getService().removeTask(taskId);
+            getService().removeTask(taskId);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to remove task=" + taskId, e);
         }
@@ -307,7 +297,7 @@
      */
     public void removeAllRecentTasks() {
         try {
-            ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+            getService().removeAllVisibleRecentTasks();
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to remove all tasks", e);
         }
@@ -318,7 +308,7 @@
      */
     public boolean isScreenPinningActive() {
         try {
-            return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
+            return getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED;
         } catch (RemoteException e) {
             return false;
         }
@@ -337,7 +327,7 @@
      */
     public boolean isLockToAppActive() {
         try {
-            return ActivityTaskManager.getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
+            return getService().getLockTaskModeState() != LOCK_TASK_MODE_NONE;
         } catch (RemoteException e) {
             return false;
         }
@@ -348,7 +338,7 @@
      */
     public boolean isLockTaskKioskModeActive() {
         try {
-            return ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED;
+            return getService().getLockTaskModeState() == LOCK_TASK_MODE_LOCKED;
         } catch (RemoteException e) {
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
new file mode 100644
index 0000000..f01b67b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsController.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
+
+import android.hardware.biometrics.BiometricSourceType;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.util.ViewController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Controls when to show the DisabledUdfpsView to unlock the device on the lockscreen.
+ * If the device is not authenticated, the bouncer will show.
+ *
+ * This tap target will only show when:
+ * - User has UDFPS enrolled
+ * - UDFPS is currently unavailable see {@link KeyguardUpdateMonitor#shouldListenForUdfps}
+ */
+@SysUISingleton
+public class DisabledUdfpsController extends ViewController<DisabledUdfpsView> implements Dumpable {
+    @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @NonNull private final KeyguardViewController mKeyguardViewController;
+    @NonNull private final StatusBarStateController mStatusBarStateController;
+
+    private boolean mIsDozing;
+    private boolean mIsBouncerShowing;
+    private boolean mIsKeyguardShowing;
+    private boolean mRunningFPS;
+    private boolean mAuthenticated;
+
+    private boolean mShowButton;
+
+    public DisabledUdfpsController(
+            @NonNull DisabledUdfpsView view,
+            @NonNull StatusBarStateController statusBarStateController,
+            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+            @NonNull AuthController authController,
+            @NonNull KeyguardViewController keyguardViewController
+    ) {
+        super(view);
+        mView.setOnClickListener(mOnClickListener);
+        mView.setSensorProperties(authController.getUdfpsProps().get(0));
+
+        mStatusBarStateController = statusBarStateController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardViewController = keyguardViewController;
+    }
+
+    @Override
+    protected void onViewAttached() {
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+        mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
+
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
+        mIsKeyguardShowing = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
+        mIsDozing = mStatusBarStateController.isDozing();
+        mAuthenticated = false;
+    }
+
+    @Override
+    protected void onViewDetached() {
+        mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
+        mStatusBarStateController.removeCallback(mStatusBarStateListener);
+    }
+
+    private void updateButtonVisibility() {
+        mShowButton = !mAuthenticated && !mIsDozing && mIsKeyguardShowing
+                && !mIsBouncerShowing && !mRunningFPS;
+        if (mShowButton) {
+            mView.setVisibility(View.VISIBLE);
+        } else {
+            mView.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("DisabledUdfpsController state:");
+        pw.println("  mShowBouncerButton: " + mShowButton);
+        pw.println("  mIsDozing: " + mIsDozing);
+        pw.println("  mIsKeyguardShowing: " + mIsKeyguardShowing);
+        pw.println("  mIsBouncerShowing: " + mIsBouncerShowing);
+        pw.println("  mRunningFPS: " + mRunningFPS);
+        pw.println("  mAuthenticated: " + mAuthenticated);
+    }
+
+    private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mKeyguardViewController.showBouncer(/* scrim */ true);
+        }
+    };
+
+    private StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    mIsKeyguardShowing = newState == StatusBarState.KEYGUARD;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onDozingChanged(boolean isDozing) {
+                    mIsDozing = isDozing;
+                    updateButtonVisibility();
+                }
+            };
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+            new KeyguardUpdateMonitorCallback() {
+                @Override
+                public void onKeyguardBouncerChanged(boolean bouncer) {
+                    mIsBouncerShowing = bouncer;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onBiometricRunningStateChanged(boolean running,
+                        BiometricSourceType biometricSourceType) {
+                    mRunningFPS = running && biometricSourceType == FINGERPRINT;
+                    updateButtonVisibility();
+                }
+
+                @Override
+                public void onUserUnlocked() {
+                    mAuthenticated = true;
+                    updateButtonVisibility();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java
new file mode 100644
index 0000000..d8ab780
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/DisabledUdfpsView.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 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.keyguard;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.RectF;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.util.AttributeSet;
+import android.view.Surface;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+/**
+ * A full screen view with an oval target where the UDFPS sensor is.
+ * Controlled by {@link DisabledUdfpsController}.
+ */
+public class DisabledUdfpsView extends Button {
+    @NonNull private final RectF mSensorRect;
+    @NonNull private final Context mContext;
+
+    // Used to obtain the sensor location.
+    @NonNull private FingerprintSensorPropertiesInternal mSensorProps;
+
+    public DisabledUdfpsView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mSensorRect = new RectF();
+    }
+
+    public void setSensorProperties(@NonNull FingerprintSensorPropertiesInternal properties) {
+        mSensorProps = properties;
+    }
+
+    // The "h" and "w" are the display's height and width relative to its current rotation.
+    private void updateSensorRect(int h, int w) {
+        // mSensorProps coordinates assume portrait mode.
+        mSensorRect.set(mSensorProps.sensorLocationX - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY - mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationX + mSensorProps.sensorRadius,
+                mSensorProps.sensorLocationY + mSensorProps.sensorRadius);
+
+        // Transform mSensorRect if the device is in landscape mode.
+        switch (mContext.getDisplay().getRotation()) {
+            case Surface.ROTATION_90:
+                mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom,
+                        h - mSensorRect.left);
+                break;
+            case Surface.ROTATION_270:
+                mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top,
+                        mSensorRect.right);
+                break;
+            default:
+                // Do nothing to stay in portrait mode.
+        }
+
+        setX(mSensorRect.left);
+        setY(mSensorRect.top);
+        setLayoutParams(new FrameLayout.LayoutParams(
+                (int) (mSensorRect.right - mSensorRect.left),
+                (int) (mSensorRect.bottom - mSensorRect.top)));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        // Always re-compute the layout regardless of whether "changed" is true. It is usually false
+        // when the device goes from landscape to seascape and vice versa, but mSensorRect and
+        // its dependencies need to be recalculated to stay at the same physical location on the
+        // screen.
+        final int w = getLayoutParams().width;
+        final int h = getLayoutParams().height;
+        updateSensorRect(h, w);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index a32cd14..3cbab8e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -244,7 +244,7 @@
                     iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize);
                 }
             }
-            button.setCompoundDrawables(iconDrawable, null, null, null);
+            button.setCompoundDrawablesRelative(iconDrawable, null, null, null);
             button.setOnClickListener(mOnClickListener);
             button.setClickable(pendingIntent != null);
         }
@@ -536,9 +536,9 @@
         }
 
         @Override
-        public void setCompoundDrawables(Drawable left, Drawable top, Drawable right,
+        public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end,
                 Drawable bottom) {
-            super.setCompoundDrawables(left, top, right, bottom);
+            super.setCompoundDrawablesRelative(start, top, end, bottom);
             updateDrawableColors();
             updatePadding();
         }
@@ -558,9 +558,9 @@
         public void setLockScreenMode(int mode) {
             mLockScreenMode = mode;
             if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
-                setGravity(Gravity.START);
+                setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START);
             } else {
-                setGravity(Gravity.CENTER);
+                setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
             }
             updatePadding();
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 611131f..a7e5195 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1925,6 +1925,7 @@
         }
 
         // TODO: Add support for multiple fingerprint sensors, b/173730729
+        updateUdfpsEnrolled(getCurrentUser());
         boolean shouldListenForFingerprint =
                 isUdfpsEnrolled() ? shouldListenForUdfps() : shouldListenForFingerprint();
         boolean runningOrRestarting = mFingerprintRunningState == BIOMETRIC_STATE_RUNNING
@@ -2137,7 +2138,6 @@
         }
         if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
         int userId = getCurrentUser();
-        updateUdfpsEnrolled(userId);
         if (isUnlockWithFingerprintPossible(userId)) {
             if (mFingerprintCancelSignal != null) {
                 mFingerprintCancelSignal.cancel();
@@ -2627,7 +2627,6 @@
         Assert.isMainThread();
         if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncerVisible + ")");
         mBouncer = bouncerVisible == 1;
-
         if (mBouncer) {
             // If the bouncer is shown, always clear this flag. This can happen in the following
             // situations: 1) Default camera with SHOW_WHEN_LOCKED is not chosen yet. 2) Secure
diff --git a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
index f5e01de..0d41a2f 100644
--- a/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
+++ b/packages/SystemUI/src/com/android/keyguard/TextInterpolator.kt
@@ -19,8 +19,9 @@
 import android.graphics.Paint
 import android.graphics.fonts.Font
 import android.graphics.text.PositionedGlyphs
-import android.graphics.text.TextRunShaper
 import android.text.Layout
+import android.text.TextPaint
+import android.text.TextShaper
 import android.util.MathUtils
 import com.android.internal.graphics.ColorUtils
 import java.lang.Math.max
@@ -57,10 +58,10 @@
      */
     val targetPaint = createDefaultPaint(layout.paint, lines)
 
-    private fun createDefaultPaint(paint: Paint, lines: Int): ArrayList<Paint> {
-        val paintList = ArrayList<Paint>()
+    private fun createDefaultPaint(paint: TextPaint, lines: Int): ArrayList<TextPaint> {
+        val paintList = ArrayList<TextPaint>()
         for (i in 0 until lines)
-            paintList.add(Paint(paint))
+            paintList.add(TextPaint(paint))
         return paintList
     }
 
@@ -79,9 +80,9 @@
     }
 
     /**
-     * A class represents text layout of a single line.
+     * A class represents text layout of a single run.
      */
-    private class Line(
+    private class Run(
         val glyphIds: IntArray,
         val baseX: FloatArray, // same length as glyphIds
         val baseY: FloatArray, // same length as glyphIds
@@ -90,11 +91,18 @@
         val fontRuns: List<FontRun>
     )
 
+    /**
+     * A class represents text layout of a single line.
+     */
+    private class Line(
+        val runs: List<Run>
+    )
+
     private var lines = listOf<Line>()
     private val fontInterpolator = FontInterpolator()
 
     // Recycling object for glyph drawing. Will be extended for the longest font run if needed.
-    private val tmpDrawPaints = ArrayList<Paint>()
+    private val tmpDrawPaints = ArrayList<TextPaint>()
     private var tmpPositionArray = FloatArray(20)
 
     /**
@@ -215,12 +223,14 @@
         }
 
         lines.forEach { line ->
-            for (i in line.baseX.indices) {
-                line.baseX[i] = MathUtils.lerp(line.baseX[i], line.targetX[i], progress)
-                line.baseY[i] = MathUtils.lerp(line.baseY[i], line.targetY[i], progress)
-            }
-            line.fontRuns.forEach {
-                it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress)
+            line.runs.forEach { run ->
+                for (i in run.baseX.indices) {
+                    run.baseX[i] = MathUtils.lerp(run.baseX[i], run.targetX[i], progress)
+                    run.baseY[i] = MathUtils.lerp(run.baseY[i], run.targetY[i], progress)
+                }
+                run.fontRuns.forEach {
+                    it.baseFont = fontInterpolator.lerp(it.baseFont, it.targetFont, progress)
+                }
             }
         }
 
@@ -228,10 +238,10 @@
     }
 
     companion object {
-        fun updatePaint(toUpdate: ArrayList<Paint>, newValues: ArrayList<Paint>) {
+        fun updatePaint(toUpdate: ArrayList<TextPaint>, newValues: ArrayList<TextPaint>) {
             toUpdate.clear()
             for (paint in newValues)
-                toUpdate.add(Paint(paint))
+                toUpdate.add(TextPaint(paint))
         }
     }
 
@@ -243,20 +253,22 @@
     fun draw(canvas: Canvas) {
         lerp(basePaint, targetPaint, progress, tmpDrawPaints)
         lines.forEachIndexed { lineNo, line ->
-            canvas.save()
-            try {
-                // Move to drawing origin.
-                val origin = layout.getDrawOrigin(lineNo)
-                canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
+            line.runs.forEach { run ->
+                canvas.save()
+                try {
+                    // Move to drawing origin.
+                    val origin = layout.getDrawOrigin(lineNo)
+                    canvas.translate(origin, layout.getLineBaseline(lineNo).toFloat())
 
-                line.fontRuns.forEach { run ->
-                    if (lineNo >= tmpDrawPaints.size)
-                        drawFontRun(canvas, line, run, tmpDrawPaints[0])
-                    else
-                        drawFontRun(canvas, line, run, tmpDrawPaints[lineNo])
+                    run.fontRuns.forEach { fontRun ->
+                        if (lineNo >= tmpDrawPaints.size)
+                            drawFontRun(canvas, run, fontRun, tmpDrawPaints[0])
+                        else
+                            drawFontRun(canvas, run, fontRun, tmpDrawPaints[lineNo])
+                    }
+                } finally {
+                    canvas.restore()
                 }
-            } finally {
-                canvas.restore()
             }
         }
     }
@@ -271,64 +283,68 @@
         }
 
         var maxRunLength = 0
-        lines = baseLayout.zip(targetLayout) { base, target ->
-            require(base.glyphCount() == target.glyphCount()) {
-                "Inconsistent glyph count at line ${lines.size}"
-            }
+        lines = baseLayout.zip(targetLayout) { baseLine, targetLine ->
+            val runs = baseLine.zip(targetLine) { base, target ->
 
-            val glyphCount = base.glyphCount()
-
-            // Good to recycle the array if the existing array can hold the new layout result.
-            val glyphIds = IntArray(glyphCount) {
-                base.getGlyphId(it).also { baseGlyphId ->
-                    require(baseGlyphId == target.getGlyphId(it)) {
-                        "Inconsistent glyph ID at $it in line ${lines.size}"
-                    }
-                }
-            }
-
-            val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
-            val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
-            val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
-            val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
-
-            // Calculate font runs
-            val fontRun = mutableListOf<FontRun>()
-            if (glyphCount != 0) {
-                var start = 0
-                var baseFont = base.getFont(start)
-                var targetFont = target.getFont(start)
-                require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
-                    "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+                require(base.glyphCount() == target.glyphCount()) {
+                    "Inconsistent glyph count at line ${lines.size}"
                 }
 
-                for (i in 1 until glyphCount) {
-                    val nextBaseFont = base.getFont(i)
-                    val nextTargetFont = target.getFont(i)
+                val glyphCount = base.glyphCount()
 
-                    if (baseFont !== nextBaseFont) {
-                        require(targetFont !== nextTargetFont) {
-                            "Base font has changed at $i but target font has not changed."
-                        }
-                        // Font transition point. push run and reset context.
-                        fontRun.add(FontRun(start, i, baseFont, targetFont))
-                        maxRunLength = max(maxRunLength, i - start)
-                        baseFont = nextBaseFont
-                        targetFont = nextTargetFont
-                        start = i
-                        require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
-                            "Cannot interpolate font at $start ($baseFont vs $targetFont)"
-                        }
-                    } else { // baseFont === nextBaseFont
-                        require(targetFont === nextTargetFont) {
-                            "Base font has not changed at $i but target font has changed."
+                // Good to recycle the array if the existing array can hold the new layout result.
+                val glyphIds = IntArray(glyphCount) {
+                    base.getGlyphId(it).also { baseGlyphId ->
+                        require(baseGlyphId == target.getGlyphId(it)) {
+                            "Inconsistent glyph ID at $it in line ${lines.size}"
                         }
                     }
                 }
-                fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
-                maxRunLength = max(maxRunLength, glyphCount - start)
+
+                val baseX = FloatArray(glyphCount) { base.getGlyphX(it) }
+                val baseY = FloatArray(glyphCount) { base.getGlyphY(it) }
+                val targetX = FloatArray(glyphCount) { target.getGlyphX(it) }
+                val targetY = FloatArray(glyphCount) { target.getGlyphY(it) }
+
+                // Calculate font runs
+                val fontRun = mutableListOf<FontRun>()
+                if (glyphCount != 0) {
+                    var start = 0
+                    var baseFont = base.getFont(start)
+                    var targetFont = target.getFont(start)
+                    require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+                        "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+                    }
+
+                    for (i in 1 until glyphCount) {
+                        val nextBaseFont = base.getFont(i)
+                        val nextTargetFont = target.getFont(i)
+
+                        if (baseFont !== nextBaseFont) {
+                            require(targetFont !== nextTargetFont) {
+                                "Base font has changed at $i but target font has not changed."
+                            }
+                            // Font transition point. push run and reset context.
+                            fontRun.add(FontRun(start, i, baseFont, targetFont))
+                            maxRunLength = max(maxRunLength, i - start)
+                            baseFont = nextBaseFont
+                            targetFont = nextTargetFont
+                            start = i
+                            require(FontInterpolator.canInterpolate(baseFont, targetFont)) {
+                                "Cannot interpolate font at $start ($baseFont vs $targetFont)"
+                            }
+                        } else { // baseFont === nextBaseFont
+                            require(targetFont === nextTargetFont) {
+                                "Base font has not changed at $i but target font has changed."
+                            }
+                        }
+                    }
+                    fontRun.add(FontRun(start, glyphCount, baseFont, targetFont))
+                    maxRunLength = max(maxRunLength, glyphCount - start)
+                }
+                Run(glyphIds, baseX, baseY, targetX, targetY, fontRun)
             }
-            Line(glyphIds, baseX, baseY, targetX, targetY, fontRun)
+            Line(runs)
         }
 
         // Update float array used for drawing.
@@ -338,7 +354,7 @@
     }
 
     // Draws single font run.
-    private fun drawFontRun(c: Canvas, line: Line, run: FontRun, paint: Paint) {
+    private fun drawFontRun(c: Canvas, line: Run, run: FontRun, paint: Paint) {
         var arrayIndex = 0
         for (i in run.start until run.end) {
             tmpPositionArray[arrayIndex++] =
@@ -358,7 +374,7 @@
     }
 
     private fun updatePositionsAndFonts(
-        layoutResult: List<PositionedGlyphs>,
+        layoutResult: List<List<PositionedGlyphs>>,
         updateBase: Boolean
     ) {
         // Update target positions with newly calculated text layout.
@@ -366,45 +382,48 @@
             "The new layout result has different line count."
         }
 
-        lines.zip(layoutResult) { line, newGlyphs ->
-            require(newGlyphs.glyphCount() == line.glyphIds.size) {
-                "The new layout has different glyph count."
-            }
-
-            line.fontRuns.forEach { run ->
-                val newFont = newGlyphs.getFont(run.start)
-                for (i in run.start until run.end) {
-                    require(newGlyphs.getGlyphId(run.start) == line.glyphIds[run.start]) {
-                        "The new layout has different glyph ID at ${run.start}"
-                    }
-                    require(newFont === newGlyphs.getFont(i)) {
-                        "The new layout has different font run." +
-                                " $newFont vs ${newGlyphs.getFont(i)} at $i"
-                    }
+        lines.zip(layoutResult) { line, runs ->
+            line.runs.zip(runs) { lineRun, newGlyphs ->
+                require(newGlyphs.glyphCount() == lineRun.glyphIds.size) {
+                    "The new layout has different glyph count."
                 }
 
-                // The passing base font and target font is already interpolatable, so just check
-                // new font can be interpolatable with base font.
-                require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
-                    "New font cannot be interpolated with existing font. $newFont, ${run.baseFont}"
+                lineRun.fontRuns.forEach { run ->
+                    val newFont = newGlyphs.getFont(run.start)
+                    for (i in run.start until run.end) {
+                        require(newGlyphs.getGlyphId(run.start) == lineRun.glyphIds[run.start]) {
+                            "The new layout has different glyph ID at ${run.start}"
+                        }
+                        require(newFont === newGlyphs.getFont(i)) {
+                            "The new layout has different font run." +
+                                    " $newFont vs ${newGlyphs.getFont(i)} at $i"
+                        }
+                    }
+
+                    // The passing base font and target font is already interpolatable, so just
+                    // check new font can be interpolatable with base font.
+                    require(FontInterpolator.canInterpolate(newFont, run.baseFont)) {
+                        "New font cannot be interpolated with existing font. $newFont," +
+                                " ${run.baseFont}"
+                    }
+
+                    if (updateBase) {
+                        run.baseFont = newFont
+                    } else {
+                        run.targetFont = newFont
+                    }
                 }
 
                 if (updateBase) {
-                    run.baseFont = newFont
+                    for (i in lineRun.baseX.indices) {
+                        lineRun.baseX[i] = newGlyphs.getGlyphX(i)
+                        lineRun.baseY[i] = newGlyphs.getGlyphY(i)
+                    }
                 } else {
-                    run.targetFont = newFont
-                }
-            }
-
-            if (updateBase) {
-                for (i in line.baseX.indices) {
-                    line.baseX[i] = newGlyphs.getGlyphX(i)
-                    line.baseY[i] = newGlyphs.getGlyphY(i)
-                }
-            } else {
-                for (i in line.baseX.indices) {
-                    line.targetX[i] = newGlyphs.getGlyphX(i)
-                    line.targetY[i] = newGlyphs.getGlyphY(i)
+                    for (i in lineRun.baseX.indices) {
+                        lineRun.targetX[i] = newGlyphs.getGlyphX(i)
+                        lineRun.targetY[i] = newGlyphs.getGlyphY(i)
+                    }
                 }
             }
         }
@@ -412,16 +431,16 @@
 
     // Linear interpolate the paint.
     private fun lerp(
-        from: ArrayList<Paint>,
-        to: ArrayList<Paint>,
+        from: ArrayList<TextPaint>,
+        to: ArrayList<TextPaint>,
         progress: Float,
-        out: ArrayList<Paint>
+        out: ArrayList<TextPaint>
     ) {
         out.clear()
         // Currently only font size & colors are interpolated.
         // TODO(172943390): Add other interpolation or support custom interpolator.
         for (index in from.indices) {
-            val paint = Paint(from[index])
+            val paint = TextPaint(from[index])
             paint.textSize = MathUtils.lerp(from[index].textSize, to[index].textSize, progress)
             paint.color = ColorUtils.blendARGB(from[index].color, to[index].color, progress)
             out.add(paint)
@@ -429,18 +448,20 @@
     }
 
     // Shape the text and stores the result to out argument.
-    private fun shapeText(layout: Layout, paints: ArrayList<Paint>): List<PositionedGlyphs> {
-        val out = mutableListOf<PositionedGlyphs>()
+    private fun shapeText(
+        layout: Layout,
+        paints: ArrayList<TextPaint>
+    ): List<List<PositionedGlyphs>> {
+        val out = mutableListOf<List<PositionedGlyphs>>()
         for (lineNo in 0 until layout.lineCount) { // Shape all lines.
             val lineStart = layout.getLineStart(lineNo)
             val count = layout.getLineEnd(lineNo) - lineStart
-            out.add(TextRunShaper.shapeTextRun(
-                    layout.text, // Styles are ignored.
-                    lineStart, count, // shape range
-                    lineStart, count, // shape context = shape range.
-                    0f, 0f, // the layout offset. Not changed.
-                    layout.getParagraphDirection(lineNo) == Layout.DIR_RIGHT_TO_LEFT,
-                    paints[lineNo])) // Use given paint instead of layout's for style interpolation.
+            val runs = mutableListOf<PositionedGlyphs>()
+            TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic,
+                    paints[lineNo]) { _, _, glyphs, _ ->
+                runs.add(glyphs)
+            }
+            out.add(runs)
         }
         return out
     }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 8e5e9ea..6b4e8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -113,8 +113,10 @@
                     .setShellCommandHandler(mWMComponent.getShellCommandHandler())
                     .setAppPairs(mWMComponent.getAppPairs());
         } else {
-            // TODO: Call on prepareSysUIComponentBuilder but not with real components.
-            builder = builder.setPip(Optional.ofNullable(null))
+            // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
+            // is separating this logic into newly creating SystemUITestsFactory.
+            builder = prepareSysUIComponentBuilder(builder, mWMComponent)
+                    .setPip(Optional.ofNullable(null))
                     .setSplitScreen(Optional.ofNullable(null))
                     .setOneHanded(Optional.ofNullable(null))
                     .setBubbles(Optional.ofNullable(null))
@@ -194,4 +196,4 @@
             AssetManager am, String modelName) {
         return new BackGestureTfClassifierProvider();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index dc841d9..9edfee7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -22,6 +22,7 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.app.TaskStackListener;
 import android.content.BroadcastReceiver;
@@ -74,7 +75,7 @@
 
     private final CommandQueue mCommandQueue;
     private final StatusBarStateController mStatusBarStateController;
-    private final IActivityTaskManager mActivityTaskManager;
+    private final ActivityTaskManager mActivityTaskManager;
     @Nullable private final FingerprintManager mFingerprintManager;
     @Nullable private final FaceManager mFaceManager;
     private final Provider<UdfpsController> mUdfpsControllerFactory;
@@ -313,7 +314,7 @@
     @Inject
     public AuthController(Context context, CommandQueue commandQueue,
             StatusBarStateController statusBarStateController,
-            IActivityTaskManager activityTaskManager,
+            ActivityTaskManager activityTaskManager,
             @Nullable FingerprintManager fingerprintManager,
             @Nullable FaceManager faceManager,
             Provider<UdfpsController> udfpsControllerFactory) {
@@ -356,12 +357,8 @@
             mUdfpsController = mUdfpsControllerFactory.get();
         }
 
-        try {
-            mTaskStackListener = new BiometricTaskStackListener();
-            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to register task stack listener", e);
-        }
+        mTaskStackListener = new BiometricTaskStackListener();
+        mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
     }
 
     @Override
@@ -413,6 +410,11 @@
         mCurrentDialog.onHelp(message);
     }
 
+    @Nullable
+    public List<FingerprintSensorPropertiesInternal> getUdfpsProps() {
+        return mUdfpsProps;
+    }
+
     private String getErrorString(int modality, int error, int vendorCode) {
         switch (modality) {
             case TYPE_FACE:
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index f6b8b4c..70a57cc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -185,8 +185,12 @@
             return true;
         }
 
-        // TODO(b/172655679): we always reject single-taps when doing a robust check for now.
-        return robustCheck;
+        // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed
+        if (robustCheck) {
+            return !mDataProvider.isJustUnlockedWithFace();
+        }
+
+        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 13ff3f5..ec4a91c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -22,6 +22,7 @@
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.screenrecord.ScreenRecordDialog;
 import com.android.systemui.settings.brightness.BrightnessDialog;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbDebuggingActivity;
 import com.android.systemui.usb.UsbDebuggingSecondaryUserActivity;
@@ -85,4 +86,10 @@
     @IntoMap
     @ClassKey(CreateUserActivity.class)
     public abstract Activity bindCreateUserActivity(CreateUserActivity activity);
+
+    /** Inject into TvNotificationPanelActivity. */
+    @Binds
+    @IntoMap
+    @ClassKey(TvNotificationPanelActivity.class)
+    public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index c3f2e18..2c06c7b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -151,6 +151,12 @@
 
     @Provides
     @Singleton
+    static ActivityTaskManager provideActivityTaskManager() {
+        return ActivityTaskManager.getInstance();
+    }
+
+    @Provides
+    @Singleton
     static IActivityTaskManager provideIActivityTaskManager() {
         return ActivityTaskManager.getService();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index c0013d8..9f6c19b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.tv.TvNotificationPanel;
 import com.android.systemui.statusbar.tv.TvStatusBar;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel;
 import com.android.systemui.theme.ThemeOverlayController;
 import com.android.systemui.toast.ToastUI;
 import com.android.systemui.util.leak.GarbageMonitor;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 4789239..5afe526 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -68,6 +68,7 @@
     private final View mRootView;
     private final TextView mFooterText;
     private final ImageView mFooterIcon;
+    private final ImageView mPrimaryFooterIcon;
     private final Context mContext;
     private final Callback mCallback = new Callback();
     private final SecurityController mSecurityController;
@@ -83,6 +84,7 @@
     private CharSequence mFooterTextContent = null;
     private int mFooterTextId;
     private int mFooterIconId;
+    private Drawable mPrimaryFooterIconDrawable;
 
     @Inject
     QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, Context context,
@@ -92,6 +94,7 @@
         mRootView.setOnClickListener(this);
         mFooterText = mRootView.findViewById(R.id.footer_text);
         mFooterIcon = mRootView.findViewById(R.id.footer_icon);
+        mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooterIconId = R.drawable.ic_info_outline;
         mContext = context;
         mMainHandler = mainHandler;
@@ -188,6 +191,18 @@
             mFooterIconId = footerIconId;
             mMainHandler.post(mUpdateIcon);
         }
+
+        // Update the primary icon
+        if (isParentalControlsEnabled) {
+            if (mPrimaryFooterIconDrawable == null) {
+                DeviceAdminInfo info = mSecurityController.getDeviceAdminInfo();
+                mPrimaryFooterIconDrawable = mSecurityController.getIcon(info);
+            }
+        } else {
+            mPrimaryFooterIconDrawable = null;
+        }
+        mMainHandler.post(mUpdatePrimaryIcon);
+
         mMainHandler.post(mUpdateDisplayState);
     }
 
@@ -532,6 +547,15 @@
         }
     };
 
+    private final Runnable mUpdatePrimaryIcon = new Runnable() {
+        @Override
+        public void run() {
+            mPrimaryFooterIcon.setVisibility(mPrimaryFooterIconDrawable != null
+                    ? View.VISIBLE : View.GONE);
+            mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable);
+        }
+    };
+
     private final Runnable mUpdateDisplayState = new Runnable() {
         @Override
         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
similarity index 62%
rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 84818ee..dbee0ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -11,19 +11,22 @@
  * 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
+ * limitations under the License.
  */
 
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.text.TextUtils;
+import android.view.NotificationHeaderView;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.internal.R;
 import com.android.internal.widget.CachingIconView;
 import com.android.internal.widget.ConversationLayout;
 import com.android.internal.widget.NotificationExpandButton;
@@ -36,35 +39,38 @@
 import java.util.Objects;
 
 /**
- * A Util to manage {@link android.view.NotificationHeaderView} objects and their redundancies.
+ * A utility to manage notification views when they are placed in a group by adjusting elements
+ * to reduce redundancies and occasionally tweak layouts to highlight the unique content.
  */
-public class NotificationHeaderUtil {
+public class NotificationGroupingUtil {
 
-    private static final TextViewComparator sTextViewComparator = new TextViewComparator();
-    private static final TextViewComparator sAppNameComparator = new AppNameComparator();
-    private static final VisibilityApplicator sVisibilityApplicator = new VisibilityApplicator();
-    private static final VisibilityApplicator sAppNameApplicator = new AppNameApplicator();
-    private static  final DataExtractor sIconExtractor = new DataExtractor() {
+    private static final TextViewComparator TEXT_VIEW_COMPARATOR = new TextViewComparator();
+    private static final TextViewComparator APP_NAME_COMPARATOR = new AppNameComparator();
+    private static final ViewComparator BADGE_COMPARATOR = new BadgeComparator();
+    private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator();
+    private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator();
+    private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator();
+    private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() {
         @Override
         public Object extractData(ExpandableNotificationRow row) {
             return row.getEntry().getSbn().getNotification();
         }
     };
-    private static final IconComparator sIconVisibilityComparator = new IconComparator() {
+    private static final IconComparator ICON_VISIBILITY_COMPARATOR = new IconComparator() {
         public boolean compare(View parent, View child, Object parentData,
                 Object childData) {
             return hasSameIcon(parentData, childData)
                     && hasSameColor(parentData, childData);
         }
     };
-    private static final IconComparator sGreyComparator = new IconComparator() {
+    private static final IconComparator GREY_COMPARATOR = new IconComparator() {
         public boolean compare(View parent, View child, Object parentData,
                 Object childData) {
             return !hasSameIcon(parentData, childData)
                     || hasSameColor(parentData, childData);
         }
     };
-    private final static ResultApplicator mGreyApplicator = new ResultApplicator() {
+    private static final ResultApplicator GREY_APPLICATOR = new ResultApplicator() {
         @Override
         public void apply(View parent, View view, boolean apply, boolean reset) {
             CachingIconView icon = view.findViewById(com.android.internal.R.id.icon);
@@ -80,87 +86,79 @@
     };
 
     private final ExpandableNotificationRow mRow;
-    private final ArrayList<HeaderProcessor> mComparators = new ArrayList<>();
+    private final ArrayList<Processor> mProcessors = new ArrayList<>();
     private final HashSet<Integer> mDividers = new HashSet<>();
 
-    public NotificationHeaderUtil(ExpandableNotificationRow row) {
+    public NotificationGroupingUtil(ExpandableNotificationRow row) {
         mRow = row;
         // To hide the icons if they are the same and the color is the same
-        mComparators.add(new HeaderProcessor(mRow,
+        mProcessors.add(new Processor(mRow,
                 com.android.internal.R.id.icon,
-                sIconExtractor,
-                sIconVisibilityComparator,
-                sVisibilityApplicator));
+                ICON_EXTRACTOR,
+                ICON_VISIBILITY_COMPARATOR,
+                VISIBILITY_APPLICATOR));
         // To grey them out the icons and expand button when the icons are not the same
-        mComparators.add(new HeaderProcessor(mRow,
-                com.android.internal.R.id.notification_header,
-                sIconExtractor,
-                sGreyComparator,
-                mGreyApplicator));
-        mComparators.add(new HeaderProcessor(mRow,
+        mProcessors.add(new Processor(mRow,
+                com.android.internal.R.id.status_bar_latest_event_content,
+                ICON_EXTRACTOR,
+                GREY_COMPARATOR,
+                GREY_APPLICATOR));
+        mProcessors.add(new Processor(mRow,
+                com.android.internal.R.id.status_bar_latest_event_content,
+                ICON_EXTRACTOR,
+                ICON_VISIBILITY_COMPARATOR,
+                LEFT_ICON_APPLICATOR));
+        mProcessors.add(new Processor(mRow,
                 com.android.internal.R.id.profile_badge,
                 null /* Extractor */,
-                new ViewComparator() {
-                    @Override
-                    public boolean compare(View parent, View child, Object parentData,
-                            Object childData) {
-                        return parent.getVisibility() != View.GONE;
-                    }
-
-                    @Override
-                    public boolean isEmpty(View view) {
-                        if (view instanceof ImageView) {
-                            return ((ImageView) view).getDrawable() == null;
-                        }
-                        return false;
-                    }
-                },
-                sVisibilityApplicator));
-        mComparators.add(new HeaderProcessor(
-                mRow,
+                BADGE_COMPARATOR,
+                VISIBILITY_APPLICATOR));
+        mProcessors.add(new Processor(mRow,
                 com.android.internal.R.id.app_name_text,
                 null,
-                sAppNameComparator,
-                sAppNameApplicator));
-        mComparators.add(HeaderProcessor.forTextView(mRow,
-                com.android.internal.R.id.header_text));
+                APP_NAME_COMPARATOR,
+                APP_NAME_APPLICATOR));
+        mProcessors.add(Processor.forTextView(mRow, com.android.internal.R.id.header_text));
         mDividers.add(com.android.internal.R.id.header_text_divider);
         mDividers.add(com.android.internal.R.id.header_text_secondary_divider);
         mDividers.add(com.android.internal.R.id.time_divider);
     }
 
-    public void updateChildrenHeaderAppearance() {
+    /**
+     * Update the appearance of the children in this group to reduce redundancies.
+     */
+    public void updateChildrenAppearance() {
         List<ExpandableNotificationRow> notificationChildren = mRow.getAttachedChildren();
-        if (notificationChildren == null) {
+        if (notificationChildren == null || !mRow.isSummaryWithChildren()) {
             return;
         }
-        // Initialize the comparators
-        for (int compI = 0; compI < mComparators.size(); compI++) {
-            mComparators.get(compI).init();
+        // Initialize the processors
+        for (int compI = 0; compI < mProcessors.size(); compI++) {
+            mProcessors.get(compI).init();
         }
 
         // Compare all notification headers
         for (int i = 0; i < notificationChildren.size(); i++) {
             ExpandableNotificationRow row = notificationChildren.get(i);
-            for (int compI = 0; compI < mComparators.size(); compI++) {
-                mComparators.get(compI).compareToHeader(row);
+            for (int compI = 0; compI < mProcessors.size(); compI++) {
+                mProcessors.get(compI).compareToGroupParent(row);
             }
         }
 
         // Apply the comparison to the row
         for (int i = 0; i < notificationChildren.size(); i++) {
             ExpandableNotificationRow row = notificationChildren.get(i);
-            for (int compI = 0; compI < mComparators.size(); compI++) {
-                mComparators.get(compI).apply(row);
+            for (int compI = 0; compI < mProcessors.size(); compI++) {
+                mProcessors.get(compI).apply(row);
             }
             // We need to sanitize the dividers since they might be off-balance now
-            sanitizeHeaderViews(row);
+            sanitizeTopLineViews(row);
         }
     }
 
-    private void sanitizeHeaderViews(ExpandableNotificationRow row) {
+    private void sanitizeTopLineViews(ExpandableNotificationRow row) {
         if (row.isSummaryWithChildren()) {
-            sanitizeHeader(row.getNotificationViewWrapper().getNotificationHeader());
+            sanitizeTopLine(row.getNotificationViewWrapper().getNotificationHeader());
             return;
         }
         final NotificationContentView layout = row.getPrivateLayout();
@@ -171,13 +169,11 @@
 
     private void sanitizeChild(View child) {
         if (child != null) {
-            ViewGroup header = child.findViewById(
-                    com.android.internal.R.id.notification_top_line);
-            sanitizeHeader(header);
+            sanitizeTopLine(child.findViewById(R.id.notification_top_line));
         }
     }
 
-    private void sanitizeHeader(ViewGroup rowHeader) {
+    private void sanitizeTopLine(ViewGroup rowHeader) {
         if (rowHeader == null) {
             return;
         }
@@ -225,28 +221,31 @@
         }
     }
 
-    public void restoreNotificationHeader(ExpandableNotificationRow row) {
-        for (int compI = 0; compI < mComparators.size(); compI++) {
-            mComparators.get(compI).apply(row, true /* reset */);
+    /**
+     * Reset the modifications to this row for removing it from the group.
+     */
+    public void restoreChildNotification(ExpandableNotificationRow row) {
+        for (int compI = 0; compI < mProcessors.size(); compI++) {
+            mProcessors.get(compI).apply(row, true /* reset */);
         }
-        sanitizeHeaderViews(row);
+        sanitizeTopLineViews(row);
     }
 
-    private static class HeaderProcessor {
+    private static class Processor {
         private final int mId;
         private final DataExtractor mExtractor;
+        private final ViewComparator mComparator;
         private final ResultApplicator mApplicator;
         private final ExpandableNotificationRow mParentRow;
         private boolean mApply;
         private View mParentView;
-        private ViewComparator mComparator;
         private Object mParentData;
 
-        public static HeaderProcessor forTextView(ExpandableNotificationRow row, int id) {
-            return new HeaderProcessor(row, id, null, sTextViewComparator, sVisibilityApplicator);
+        public static Processor forTextView(ExpandableNotificationRow row, int id) {
+            return new Processor(row, id, null, TEXT_VIEW_COMPARATOR, VISIBILITY_APPLICATOR);
         }
 
-        HeaderProcessor(ExpandableNotificationRow row, int id, DataExtractor extractor,
+        Processor(ExpandableNotificationRow row, int id, DataExtractor extractor,
                 ViewComparator comparator,
                 ResultApplicator applicator) {
             mId = id;
@@ -257,12 +256,12 @@
         }
 
         public void init() {
-            mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader()
-                    .findViewById(mId);
+            View header = mParentRow.getNotificationViewWrapper().getNotificationHeader();
+            mParentView = header == null ? null : header.findViewById(mId);
             mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow);
             mApply = !mComparator.isEmpty(mParentView);
         }
-        public void compareToHeader(ExpandableNotificationRow row) {
+        public void compareToGroupParent(ExpandableNotificationRow row) {
             if (!mApply) {
                 return;
             }
@@ -308,8 +307,8 @@
 
     private interface ViewComparator {
         /**
-         * @param parent the parent view
-         * @param child the child view
+         * @param parent the view with the given id in the group header
+         * @param child the view with the given id in the child notification
          * @param parentData optional data for the parent
          * @param childData optional data for the child
          * @return whether to views are the same
@@ -322,6 +321,21 @@
         Object extractData(ExpandableNotificationRow row);
     }
 
+    private static class BadgeComparator implements ViewComparator {
+        @Override
+        public boolean compare(View parent, View child, Object parentData, Object childData) {
+            return parent.getVisibility() != View.GONE;
+        }
+
+        @Override
+        public boolean isEmpty(View view) {
+            if (view instanceof ImageView) {
+                return ((ImageView) view).getDrawable() == null;
+            }
+            return false;
+        }
+    }
+
     private static class TextViewComparator implements ViewComparator {
         @Override
         public boolean compare(View parent, View child, Object parentData, Object childData) {
@@ -338,7 +352,7 @@
         }
     }
 
-    private static abstract class IconComparator implements ViewComparator {
+    private abstract static class IconComparator implements ViewComparator {
         @Override
         public boolean compare(View parent, View child, Object parentData, Object childData) {
             return false;
@@ -366,6 +380,12 @@
     }
 
     private interface ResultApplicator {
+        /**
+         * @param parent the root view of the child notification
+         * @param view the view with the given id in the child notification
+         * @param apply whether the state should be applied or removed
+         * @param reset if [de]application is the result of a reset
+         */
         void apply(View parent, View view, boolean apply, boolean reset);
     }
 
@@ -403,4 +423,54 @@
             return super.compare(parent, child, parentData, childData);
         }
     }
+
+    private static class LeftIconApplicator implements ResultApplicator {
+
+        public static final int[] MARGIN_ADJUSTED_VIEWS = {
+                R.id.notification_headerless_view_column,
+                R.id.line1,
+                R.id.notification_main_column,
+                R.id.notification_header};
+
+        @Override
+        public void apply(View parent, View child, boolean apply, boolean reset) {
+            ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon);
+            ImageView leftIcon = child.findViewById(com.android.internal.R.id.left_icon);
+            if (rightIcon == null || leftIcon == null) {
+                return;
+            }
+            Drawable iconDrawable = rightIcon.getDrawable();
+            if (iconDrawable == null) {
+                return;
+            }
+            rightIcon.setVisibility(apply ? View.GONE : View.VISIBLE);
+            leftIcon.setVisibility(apply ? View.VISIBLE : View.GONE);
+            leftIcon.setImageDrawable(apply ? iconDrawable : null);
+
+            for (int viewId : MARGIN_ADJUSTED_VIEWS) {
+                adjustMargins(!apply, child.findViewById(viewId));
+            }
+        }
+
+        void adjustMargins(boolean iconVisible, View target) {
+            if (target == null) {
+                return;
+            }
+            Integer value = (Integer) target.getTag(iconVisible
+                    ? com.android.internal.R.id.tag_margin_end_when_icon_visible
+                    : com.android.internal.R.id.tag_margin_end_when_icon_gone);
+            if (value == null) {
+                return;
+            }
+            if (target instanceof NotificationHeaderView) {
+                ((NotificationHeaderView) target).setTopLineExtraMarginEnd(value);
+            } else {
+                ViewGroup.LayoutParams layoutParams = target.getLayoutParams();
+                if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
+                    ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
+                    target.setLayoutParams(layoutParams);
+                }
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index 44b9bd2..d1ab7ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -19,18 +19,25 @@
 import android.app.Notification
 import android.content.Context
 import android.content.pm.LauncherApps
+import android.graphics.drawable.AnimatedImageDrawable
 import android.os.Handler
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.NotificationListenerService.RankingMap
 import com.android.internal.statusbar.NotificationVisibility
 import com.android.internal.widget.ConversationLayout
+import com.android.internal.widget.MessagingImageMessage
+import com.android.internal.widget.MessagingLayout
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationContentView
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import com.android.systemui.util.children
 import java.util.concurrent.ConcurrentHashMap
 import javax.inject.Inject
 
@@ -58,6 +65,71 @@
 }
 
 /**
+ * Tracks state related to animated images inside of notifications. Ex: starting and stopping
+ * animations to conserve CPU and memory.
+ */
+@SysUISingleton
+class AnimatedImageNotificationManager @Inject constructor(
+    private val notificationEntryManager: NotificationEntryManager,
+    private val headsUpManager: HeadsUpManager,
+    private val statusBarStateController: StatusBarStateController
+) {
+
+    private var isStatusBarExpanded = false
+
+    /** Begins listening to state changes and updating animations accordingly. */
+    fun bind() {
+        headsUpManager.addListener(object : OnHeadsUpChangedListener {
+            override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+                entry.row?.let { row ->
+                    updateAnimatedImageDrawables(row, animating = isHeadsUp || isStatusBarExpanded)
+                }
+            }
+        })
+        statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
+            override fun onExpandedChanged(isExpanded: Boolean) {
+                isStatusBarExpanded = isExpanded
+                notificationEntryManager.activeNotificationsForCurrentUser.forEach { entry ->
+                    entry.row?.let { row ->
+                        updateAnimatedImageDrawables(row, animating = isExpanded || row.isHeadsUp)
+                    }
+                }
+            }
+        })
+        notificationEntryManager.addNotificationEntryListener(object : NotificationEntryListener {
+            override fun onEntryInflated(entry: NotificationEntry) {
+                entry.row?.let { row ->
+                    updateAnimatedImageDrawables(
+                            row,
+                            animating = isStatusBarExpanded || row.isHeadsUp)
+                }
+            }
+            override fun onEntryReinflated(entry: NotificationEntry) = onEntryInflated(entry)
+        })
+    }
+
+    private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) =
+            (row.layouts?.asSequence() ?: emptySequence())
+                    .flatMap { layout -> layout.allViews.asSequence() }
+                    .flatMap { view ->
+                        (view as? ConversationLayout)?.messagingGroups?.asSequence()
+                                ?: (view as? MessagingLayout)?.messagingGroups?.asSequence()
+                                ?: emptySequence()
+                    }
+                    .flatMap { messagingGroup -> messagingGroup.messageContainer.children }
+                    .mapNotNull { view ->
+                        (view as? MessagingImageMessage)
+                                ?.let { imageMessage ->
+                                    imageMessage.drawable as? AnimatedImageDrawable
+                                }
+                    }
+                    .forEach { animatedImageDrawable ->
+                        if (animating) animatedImageDrawable.start()
+                        else animatedImageDrawable.stop()
+                    }
+}
+
+/**
  * Tracks state related to conversation notifications, and updates the UI of existing notifications
  * when necessary.
  */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 363a085..1f9bc77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -364,18 +364,13 @@
 
     @Nullable
     private Intent getTaskIntent(int taskId, int userId) {
-        try {
-            final List<ActivityManager.RecentTaskInfo> tasks =
-                    ActivityTaskManager.getService()
-                            .getRecentTasks(NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId)
-                            .getList();
-            for (int i = 0; i < tasks.size(); i++) {
-                if (tasks.get(i).id == taskId) {
-                    return tasks.get(i).baseIntent;
-                }
+        final List<ActivityManager.RecentTaskInfo> tasks =
+                ActivityTaskManager.getInstance().getRecentTasks(
+                        NUM_TASKS_FOR_INSTANT_APP_INFO, 0, userId);
+        for (int i = 0; i < tasks.size(); i++) {
+            if (tasks.get(i).id == taskId) {
+                return tasks.get(i).baseIntent;
             }
-        } catch (RemoteException e) {
-            // Fall through
         }
         return null;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 8f352ad..54ce4ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationPresenter
+import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.NotificationClicker
 import com.android.systemui.statusbar.notification.NotificationEntryManager
@@ -71,7 +72,8 @@
     private val headsUpManager: HeadsUpManager,
     private val headsUpController: HeadsUpController,
     private val headsUpViewBinder: HeadsUpViewBinder,
-    private val clickerBuilder: NotificationClicker.Builder
+    private val clickerBuilder: NotificationClicker.Builder,
+    private val animatedImageNotificationManager: AnimatedImageNotificationManager
 ) : NotificationsController {
 
     override fun initialize(
@@ -100,6 +102,7 @@
                 bindRowCallback)
         headsUpViewBinder.setPresenter(presenter)
         notifBindPipelineInitializer.initialize()
+        animatedImageNotificationManager.bind()
 
         if (featureFlags.isNewNotifPipelineEnabled) {
             newNotifPipeline.get().initialize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5ed17f1..10118e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -558,7 +558,7 @@
             setChronometerRunning(true);
         }
         if (mNotificationParent != null) {
-            mNotificationParent.updateChildrenHeaderAppearance();
+            mNotificationParent.updateChildrenAppearance();
         }
         onAttachedChildrenCountChanged();
         // The public layouts expand button is always visible
@@ -2337,7 +2337,7 @@
         }
         getShowingLayout().updateBackgroundColor(false /* animate */);
         mPrivateLayout.updateExpandButtons(isExpandable());
-        updateChildrenHeaderAppearance();
+        updateChildrenAppearance();
         updateChildrenVisibility();
         applyChildrenRoundness();
     }
@@ -2382,9 +2382,12 @@
         return channels;
     }
 
-    public void updateChildrenHeaderAppearance() {
+    /**
+     * If this is a group, update the appearance of the children.
+     */
+    public void updateChildrenAppearance() {
         if (mIsSummaryWithChildren) {
-            mChildrenContainer.updateChildrenHeaderAppearance();
+            mChildrenContainer.updateChildrenAppearance();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
index 7bd192d..44ccb68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java
@@ -19,10 +19,7 @@
 import android.app.ActivityManager;
 import android.app.Notification;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -81,7 +78,7 @@
      * @return True if has its internal cache, false otherwise.
      */
     public boolean hasCache() {
-        return mImageCache != null && !ActivityManager.isLowRamDeviceStatic();
+        return mImageCache != null && !isLowRam();
     }
 
     private boolean isLowRam() {
@@ -110,11 +107,6 @@
                 : R.dimen.notification_custom_view_max_image_height);
     }
 
-    @VisibleForTesting
-    protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException {
-        return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext);
-    }
-
     /**
      * To resolve image from specified uri directly. If the resulting image is larger than the
      * maximum allowed size, scale it down.
@@ -123,13 +115,7 @@
      * @throws IOException Throws if failed at resolving the image.
      */
     Drawable resolveImage(Uri uri) throws IOException {
-        BitmapDrawable image = resolveImageInternal(uri);
-        if (image == null || image.getBitmap() == null) {
-            throw new IOException("resolveImageInternal returned null for uri: " + uri);
-        }
-        Bitmap bitmap = image.getBitmap();
-        image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight));
-        return image;
+        return LocalImageResolver.resolveImage(uri, mContext, mMaxImageWidth, mMaxImageHeight);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 05db67d..37d5da2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -64,6 +64,7 @@
     private ImageView mWorkProfileImage;
     private View mAudiblyAlertedIcon;
     private View mFeedbackIcon;
+    private View mLeftIcon;
     private View mRightIcon;
 
     private boolean mIsLowPriority;
@@ -108,6 +109,7 @@
         mAppNameText = mView.findViewById(com.android.internal.R.id.app_name_text);
         mExpandButton = mView.findViewById(com.android.internal.R.id.expand_button);
         mAltExpandTarget = mView.findViewById(com.android.internal.R.id.alternate_expand_target);
+        mLeftIcon = mView.findViewById(com.android.internal.R.id.left_icon);
         mRightIcon = mView.findViewById(com.android.internal.R.id.right_icon);
         mWorkProfileImage = mView.findViewById(com.android.internal.R.id.profile_badge);
         mNotificationHeader = mView.findViewById(com.android.internal.R.id.notification_header);
@@ -146,6 +148,9 @@
         updateCropToPaddingForImageViews();
         Notification notification = row.getEntry().getSbn().getNotification();
         mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+        if (mLeftIcon != null) {
+            mLeftIcon.setClipToOutline(true);
+        }
         if (mRightIcon != null) {
             mRightIcon.setClipToOutline(true);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index b04f94c..601fc19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -34,7 +34,7 @@
 import com.android.internal.widget.CachingIconView;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
-import com.android.systemui.statusbar.NotificationHeaderUtil;
+import com.android.systemui.statusbar.NotificationGroupingUtil;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -94,7 +94,7 @@
     private NotificationViewWrapper mNotificationHeaderWrapper;
     private NotificationHeaderView mNotificationHeaderLowPriority;
     private NotificationViewWrapper mNotificationHeaderWrapperLowPriority;
-    private NotificationHeaderUtil mHeaderUtil;
+    private NotificationGroupingUtil mGroupingUtil;
     private ViewState mHeaderViewState;
     private int mClipBottomAmount;
     private boolean mIsLowPriority;
@@ -299,7 +299,7 @@
         row.setSystemChildExpanded(false);
         row.setUserLocked(false);
         if (!row.isRemoved()) {
-            mHeaderUtil.restoreNotificationHeader(row);
+            mGroupingUtil.restoreChildNotification(row);
         }
     }
 
@@ -341,7 +341,7 @@
         }
         recreateLowPriorityHeader(builder, isConversation);
         updateHeaderVisibility(false /* animate */);
-        updateChildrenHeaderAppearance();
+        updateChildrenAppearance();
     }
 
     /**
@@ -389,8 +389,11 @@
         }
     }
 
-    public void updateChildrenHeaderAppearance() {
-        mHeaderUtil.updateChildrenHeaderAppearance();
+    /**
+     * Update the appearance of the children to reduce redundancies.
+     */
+    public void updateChildrenAppearance() {
+        mGroupingUtil.updateChildrenAppearance();
     }
 
     public void updateGroupOverflow() {
@@ -861,7 +864,7 @@
 
     public void setContainingNotification(ExpandableNotificationRow parent) {
         mContainingNotification = parent;
-        mHeaderUtil = new NotificationHeaderUtil(mContainingNotification);
+        mGroupingUtil = new NotificationGroupingUtil(mContainingNotification);
     }
 
     public ExpandableNotificationRow getContainingNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f7139aa..bb46172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -68,6 +68,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.LatencyTracker;
+import com.android.keyguard.DisabledUdfpsController;
 import com.android.keyguard.KeyguardClockSwitchController;
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardStatusViewController;
@@ -256,7 +257,25 @@
                     mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
                     mDelayShowingKeyguardStatusBar = false;
                 }
-            };
+
+                @Override
+                public void onKeyguardVisibilityChanged(boolean showing) {
+                    if (mDisabledUdfpsController == null
+                            && mAuthController.getUdfpsRegion() != null
+                            && mAuthController.isUdfpsEnrolled(
+                                   KeyguardUpdateMonitor.getCurrentUser())) {
+                        LayoutInflater.from(mView.getContext())
+                                .inflate(R.layout.disabled_udfps_view, mView);
+                        mDisabledUdfpsController = new DisabledUdfpsController(
+                                mView.findViewById(R.id.disabled_udfps_view),
+                                mStatusBarStateController,
+                                mUpdateMonitor,
+                                mAuthController,
+                                mStatusBarKeyguardViewManager);
+                        mDisabledUdfpsController.init();
+                    }
+                }
+    };
 
     private final InjectionInflationController mInjectionInflationController;
     private final PowerManager mPowerManager;
@@ -284,6 +303,7 @@
     private QS mQs;
     private FrameLayout mQsFrame;
     private KeyguardStatusViewController mKeyguardStatusViewController;
+    private DisabledUdfpsController mDisabledUdfpsController;
     private View mQsNavbarScrim;
     private NotificationsQuickSettingsContainer mNotificationContainerParent;
     private boolean mAnimateNextPositionUpdate;
@@ -3044,6 +3064,9 @@
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
+        if (mDisabledUdfpsController != null) {
+            mDisabledUdfpsController.dump(fd, pw, args);
+        }
     }
 
     public boolean hasActiveClearableNotifications() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 79f0915..adbc85b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -42,6 +42,7 @@
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.ContentInfo;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -590,12 +591,12 @@
                         new OnReceiveContentListener() {
                             @Override
                             @Nullable
-                            public Payload onReceiveContent(@NonNull View view,
-                                    @NonNull Payload payload) {
-                                Map<Boolean, Payload> split = payload.partition(
+                            public ContentInfo onReceiveContent(@NonNull View view,
+                                    @NonNull ContentInfo payload) {
+                                Map<Boolean, ContentInfo> split = payload.partition(
                                         item -> item.getUri() != null);
-                                Payload uriItems = split.get(true);
-                                Payload remainingItems = split.get(false);
+                                ContentInfo uriItems = split.get(true);
+                                ContentInfo remainingItems = split.get(false);
                                 if (uriItems != null) {
                                     ClipData clip = uriItems.getClip();
                                     ClipDescription description = clip.getDescription();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 2795857..bdf2b0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -71,7 +71,6 @@
             // Creating AudioRecordingDisclosureBar and just letting it run
             new AudioRecordingDisclosureBar(mContext);
         }
-
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
new file mode 100644
index 0000000..3b1a4db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.systemui.R;
+
+/**
+ * Adapter for the VerticalGridView of the TvNotificationsPanelView.
+ */
+public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private static final String TAG = "TvNotificationAdapter";
+    private SparseArray<StatusBarNotification> mNotifications;
+
+    public TvNotificationAdapter() {
+        setHasStableIds(true);
+    }
+
+    @NonNull
+    @Override
+    public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item,
+                parent, false);
+        return new TvNotificationViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
+        if (mNotifications == null) {
+            Log.e(TAG, "Could not bind view holder because the notification is missing");
+            return;
+        }
+
+        TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder;
+        Notification notification = mNotifications.valueAt(position).getNotification();
+        holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE));
+        holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT));
+        holder.mPendingIntent = notification.contentIntent;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mNotifications == null ? 0 : mNotifications.size();
+    }
+
+    @Override
+    public long getItemId(int position) {
+        // the item id is the notification id
+        return mNotifications.keyAt(position);
+    }
+
+    /**
+     * Updates the notifications and calls notifyDataSetChanged().
+     */
+    public void setNotifications(SparseArray<StatusBarNotification> notifications) {
+        this.mNotifications = notifications;
+        notifyDataSetChanged();
+    }
+
+    private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements
+            View.OnClickListener {
+        final TextView mTitle;
+        final TextView mDetails;
+        PendingIntent mPendingIntent;
+
+        protected TvNotificationViewHolder(View itemView) {
+            super(itemView);
+            mTitle = itemView.findViewById(R.id.tv_notification_title);
+            mDetails = itemView.findViewById(R.id.tv_notification_details);
+            itemView.setOnClickListener(this);
+        }
+
+        @Override
+        public void onClick(View v) {
+            try {
+                if (mPendingIntent != null) {
+                    mPendingIntent.send();
+                }
+            } catch (PendingIntent.CanceledException e) {
+                Log.d(TAG, "Pending intent canceled for : " + mPendingIntent);
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
new file mode 100644
index 0000000..d985803
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.content.Context;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationListener;
+
+import javax.inject.Inject;
+
+/**
+ * Keeps track of the notifications on TV.
+ */
+public class TvNotificationHandler extends SystemUI implements
+        NotificationListener.NotificationHandler {
+    private static final String TAG = "TvNotificationHandler";
+    private final NotificationListener mNotificationListener;
+    private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>();
+    @Nullable
+    private Listener mUpdateListener;
+
+    @Inject
+    public TvNotificationHandler(Context context, NotificationListener notificationListener) {
+        super(context);
+        mNotificationListener = notificationListener;
+    }
+
+    public SparseArray<StatusBarNotification> getCurrentNotifications() {
+        return mNotifications;
+    }
+
+    public void setTvNotificationListener(Listener listener) {
+        mUpdateListener = listener;
+    }
+
+    @Override
+    public void start() {
+        mNotificationListener.addNotificationHandler(this);
+        mNotificationListener.registerAsSystemService();
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap) {
+        if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) {
+            Log.v(TAG, "Notification not added because it isn't relevant for tv");
+            return;
+        }
+
+        mNotifications.put(sbn.getId(), sbn);
+        if (mUpdateListener != null) {
+            mUpdateListener.notificationsUpdated(mNotifications);
+        }
+        Log.d(TAG, "Notification added");
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap) {
+
+        if (mNotifications.contains(sbn.getId())) {
+            mNotifications.remove(sbn.getId());
+            Log.d(TAG, "Notification removed");
+
+            if (mUpdateListener != null) {
+                mUpdateListener.notificationsUpdated(mNotifications);
+            }
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn,
+            NotificationListenerService.RankingMap rankingMap, int reason) {
+        onNotificationRemoved(sbn, rankingMap);
+    }
+
+    @Override
+    public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
+        // noop
+    }
+
+    @Override
+    public void onNotificationsInitialized() {
+        // noop
+    }
+
+    /**
+     * Get notified when the notifications are updated.
+     */
+    interface Listener {
+        void notificationsUpdated(SparseArray<StatusBarNotification> sbns);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index 0bd3624..477424c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.tv;
+package com.android.systemui.statusbar.tv.notifications;
 
 import android.Manifest;
 import android.app.NotificationManager;
@@ -59,9 +59,8 @@
             startNotificationHandlerActivity(
                     new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
         } else {
-            Log.w(TAG,
-                    "Not toggling notification panel: config_notificationHandlerPackage is "
-                            + "empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
@@ -71,9 +70,8 @@
             startNotificationHandlerActivity(
                     new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
         } else {
-            Log.w(TAG,
-                    "Not expanding notification panel: config_notificationHandlerPackage is "
-                            + "empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
@@ -86,11 +84,17 @@
             closeNotificationIntent.setPackage(mNotificationHandlerPackage);
             mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
         } else {
-            Log.w(TAG,
-                    "Not closing notification panel: config_notificationHandlerPackage is empty");
+            openInternalNotificationPanel(
+                    NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
         }
     }
 
+    private void openInternalNotificationPanel(String action) {
+        Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
+        intent.setAction(action);
+        mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
+    }
+
     /**
      * Starts the activity intent if all of the following are true
      * <ul>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
new file mode 100644
index 0000000..30f401b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.tv.notifications;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.SparseArray;
+import android.view.View;
+
+import androidx.leanback.widget.VerticalGridView;
+
+import com.android.systemui.R;
+
+import javax.inject.Inject;
+
+/**
+ * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can
+ * be found to show the notifications.
+ */
+public class TvNotificationPanelActivity extends Activity implements
+        TvNotificationHandler.Listener {
+    private final TvNotificationHandler mTvNotificationHandler;
+    private TvNotificationAdapter mTvNotificationAdapter;
+    private VerticalGridView mNotificationListView;
+    private View mNotificationPlaceholder;
+    private boolean mPanelAlreadyOpen = false;
+
+    @Inject
+    public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) {
+        super();
+        mTvNotificationHandler = tvNotificationHandler;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (maybeClosePanel(getIntent())) {
+            return;
+        }
+        mPanelAlreadyOpen = true;
+
+        setContentView(R.layout.tv_notification_panel);
+
+        mNotificationPlaceholder = findViewById(R.id.no_tv_notifications);
+        mTvNotificationAdapter = new TvNotificationAdapter();
+
+        mNotificationListView = findViewById(R.id.notifications_list);
+        mNotificationListView.setAdapter(mTvNotificationAdapter);
+        mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width);
+
+        mTvNotificationHandler.setTvNotificationListener(this);
+        notificationsUpdated(mTvNotificationHandler.getCurrentNotifications());
+    }
+
+    @Override
+    public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) {
+        mTvNotificationAdapter.setNotifications(notificationList);
+
+        boolean noNotifications = notificationList.size() == 0;
+        mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE);
+        mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        maybeClosePanel(intent);
+    }
+
+    /**
+     * Handles intents from onCreate and onNewIntent.
+     *
+     * @return true if the panel is being closed, false if it is being opened
+     */
+    private boolean maybeClosePanel(Intent intent) {
+        if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction())
+                || (mPanelAlreadyOpen
+                && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals(
+                intent.getAction()))) {
+            finish();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mTvNotificationHandler.setTvNotificationListener(null);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
index 2c3ea4f..353333f 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
@@ -16,14 +16,22 @@
 
 package com.android.systemui.tv;
 
+import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.GlobalRootComponent;
-import com.android.systemui.wmshell.TvPipModule;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
 import dagger.Binds;
 import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 @Module
 interface TvSystemUIBinder {
     @Binds
     GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
+
+    @Binds
+    @IntoMap
+    @ClassKey(TvNotificationHandler.class)
+    SystemUI bindTvNotificationHandler(TvNotificationHandler systemui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 8ffc7cf..56a4c203 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -43,6 +43,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -62,6 +63,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
 
 import javax.inject.Named;
 
@@ -164,4 +166,11 @@
 
     @Binds
     abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
+
+    @Provides
+    @SysUISingleton
+    static TvNotificationHandler provideTvNotificationHandler(Context context,
+            NotificationListener notificationListener) {
+        return new TvNotificationHandler(context, notificationListener);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
index 2e8eb00..53d84db 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt
@@ -17,7 +17,6 @@
 package com.android.keyguard
 
 import android.animation.ValueAnimator
-import android.graphics.Paint
 import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
@@ -53,7 +52,7 @@
         val layout = makeLayout("Hello, World", PAINT[0])
         val valueAnimator = mock(ValueAnimator::class.java)
         val textInterpolator = mock(TextInterpolator::class.java)
-        val paint = arrayListOf(mock(Paint::class.java))
+        val paint = arrayListOf(mock(TextPaint::class.java))
         `when`(textInterpolator.targetPaint).thenReturn(paint)
 
         val textAnimator = TextAnimator(layout, {}).apply {
@@ -85,7 +84,7 @@
         val layout = makeLayout("Hello, World", PAINT[0])
         val valueAnimator = mock(ValueAnimator::class.java)
         val textInterpolator = mock(TextInterpolator::class.java)
-        val paint = arrayListOf(mock(Paint::class.java))
+        val paint = arrayListOf(mock(TextPaint::class.java))
         `when`(textInterpolator.targetPaint).thenReturn(paint)
 
         val textAnimator = TextAnimator(layout, {}).apply {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
index 002ba36..1206dab 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/TextInterpolatorTest.kt
@@ -18,11 +18,12 @@
 
 import android.graphics.Bitmap
 import android.graphics.Canvas
-import android.graphics.Paint
 import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
 import android.text.TextPaint
+import android.text.TextDirectionHeuristic
+import android.text.TextDirectionHeuristics
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -31,6 +32,7 @@
 import kotlin.math.ceil
 
 private const val TEXT = "Hello, World."
+private const val BIDI_TEXT = "abc\u05D0\u05D1\u05D2"
 private const val BMP_WIDTH = 400
 private const val BMP_HEIGHT = 300
 
@@ -38,11 +40,11 @@
     textSize = 32f
 }
 
-private val START_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply {
+private val START_PAINT = arrayListOf(TextPaint(PAINT).apply {
     fontVariationSettings = "'wght' 400"
 })
 
-private val END_PAINT = arrayListOf<Paint>(TextPaint(PAINT).apply {
+private val END_PAINT = arrayListOf(TextPaint(PAINT).apply {
     fontVariationSettings = "'wght' 700"
 })
 
@@ -50,9 +52,14 @@
 @SmallTest
 class TextInterpolatorTest : SysuiTestCase() {
 
-    private fun makeLayout(text: String, paint: TextPaint): Layout {
+    private fun makeLayout(
+        text: String,
+        paint: TextPaint,
+        dir: TextDirectionHeuristic = TextDirectionHeuristics.LTR
+    ): Layout {
         val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
-        return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build()
+        return StaticLayout.Builder.obtain(text, 0, text.length, paint, width)
+                .setTextDirection(dir).build()
     }
 
     @Test
@@ -69,7 +76,7 @@
         // Just after created TextInterpolator, it should have 0 progress.
         assertThat(interp.progress).isEqualTo(0f)
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(TEXT, START_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(TEXT, START_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
@@ -87,7 +94,7 @@
 
         interp.progress = 1f
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        val expected = makeLayout(TEXT, END_PAINT[0] as TextPaint).toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(TEXT, END_PAINT[0]).toBitmap(BMP_WIDTH, BMP_HEIGHT)
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
@@ -108,9 +115,9 @@
         // end state.
         interp.progress = 0.5f
         val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
-        assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0] as TextPaint)
+        assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT[0])
             .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
-        assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0] as TextPaint)
+        assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT[0])
             .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
     }
 
@@ -135,10 +142,50 @@
 
         assertThat(expected.sameAs(actual)).isTrue()
     }
+
+    @Test
+    fun testBidi_LTR() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
+
+        val interp = TextInterpolator(layout)
+        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.onBasePaintModified()
+
+        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        assertThat(interp.progress).isEqualTo(0f)
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.LTR)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        assertThat(expected.sameAs(actual)).isTrue()
+    }
+
+    @Test
+    fun testBidi_RTL() {
+        val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
+
+        val interp = TextInterpolator(layout)
+        TextInterpolator.updatePaint(interp.basePaint, START_PAINT)
+        interp.onBasePaintModified()
+
+        TextInterpolator.updatePaint(interp.targetPaint, END_PAINT)
+        interp.onTargetPaintModified()
+
+        // Just after created TextInterpolator, it should have 0 progress.
+        assertThat(interp.progress).isEqualTo(0f)
+        val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
+        val expected = makeLayout(BIDI_TEXT, START_PAINT[0], TextDirectionHeuristics.RTL)
+                .toBitmap(BMP_WIDTH, BMP_HEIGHT)
+
+        assertThat(expected.sameAs(actual)).isTrue()
+    }
 }
 
 private fun Layout.toBitmap(width: Int, height: Int) =
-        Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }!!
+        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
 
 private fun TextInterpolator.toBitmap(width: Int, height: Int) =
-        Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8).also { draw(Canvas(it)) }
\ No newline at end of file
+        Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index ec0aa4c..30c4cf6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
 import android.app.IActivityTaskManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -92,7 +93,7 @@
     @Mock
     private StatusBarStateController mStatusBarStateController;
     @Mock
-    private IActivityTaskManager mActivityTaskManager;
+    private ActivityTaskManager mActivityTaskManager;
     @Mock
     private FingerprintManager mFingerprintManager;
     @Mock
@@ -553,7 +554,7 @@
 
         TestableAuthController(Context context, CommandQueue commandQueue,
                 StatusBarStateController statusBarStateController,
-                IActivityTaskManager activityTaskManager,
+                ActivityTaskManager activityTaskManager,
                 FingerprintManager fingerprintManager,
                 FaceManager faceManager,
                 Provider<UdfpsController> udfpsControllerFactory) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 3e44fa4..477fe63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.people.widget;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -164,7 +165,7 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotificationChannel channel =
-                mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME);
+                new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
 
         mNoMan.issueChannelModification(TEST_PACKAGE_A,
                 UserHandle.getUserHandleForUid(0), channel, IMPORTANCE_HIGH);
@@ -181,7 +182,7 @@
         when(mIAppWidgetService.getAppWidgetIds(any())).thenReturn(widgetIdsArray);
 
         NotificationChannel channel =
-                mNoMan.createNotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME);
+                new NotificationChannel(TEST_CHANNEL_ID, TEST_CHANNEL_NAME, IMPORTANCE_DEFAULT);
         channel.setConversationId(TEST_PARENT_CHANNEL_ID, TEST_CONVERSATION_ID);
 
         mNoMan.issueChannelModification(TEST_PACKAGE_A,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 6fa6f31..fd0715b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -15,6 +15,7 @@
 package com.android.systemui.qs;
 
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Matchers.any;
@@ -24,6 +25,8 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.UserInfo;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.VectorDrawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.provider.Settings;
@@ -75,6 +78,7 @@
     private ViewGroup mRootView;
     private TextView mFooterText;
     private TestableImageView mFooterIcon;
+    private TestableImageView mPrimaryFooterIcon;
     private QSSecurityFooter mFooter;
     @Mock
     private SecurityController mSecurityController;
@@ -95,6 +99,7 @@
                 mActivityStarter, mSecurityController, looper);
         mFooterText = mRootView.findViewById(R.id.footer_text);
         mFooterIcon = mRootView.findViewById(R.id.footer_icon);
+        mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooter.setHostEnvironment(null);
     }
 
@@ -119,6 +124,7 @@
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
     }
@@ -136,6 +142,7 @@
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
     }
@@ -165,6 +172,7 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         // -1 == never set.
         assertEquals(-1, mFooterIcon.getLastImageResource());
 
@@ -203,6 +211,7 @@
                                         VPN_PACKAGE),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
@@ -229,6 +238,7 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
@@ -252,6 +262,7 @@
 
         TestableLooper.get(this).processAllMessages();
         assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
         assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
@@ -534,12 +545,27 @@
     @Test
     public void testParentalControls() {
         when(mSecurityController.isParentalControlsEnabled()).thenReturn(true);
+
+        Drawable testDrawable = new VectorDrawable();
+        when(mSecurityController.getIcon(any())).thenReturn(testDrawable);
+        assertNotNull(mSecurityController.getIcon(null));
+
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
 
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
                 mFooterText.getText());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+
+        assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable());
+
+        // Ensure the primary icon is set to gone when parental controls is disabled.
+        when(mSecurityController.isParentalControlsEnabled()).thenReturn(false);
+        mFooter.refreshState();
+        TestableLooper.get(this).processAllMessages();
+
+        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
index 1bfe10c5..4507366 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NoManSimulator.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection;
 
-import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
-
 import static org.junit.Assert.assertNotNull;
 
 import android.app.NotificationChannel;
@@ -76,10 +74,6 @@
         }
     }
 
-    public NotificationChannel createNotificationChannel(String id, String name) {
-        return new NotificationChannel(id, name, IMPORTANCE_DEFAULT);
-    }
-
     public void issueChannelModification(
             String pkg, UserHandle user, NotificationChannel channel, int modificationType) {
         for (NotificationHandler listener : mListeners) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
index 7f48cd1..edf2b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java
@@ -69,32 +69,4 @@
         assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20);
         assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15);
     }
-
-    @Test
-    public void resolveImage_sizeTooBig() throws IOException {
-        doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
-        mResolver.mMaxImageHeight = 5;
-        mResolver.mMaxImageWidth = 5;
-
-        // original bitmap size is 10x10
-        BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
-        Bitmap resolvedBitmap = resolved.getBitmap();
-        assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth());
-        assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight());
-        assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap);
-    }
-
-    @Test
-    public void resolveImage_sizeOK() throws IOException {
-        doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri);
-        mResolver.mMaxImageWidth = 15;
-        mResolver.mMaxImageHeight = 15;
-
-        // original bitmap size is 10x10
-        BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri);
-        Bitmap resolvedBitmap = resolved.getBitmap();
-        assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth());
-        assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight());
-        assertSame("Bitmap not replaced", resolvedBitmap, mBitmap);
-    }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index fec7ac0..614863d 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -188,6 +188,13 @@
     src: ":services.core.json.gz",
 }
 
+filegroup {
+    name: "services.core-sources-deviceconfig-interface",
+    srcs: [
+         "java/com/android/server/utils/DeviceConfigInterface.java"
+    ],
+}
+
 // TODO: Move connectivity service sources to independent directory.
 filegroup {
     name: "connectivity-service-srcs",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 031cc42..e8ee18c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4821,15 +4821,6 @@
         }
     }
 
-    private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
-        ensureRunningOnConnectivityServiceThread();
-        NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
-        if (vpnNai == null || nc == null) {
-            return;
-        }
-        updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
-    }
-
     @Override
     public boolean updateLockdownVpn() {
         if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -5140,7 +5131,7 @@
         }
     }
 
-    private void onUserStart(int userId) {
+    private void onUserStarted(int userId) {
         synchronized (mVpns) {
             Vpn userVpn = mVpns.get(userId);
             if (userVpn != null) {
@@ -5155,7 +5146,7 @@
         }
     }
 
-    private void onUserStop(int userId) {
+    private void onUserStopped(int userId) {
         synchronized (mVpns) {
             Vpn userVpn = mVpns.get(userId);
             if (userVpn == null) {
@@ -5169,28 +5160,22 @@
 
     private void onUserAdded(int userId) {
         mPermissionMonitor.onUserAdded(userId);
-        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserAdded(userId);
-                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
-                updateVpnCapabilities(vpn, nc);
             }
         }
     }
 
     private void onUserRemoved(int userId) {
         mPermissionMonitor.onUserRemoved(userId);
-        Network defaultNetwork = getNetwork(getDefaultNetwork());
         synchronized (mVpns) {
             final int vpnsSize = mVpns.size();
             for (int i = 0; i < vpnsSize; i++) {
                 Vpn vpn = mVpns.valueAt(i);
                 vpn.onUserRemoved(userId);
-                NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
-                updateVpnCapabilities(vpn, nc);
             }
         }
     }
@@ -5272,9 +5257,9 @@
             if (userId == UserHandle.USER_NULL) return;
 
             if (Intent.ACTION_USER_STARTED.equals(action)) {
-                onUserStart(userId);
+                onUserStarted(userId);
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
-                onUserStop(userId);
+                onUserStopped(userId);
             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
                 onUserAdded(userId);
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
@@ -8276,13 +8261,12 @@
             return false;
         }
 
-        final Network[] underlyingNetworks;
-        synchronized (mVpns) {
-            final Vpn vpn = getVpnIfOwner(callbackUid);
-            underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks();
-        }
-        if (underlyingNetworks != null) {
-            if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true;
+        for (NetworkAgentInfo virtual : mNetworkAgentInfos.values()) {
+            if (virtual.supportsUnderlyingNetworks()
+                    && virtual.networkCapabilities.getOwnerUid() == callbackUid
+                    && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) {
+                return true;
+            }
         }
 
         // Administrator UIDs also contains the Owner UID
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index f3c5fd8..9bf63cb 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -1043,7 +1043,7 @@
                 TypedXmlSerializer out = Xml.resolveSerializer(stream);
                 out.startDocument(null, true);
                 out.startTag(null, TAG_PACKAGE_WATCHDOG);
-                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+                out.attributeInt(null, ATTR_VERSION, DB_VERSION);
                 for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                     mAllObservers.valueAt(oIndex).writeLocked(out);
                 }
@@ -1107,7 +1107,7 @@
          * Does not persist any package failure thresholds.
          */
         @GuardedBy("mLock")
-        public boolean writeLocked(XmlSerializer out) {
+        public boolean writeLocked(TypedXmlSerializer out) {
             try {
                 out.startTag(null, TAG_OBSERVER);
                 out.attribute(null, ATTR_NAME, name);
@@ -1225,7 +1225,7 @@
          * #loadFromFile which in turn is only called on construction of the
          * singleton PackageWatchdog.
          **/
-        public static ObserverInternal read(XmlPullParser parser, PackageWatchdog watchdog) {
+        public static ObserverInternal read(TypedXmlPullParser parser, PackageWatchdog watchdog) {
             String observerName = null;
             if (TAG_OBSERVER.equals(parser.getName())) {
                 observerName = parser.getAttributeValue(null, ATTR_NAME);
@@ -1240,14 +1240,14 @@
                 while (XmlUtils.nextElementWithin(parser, innerDepth)) {
                     if (TAG_PACKAGE.equals(parser.getName())) {
                         try {
-                            String packageName = parser.getAttributeValue(null, ATTR_NAME);
-                            long duration = Long.parseLong(
-                                    parser.getAttributeValue(null, ATTR_DURATION));
-                            long healthCheckDuration = Long.parseLong(
-                                    parser.getAttributeValue(null,
-                                            ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
-                            boolean hasPassedHealthCheck = Boolean.parseBoolean(
-                                    parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK));
+                            String packageName = parser.getAttributeValue(
+                                    null, ATTR_NAME);
+                            long duration = parser.getAttributeLong(
+                                    null, ATTR_DURATION);
+                            long healthCheckDuration = parser.getAttributeLong(
+                                    null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION);
+                            boolean hasPassedHealthCheck = parser.getAttributeBoolean(
+                                    null, ATTR_PASSED_HEALTH_CHECK, false);
                             MonitoredPackage pkg = watchdog.newMonitoredPackage(packageName,
                                     duration, healthCheckDuration, hasPassedHealthCheck);
                             if (pkg != null) {
@@ -1364,14 +1364,12 @@
 
         /** Writes the salient fields to disk using {@code out}. */
         @GuardedBy("mLock")
-        public void writeLocked(XmlSerializer out) throws IOException {
+        public void writeLocked(TypedXmlSerializer out) throws IOException {
             out.startTag(null, TAG_PACKAGE);
             out.attribute(null, ATTR_NAME, getName());
-            out.attribute(null, ATTR_DURATION, String.valueOf(mDurationMs));
-            out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
-                    String.valueOf(mHealthCheckDurationMs));
-            out.attribute(null, ATTR_PASSED_HEALTH_CHECK,
-                    String.valueOf(mHasPassedHealthCheck));
+            out.attributeLong(null, ATTR_DURATION, mDurationMs);
+            out.attributeLong(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION, mHealthCheckDurationMs);
+            out.attributeBoolean(null, ATTR_PASSED_HEALTH_CHECK, mHasPassedHealthCheck);
             out.endTag(null, TAG_PACKAGE);
         }
 
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index 2455e76..d30e9fb 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -107,7 +107,7 @@
                     TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
                     serializer.startDocument(null, true);
                     serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
-                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(enable));
+                    serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, enable);
                     serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
                     serializer.endDocument();
                     mAtomicFile.finishWrite(outputStream);
@@ -180,7 +180,7 @@
                     TypedXmlSerializer serializer = Xml.resolveSerializer(outputStream);
                     serializer.startDocument(null, true);
                     serializer.startTag(null, XML_TAG_SENSOR_PRIVACY);
-                    serializer.attribute(null, XML_ATTRIBUTE_ENABLED, String.valueOf(mEnabled));
+                    serializer.attributeBoolean(null, XML_ATTRIBUTE_ENABLED, mEnabled);
                     serializer.endTag(null, XML_TAG_SENSOR_PRIVACY);
                     serializer.endDocument();
                     mAtomicFile.finishWrite(outputStream);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 04a52e0..dbd27af4 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -38,13 +38,8 @@
 import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
-import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST;
 
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -144,7 +139,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -160,9 +154,7 @@
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -172,7 +164,6 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.security.spec.KeySpec;
 import java.util.ArrayList;
@@ -186,7 +177,6 @@
 import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -212,21 +202,34 @@
     private static final String ZRAM_ENABLED_PROPERTY =
             "persist.sys.zram_enabled";
 
-    private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage();
-
     // A system property to control if obb app data isolation is enabled in vold.
     private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
             "persist.sys.vold_app_data_isolation_enabled";
 
+    // TODO(b/169327180): Will be fetched from the server, but for now, we emulate this in
+    // the system_server since it can write to DeviceConfig and MediaProvider can read it
+    private static final String PROP_TRANSCODE_ENABLED = "transcode_enabled";
+    private static final String PROP_TRANSCODE_DEFAULT = "transcode_default";
+    private static final String PROP_TRANSCODE_COMPAT_MANIFEST = "transcode_compat_manifest";
+    private static final boolean TRANSCODE_ENABLED_VALUE = false;
+    // Determines the default behavior of apps when transcode is enabled, AKA, Option A/Option B.
+    // If true, transcode by default (Option B). If false, don't transcode by default (Option A)
+    // For dogfood, we go with Option B
+    private static final boolean TRANSCODE_DEFAULT_VALUE = true;
+    // Format is <package_name>,<media_capability_bit_mask>,...
+    // media_capability_bit_mask is defined in MediaProvider/../TranscodeHelper.java:
+    // FLAG_HEVC = 1 << 0;
+    // FLAG_SLOW_MOTION = 1 << 1;
+    // FLAG_HDR_10 = 1 << 2;
+    // FLAG_HDR_10_PLUS = 1 << 3;
+    // FLAG_HDR_HLG = 1 << 4;
+    // FLAG_HDR_DOLBY_VISION = 1 << 5;
+    private static final String TRANSCODE_COMPAT_MANIFEST_VALUE =
+            "com.google.android.apps.photos,1";
+
     // How long we wait to reset storage, if we failed to call onMount on the
     // external storage service.
     public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10;
-    /**
-     * If {@code 1}, enables the isolated storage feature. If {@code -1},
-     * disables the isolated storage feature. If {@code 0}, uses the default
-     * value from the build system.
-     */
-    private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled";
 
     @GuardedBy("mLock")
     private final Set<Integer> mFuseMountedUser = new ArraySet<>();
@@ -898,22 +901,18 @@
                     com.android.internal.R.bool.config_zramWriteback)) {
             ZramWriteback.scheduleZramWriteback(mContext);
         }
-        // Toggle isolated-enable system property in response to settings
-        mContext.getContentResolver().registerContentObserver(
-            Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE),
-            false /*notifyForDescendants*/,
-            new ContentObserver(null /* current thread */) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    refreshIsolatedStorageSettings();
-                }
-            });
-        // For now, simply clone property when it changes
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
-                mContext.getMainExecutor(), (properties) -> {
-                    refreshIsolatedStorageSettings();
-                });
-        refreshIsolatedStorageSettings();
+
+        // TODO(b/169327180): Remove after setting up server-side DeviceConfig flags
+        // Set DeviceConfig values for transcoding that will be read by MediaProvider
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                PROP_TRANSCODE_ENABLED, String.valueOf(TRANSCODE_ENABLED_VALUE),
+                false /* makeDefault */);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                PROP_TRANSCODE_DEFAULT, String.valueOf(TRANSCODE_DEFAULT_VALUE),
+                false /* makeDefault */);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                PROP_TRANSCODE_COMPAT_MANIFEST, TRANSCODE_COMPAT_MANIFEST_VALUE,
+                false /* makeDefault */);
     }
 
     /**
@@ -945,38 +944,6 @@
         }
     }
 
-    private void refreshIsolatedStorageSettings() {
-        // Always copy value from newer DeviceConfig location
-        Settings.Global.putString(mResolver,
-                Settings.Global.ISOLATED_STORAGE_REMOTE,
-                DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
-                        ISOLATED_STORAGE_ENABLED));
-
-        final int local = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ISOLATED_STORAGE_LOCAL, 0);
-        final int remote = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.ISOLATED_STORAGE_REMOTE, 0);
-
-        // Walk down precedence chain; we prefer local settings first, then
-        // remote settings, before finally falling back to hard-coded default.
-        final boolean res;
-        if (local == -1) {
-            res = false;
-        } else if (local == 1) {
-            res = true;
-        } else if (remote == -1) {
-            res = false;
-        } else if (remote == 1) {
-            res = true;
-        } else {
-            res = true;
-        }
-
-        Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag "
-                + remote + " resolved to " + res);
-        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res));
-    }
-
     /**
      * MediaProvider has a ton of code that makes assumptions about storage
      * paths never changing, so we outright kill them to pick up new state.
@@ -1345,12 +1312,13 @@
                     final int oldState = vol.state;
                     final int newState = state;
                     vol.state = newState;
+                    final VolumeInfo vInfo = new VolumeInfo(vol);
                     final SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = vol;
+                    args.arg1 = vInfo;
                     args.arg2 = oldState;
                     args.arg3 = newState;
                     mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget();
-                    onVolumeStateChangedLocked(vol, oldState, newState);
+                    onVolumeStateChangedLocked(vInfo, oldState, newState);
                 }
             }
         }
@@ -1762,11 +1730,6 @@
      */
     public StorageManagerService(Context context) {
         sSelf = this;
-
-        // Snapshot feature flag used for this boot
-        SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString(
-                SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true)));
-
         mVoldAppDataIsolationEnabled = SystemProperties.getBoolean(
                 ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false);
         mContext = context;
@@ -2032,7 +1995,7 @@
                 if (type == START_TAG) {
                     final String tag = in.getName();
                     if (TAG_VOLUMES.equals(tag)) {
-                        final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT);
+                        final int version = in.getAttributeInt(null, ATTR_VERSION, VERSION_INIT);
                         final boolean primaryPhysical = SystemProperties.getBoolean(
                                 StorageManager.PROP_PRIMARY_PHYSICAL, false);
                         final boolean validAttr = (version >= VERSION_FIX_PRIMARY)
@@ -2067,7 +2030,7 @@
             TypedXmlSerializer out = Xml.resolveSerializer(fos);
             out.startDocument(null, true);
             out.startTag(null, TAG_VOLUMES);
-            writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
+            out.attributeInt(null, ATTR_VERSION, VERSION_FIX_PRIMARY);
             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
             final int size = mRecords.size();
             for (int i = 0; i < size; i++) {
@@ -2085,31 +2048,33 @@
         }
     }
 
-    public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException {
-        final int type = readIntAttribute(in, ATTR_TYPE);
+    public static VolumeRecord readVolumeRecord(TypedXmlPullParser in)
+            throws IOException, XmlPullParserException {
+        final int type = in.getAttributeInt(null, ATTR_TYPE);
         final String fsUuid = readStringAttribute(in, ATTR_FS_UUID);
         final VolumeRecord meta = new VolumeRecord(type, fsUuid);
         meta.partGuid = readStringAttribute(in, ATTR_PART_GUID);
         meta.nickname = readStringAttribute(in, ATTR_NICKNAME);
-        meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS);
-        meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0);
-        meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0);
-        meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0);
-        meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0);
+        meta.userFlags = in.getAttributeInt(null, ATTR_USER_FLAGS);
+        meta.createdMillis = in.getAttributeLong(null, ATTR_CREATED_MILLIS, 0);
+        meta.lastSeenMillis = in.getAttributeLong(null, ATTR_LAST_SEEN_MILLIS, 0);
+        meta.lastTrimMillis = in.getAttributeLong(null, ATTR_LAST_TRIM_MILLIS, 0);
+        meta.lastBenchMillis = in.getAttributeLong(null, ATTR_LAST_BENCH_MILLIS, 0);
         return meta;
     }
 
-    public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException {
+    public static void writeVolumeRecord(TypedXmlSerializer out, VolumeRecord rec)
+            throws IOException {
         out.startTag(null, TAG_VOLUME);
-        writeIntAttribute(out, ATTR_TYPE, rec.type);
+        out.attributeInt(null, ATTR_TYPE, rec.type);
         writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid);
         writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid);
         writeStringAttribute(out, ATTR_NICKNAME, rec.nickname);
-        writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags);
-        writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis);
-        writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
-        writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
-        writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
+        out.attributeInt(null, ATTR_USER_FLAGS, rec.userFlags);
+        out.attributeLong(null, ATTR_CREATED_MILLIS, rec.createdMillis);
+        out.attributeLong(null, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis);
+        out.attributeLong(null, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis);
+        out.attributeLong(null, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis);
         out.endTag(null, TAG_VOLUME);
     }
 
@@ -2604,32 +2569,6 @@
                 Binder.restoreCallingIdentity(token);
             }
         }
-
-        if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON
-                | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) {
-            final int value;
-            if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) {
-                value = 1;
-            } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) {
-                value = -1;
-            } else {
-                value = 0;
-            }
-
-            final long token = Binder.clearCallingIdentity();
-            try {
-                Settings.Global.putInt(mContext.getContentResolver(),
-                        Settings.Global.ISOLATED_STORAGE_LOCAL, value);
-                refreshIsolatedStorageSettings();
-
-                // Perform hard reboot to kick policy into place
-                mHandler.post(() -> {
-                    mContext.getSystemService(PowerManager.class).reboot(null);
-                });
-            } finally {
-                Binder.restoreCallingIdentity(token);
-            }
-        }
     }
 
     @Override
@@ -4149,15 +4088,6 @@
         }
     }
 
-    private int getMountMode(int uid, String packageName) {
-        final int mode = getMountModeInternal(uid, packageName);
-        if (LOCAL_LOGV) {
-            Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/"
-                    + UserHandle.formatUid(uid));
-        }
-        return mode;
-    }
-
     private int getMountModeInternal(int uid, String packageName) {
         try {
             // Get some easy cases out of the way first
@@ -4398,17 +4328,6 @@
             pw.println();
             pw.println("Local unlocked users: " + mLocalUnlockedUsers);
             pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
-
-            final ContentResolver cr = mContext.getContentResolver();
-            pw.println();
-            pw.println("Isolated storage, local feature flag: "
-                    + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0));
-            pw.println("Isolated storage, remote feature flag: "
-                    + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0));
-            pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage());
-            pw.println("Forced scoped storage app list: "
-                    + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
-                    PROP_FORCED_SCOPED_STORAGE_WHITELIST));
             pw.println("isAutomotive:" + mIsAutomotive);
         }
 
@@ -4460,20 +4379,10 @@
     }
 
     private final class StorageManagerInternalImpl extends StorageManagerInternal {
-        // Not guarded by a lock.
-        private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies =
-                new CopyOnWriteArrayList<>();
-
         @GuardedBy("mResetListeners")
         private final List<StorageManagerInternal.ResetListener> mResetListeners =
                 new ArrayList<>();
 
-        @Override
-        public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) {
-            // No locking - CopyOnWriteArrayList
-            mPolicies.add(policy);
-        }
-
         /**
          * Check if fuse is running in target user, if it's running then setup its storage dirs.
          * Return true if storage dirs are mounted.
@@ -4518,30 +4427,12 @@
 
         @Override
         public int getExternalStorageMountMode(int uid, String packageName) {
-            if (ENABLE_ISOLATED_STORAGE) {
-                return getMountMode(uid, packageName);
+            final int mode = getMountModeInternal(uid, packageName);
+            if (LOCAL_LOGV) {
+                Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/"
+                        + UserHandle.formatUid(uid));
             }
-            try {
-                if (packageName == null) {
-                    final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid);
-                    packageName = packagesForUid[0];
-                }
-            } catch (RemoteException e) {
-                // Should not happen - same process
-            }
-            // No locking - CopyOnWriteArrayList
-            int mountMode = Integer.MAX_VALUE;
-            for (ExternalStorageMountPolicy policy : mPolicies) {
-                final int policyMode = policy.getMountMode(uid, packageName);
-                if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) {
-                    return Zygote.MOUNT_EXTERNAL_NONE;
-                }
-                mountMode = Math.min(mountMode, policyMode);
-            }
-            if (mountMode == Integer.MAX_VALUE) {
-                return Zygote.MOUNT_EXTERNAL_NONE;
-            }
-            return mountMode;
+            return mode;
         }
 
         @Override
@@ -4611,17 +4502,8 @@
             if (uid == Process.SYSTEM_UID) {
                 return true;
             }
-            if (ENABLE_ISOLATED_STORAGE) {
-                return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE;
-            }
-            // No locking - CopyOnWriteArrayList
-            for (ExternalStorageMountPolicy policy : mPolicies) {
-                final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
-                if (!policyHasStorage) {
-                    return false;
-                }
-            }
-            return true;
+
+            return getExternalStorageMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE;
         }
 
         private void killAppForOpChange(int code, int uid) {
diff --git a/services/core/java/com/android/server/SystemUpdateManagerService.java b/services/core/java/com/android/server/SystemUpdateManagerService.java
index 61a7d00..fcba9b5 100644
--- a/services/core/java/com/android/server/SystemUpdateManagerService.java
+++ b/services/core/java/com/android/server/SystemUpdateManagerService.java
@@ -201,7 +201,7 @@
 
     // Performs I/O work only, without validating the loaded info.
     @Nullable
-    private PersistableBundle readInfoFileLocked(XmlPullParser parser)
+    private PersistableBundle readInfoFileLocked(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index ebeec39..95af842 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -29,6 +29,10 @@
         {
             "name": "CtsScopedStorageHostTest",
             "file_patterns": ["StorageManagerService\\.java"]
+        },
+        {
+            "name": "CtsScopedStorageDeviceOnlyTest",
+            "file_patterns": ["StorageManagerService\\.java"]
         }
     ]
 }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
index 1c77a7f..b379b5d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
@@ -23,25 +23,21 @@
 import android.annotation.NonNull;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.PackageUtils;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
-import com.android.server.accounts.AccountsDb.DeDatabaseHelper;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -163,7 +159,7 @@
                 }
                 try {
                     ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
-                    final XmlSerializer serializer = new FastXmlSerializer();
+                    final TypedXmlSerializer serializer = Xml.newFastSerializer();
                     serializer.setOutput(dataStream, StandardCharsets.UTF_8.name());
                     serializer.startDocument(null, true);
                     serializer.startTag(null, TAG_PERMISSIONS);
@@ -216,7 +212,7 @@
     public void restoreAccountAccessPermissions(byte[] data, int userId) {
         try {
             ByteArrayInputStream dataStream = new ByteArrayInputStream(data);
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(dataStream, StandardCharsets.UTF_8.name());
             PackageManager packageManager = mAccountManagerService.mContext.getPackageManager();
 
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f59af76..d99b195 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -1947,8 +1947,7 @@
                                 + tagName);
                         return keyMap;
                     }
-                    int keystoreVersion = Integer.parseInt(
-                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
                     if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
                         Slog.e(TAG, "Keystore version=" + keystoreVersion
                                 + " not supported (max_supported="
@@ -2068,8 +2067,7 @@
                                 + tagName);
                         return trustedNetworks;
                     }
-                    int keystoreVersion = Integer.parseInt(
-                            parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+                    int keystoreVersion = parser.getAttributeInt(null, XML_ATTRIBUTE_VERSION);
                     if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
                         Slog.e(TAG, "Keystore version=" + keystoreVersion
                                 + " not supported (max_supported="
@@ -2148,7 +2146,7 @@
                 serializer.startDocument(null, true);
 
                 serializer.startTag(null, XML_KEYSTORE_START_TAG);
-                serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
+                serializer.attributeInt(null, XML_ATTRIBUTE_VERSION, KEYSTORE_VERSION);
                 for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
                     serializer.startTag(null, XML_TAG_ADB_KEY);
                     serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8729026..4165200 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4987,14 +4987,16 @@
      *  - the first arg isn't the flattened component name of an existing service:
      *    dump all services whose component contains the first arg as a substring
      */
-    protected boolean dumpService(FileDescriptor fd, PrintWriter pw, final String name,
+    protected boolean dumpService(FileDescriptor fd, PrintWriter pw, String name, int[] users,
             String[] args, int opti, boolean dumpAll) {
         final ArrayList<ServiceRecord> services = new ArrayList<>();
 
         final Predicate<ServiceRecord> filter = DumpUtils.filterRecord(name);
 
         synchronized (mAm) {
-            int[] users = mAm.mUserController.getUsers();
+            if (users == null) {
+                users = mAm.mUserController.getUsers();
+            }
 
             for (int user : users) {
                 ServiceMap smap = mServiceMap.get(user);
@@ -5039,11 +5041,13 @@
         String innerPrefix = prefix + "  ";
         synchronized (mAm) {
             pw.print(prefix); pw.print("SERVICE ");
-                    pw.print(r.shortInstanceName); pw.print(" ");
-                    pw.print(Integer.toHexString(System.identityHashCode(r)));
-                    pw.print(" pid=");
-                    if (r.app != null) pw.println(r.app.pid);
-                    else pw.println("(not running)");
+            pw.print(r.shortInstanceName); pw.print(" ");
+            pw.print(Integer.toHexString(System.identityHashCode(r)));
+            pw.print(" pid=");
+            if (r.app != null) {
+                pw.print(r.app.pid);
+                pw.print(" user="); pw.println(r.userId);
+            } else pw.println("(not running)");
             if (dumpAll) {
                 r.dump(pw, innerPrefix);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 75e8b13..df1081e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5644,7 +5644,7 @@
 
     @Override
     public List<RunningTaskInfo> getTasks(int maxNum) {
-        return mActivityTaskManager.getTasks(maxNum);
+        return mActivityTaskManager.getTasks(maxNum, false /* filterForVisibleRecents */);
     }
 
     @Override
@@ -8664,17 +8664,30 @@
             } else if ("service".equals(cmd)) {
                 String[] newArgs;
                 String name;
+                int[] users = null;
                 if (opti >= args.length) {
                     name = null;
                     newArgs = EMPTY_STRING_ARRAY;
                 } else {
                     name = args[opti];
                     opti++;
+                    if ("--user".equals(name) && opti < args.length) {
+                        int userId = UserHandle.parseUserArg(args[opti]);
+                        opti++;
+                        if (userId != UserHandle.USER_ALL) {
+                            if (userId == UserHandle.USER_CURRENT) {
+                                userId = getCurrentUser().id;
+                            }
+                            users = new int[] { userId };
+                        }
+                        name = args[opti];
+                        opti++;
+                    }
                     newArgs = new String[args.length - opti];
                     if (args.length > 2) System.arraycopy(args, opti, newArgs, 0,
                             args.length - opti);
                 }
-                if (!mServices.dumpService(fd, pw, name, newArgs, 0, dumpAll)) {
+                if (!mServices.dumpService(fd, pw, name, users, newArgs, 0, dumpAll)) {
                     pw.println("No services match: " + name);
                     pw.println("Use -h for help.");
                 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 28afcbb..7299e81 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -88,6 +88,8 @@
         DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+        DeviceConfig.NAMESPACE_STATSD_NATIVE,
+        DeviceConfig.NAMESPACE_STATSD_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
     };
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index da47040..d4e2d27 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -124,7 +124,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.os.storage.StorageManagerInternal;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -155,10 +154,8 @@
 import com.android.internal.app.IAppOpsStartedCallback;
 import com.android.internal.app.MessageSamplingConfig;
 import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -175,7 +172,6 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -185,7 +181,6 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
@@ -1057,19 +1052,20 @@
             }
 
             int numInProgressEvents = mInProgressEvents.size();
+            List<IBinder> binders = new ArrayList<>(mInProgressEvents.keySet());
             for (int i = 0; i < numInProgressEvents; i++) {
-                InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+                InProgressStartOpEvent event = mInProgressEvents.get(binders.get(i));
 
-                if (event.getUidState() != newState) {
+                if (event != null && event.getUidState() != newState) {
                     try {
                         // Remove all but one unfinished start count and then call finished() to
                         // remove start event object
                         int numPreviousUnfinishedStarts = event.numUnfinishedStarts;
                         event.numUnfinishedStarts = 1;
-                        finished(event.getClientId(), false);
-
                         OpEventProxyInfo proxy = event.getProxy();
 
+                        finished(event.getClientId(), false);
+
                         // Call started() to add a new start event object and then add the
                         // previously removed unfinished start counts back
                         if (proxy != null) {
@@ -1079,7 +1075,11 @@
                             started(event.getClientId(), Process.INVALID_UID, null, null, newState,
                                     OP_FLAG_SELF, false);
                         }
-                        event.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
+
+                        InProgressStartOpEvent newEvent = mInProgressEvents.get(binders.get(i));
+                        if (newEvent != null) {
+                            newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
+                        }
                     } catch (RemoteException e) {
                         if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
                     }
@@ -1764,26 +1764,6 @@
                     }
                 });
 
-        if (!StorageManager.hasIsolatedStorage()) {
-            StorageManagerInternal storageManagerInternal = LocalServices.getService(
-                    StorageManagerInternal.class);
-            storageManagerInternal.addExternalStoragePolicy(
-                    new StorageManagerInternal.ExternalStorageMountPolicy() {
-                        @Override
-                        public int getMountMode(int uid, String packageName) {
-                            if (Process.isIsolated(uid)) {
-                                return Zygote.MOUNT_EXTERNAL_NONE;
-                            }
-                            return Zygote.MOUNT_EXTERNAL_DEFAULT;
-                        }
-
-                        @Override
-                        public boolean hasExternalStorage(int uid, String packageName) {
-                            final int mountMode = getMountMode(uid, packageName);
-                            return mountMode != Zygote.MOUNT_EXTERNAL_NONE;
-                        }
-                    });
-        }
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
     }
 
@@ -4285,10 +4265,7 @@
                         throw new IllegalStateException("no start tag found");
                     }
 
-                    final String versionString = parser.getAttributeValue(null, "v");
-                    if (versionString != null) {
-                        oldVersion = Integer.parseInt(versionString);
-                    }
+                    oldVersion = parser.getAttributeInt(null, "v", NO_VERSION);
 
                     int outerDepth = parser.getDepth();
                     while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4388,9 +4365,9 @@
         scheduleFastWriteLocked();
     }
 
-    private void readUidOps(XmlPullParser parser) throws NumberFormatException,
+    private void readUidOps(TypedXmlPullParser parser) throws NumberFormatException,
             XmlPullParserException, IOException {
-        final int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        final int uid = parser.getAttributeInt(null, "n");
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4401,8 +4378,8 @@
 
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                final int code = Integer.parseInt(parser.getAttributeValue(null, "n"));
-                final int mode = Integer.parseInt(parser.getAttributeValue(null, "m"));
+                final int code = parser.getAttributeInt(null, "n");
+                final int mode = parser.getAttributeInt(null, "m");
                 setUidMode(code, uid, mode);
             } else {
                 Slog.w(TAG, "Unknown element under <uid-ops>: "
@@ -4412,7 +4389,7 @@
         }
     }
 
-    private void readPackage(XmlPullParser parser)
+    private void readPackage(TypedXmlPullParser parser)
             throws NumberFormatException, XmlPullParserException, IOException {
         String pkgName = parser.getAttributeValue(null, "n");
         int outerDepth = parser.getDepth();
@@ -4434,9 +4411,9 @@
         }
     }
 
-    private void readUid(XmlPullParser parser, String pkgName)
+    private void readUid(TypedXmlPullParser parser, String pkgName)
             throws NumberFormatException, XmlPullParserException, IOException {
-        int uid = Integer.parseInt(parser.getAttributeValue(null, "n"));
+        int uid = parser.getAttributeInt(null, "n");
         final UidState uidState = getUidStateLocked(uid, true);
         int outerDepth = parser.getDepth();
         int type;
@@ -4457,19 +4434,20 @@
         uidState.evalForegroundOps(mOpModeWatchers);
     }
 
-    private void readAttributionOp(XmlPullParser parser, @NonNull Op parent,
-            @Nullable String attribution) throws NumberFormatException, IOException {
+    private void readAttributionOp(TypedXmlPullParser parser, @NonNull Op parent,
+            @Nullable String attribution)
+            throws NumberFormatException, IOException, XmlPullParserException {
         final AttributedOp attributedOp = parent.getOrCreateAttribution(parent, attribution);
 
-        final long key = XmlUtils.readLongAttribute(parser, "n");
+        final long key = parser.getAttributeLong(null, "n");
         final int uidState = extractUidStateFromKey(key);
         final int opFlags = extractFlagsFromKey(key);
 
-        final long accessTime = XmlUtils.readLongAttribute(parser, "t", 0);
-        final long rejectTime = XmlUtils.readLongAttribute(parser, "r", 0);
-        final long accessDuration = XmlUtils.readLongAttribute(parser, "d", -1);
+        final long accessTime = parser.getAttributeLong(null, "t", 0);
+        final long rejectTime = parser.getAttributeLong(null, "r", 0);
+        final long accessDuration = parser.getAttributeLong(null, "d", -1);
         final String proxyPkg = XmlUtils.readStringAttribute(parser, "pp");
-        final int proxyUid = XmlUtils.readIntAttribute(parser, "pu", Process.INVALID_UID);
+        final int proxyUid = parser.getAttributeInt(null, "pu", Process.INVALID_UID);
         final String proxyAttributionTag = XmlUtils.readStringAttribute(parser, "pc");
 
         if (accessTime > 0) {
@@ -4481,14 +4459,13 @@
         }
     }
 
-    private void readOp(XmlPullParser parser, @NonNull UidState uidState, @NonNull String pkgName)
-            throws NumberFormatException,
-        XmlPullParserException, IOException {
-        int opCode = Integer.parseInt(parser.getAttributeValue(null, "n"));
+    private void readOp(TypedXmlPullParser parser,
+            @NonNull UidState uidState, @NonNull String pkgName)
+            throws NumberFormatException, XmlPullParserException, IOException {
+        int opCode = parser.getAttributeInt(null, "n");
         Op op = new Op(uidState, pkgName, opCode, uidState.uid);
 
-        final int mode = XmlUtils.readIntAttribute(parser, "m",
-                AppOpsManager.opToDefaultMode(op.op));
+        final int mode = parser.getAttributeInt(null, "m", AppOpsManager.opToDefaultMode(op.op));
         op.mode = mode;
 
         int outerDepth = parser.getDepth();
@@ -4535,7 +4512,7 @@
                 TypedXmlSerializer out = Xml.resolveSerializer(stream);
                 out.startDocument(null, true);
                 out.startTag(null, "app-ops");
-                out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
+                out.attributeInt(null, "v", CURRENT_VERSION);
 
                 SparseArray<SparseIntArray> uidStatesClone;
                 synchronized (this) {
@@ -4565,15 +4542,14 @@
                     SparseIntArray opModes = uidStatesClone.valueAt(uidStateNum);
                     if (opModes != null && opModes.size() > 0) {
                         out.startTag(null, "uid");
-                        out.attribute(null, "n",
-                                Integer.toString(uidStatesClone.keyAt(uidStateNum)));
+                        out.attributeInt(null, "n", uidStatesClone.keyAt(uidStateNum));
                         final int opCount = opModes.size();
                         for (int opCountNum = 0; opCountNum < opCount; opCountNum++) {
                             final int op = opModes.keyAt(opCountNum);
                             final int mode = opModes.valueAt(opCountNum);
                             out.startTag(null, "op");
-                            out.attribute(null, "n", Integer.toString(op));
-                            out.attribute(null, "m", Integer.toString(mode));
+                            out.attributeInt(null, "n", op);
+                            out.attributeInt(null, "m", mode);
                             out.endTag(null, "op");
                         }
                         out.endTag(null, "uid");
@@ -4593,14 +4569,14 @@
                             out.attribute(null, "n", lastPkg);
                         }
                         out.startTag(null, "uid");
-                        out.attribute(null, "n", Integer.toString(pkg.getUid()));
+                        out.attributeInt(null, "n", pkg.getUid());
                         List<AppOpsManager.OpEntry> ops = pkg.getOps();
                         for (int j=0; j<ops.size(); j++) {
                             AppOpsManager.OpEntry op = ops.get(j);
                             out.startTag(null, "op");
-                            out.attribute(null, "n", Integer.toString(op.getOp()));
+                            out.attributeInt(null, "n", op.getOp());
                             if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
-                                out.attribute(null, "m", Integer.toString(op.getMode()));
+                                out.attributeInt(null, "m", op.getMode());
                             }
 
                             for (String attributionTag : op.getAttributedOpEntries().keySet()) {
@@ -4644,15 +4620,15 @@
                                     if (attributionTag != null) {
                                         out.attribute(null, "id", attributionTag);
                                     }
-                                    out.attribute(null, "n", Long.toString(key));
+                                    out.attributeLong(null, "n", key);
                                     if (accessTime > 0) {
-                                        out.attribute(null, "t", Long.toString(accessTime));
+                                        out.attributeLong(null, "t", accessTime);
                                     }
                                     if (rejectTime > 0) {
-                                        out.attribute(null, "r", Long.toString(rejectTime));
+                                        out.attributeLong(null, "r", rejectTime);
                                     }
                                     if (accessDuration > 0) {
-                                        out.attribute(null, "d", Long.toString(accessDuration));
+                                        out.attributeLong(null, "d", accessDuration);
                                     }
                                     if (proxyPkg != null) {
                                         out.attribute(null, "pp", proxyPkg);
@@ -4661,7 +4637,7 @@
                                         out.attribute(null, "pc", proxyAttributionTag);
                                     }
                                     if (proxyUid >= 0) {
-                                        out.attribute(null, "pu", Integer.toString(proxyUid));
+                                        out.attributeInt(null, "pu", proxyUid);
                                     }
                                     out.endTag(null, "st");
                                 }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index f49b5dc..676fcd0 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -49,6 +49,8 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -1184,19 +1186,18 @@
             }
             List<HistoricalOps> allOps = null;
             try (FileInputStream stream = new FileInputStream(file)) {
-                final XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(stream, StandardCharsets.UTF_8.name());
+                final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
                 XmlUtils.beginDocument(parser, TAG_HISTORY);
 
                 // We haven't released version 1 and have more detailed
                 // accounting - just nuke the current state
-                final int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
+                final int version = parser.getAttributeInt(null, ATTR_VERSION);
                 if (CURRENT_VERSION == 2 && version < CURRENT_VERSION) {
                     throw new IllegalStateException("Dropping unsupported history "
                             + "version 1 for file:" + file);
                 }
 
-                final long overflowMillis = XmlUtils.readLongAttribute(parser, ATTR_OVERFLOW, 0);
+                final long overflowMillis = parser.getAttributeLong(null, ATTR_OVERFLOW, 0);
                 final int depth = parser.getDepth();
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     if (TAG_OPS.equals(parser.getName())) {
@@ -1235,15 +1236,16 @@
         }
 
         private @Nullable HistoricalOps readeHistoricalOpsDLocked(
-                @NonNull XmlPullParser parser, int filterUid, @Nullable String filterPackageName,
+                @NonNull TypedXmlPullParser parser, int filterUid,
+                @Nullable String filterPackageName,
                 @Nullable String filterAttributionTag, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, long filterBeginTimeMillis,
                 long filterEndTimeMillis, @OpFlags int filterFlags,
                 @Nullable long[] cumulativeOverflowMillis)
                 throws IOException, XmlPullParserException {
-            final long beginTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_BEGIN_TIME, 0)
+            final long beginTimeMillis = parser.getAttributeLong(null, ATTR_BEGIN_TIME, 0)
                     + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
-            final long endTimeMillis = XmlUtils.readLongAttribute(parser, ATTR_END_TIME, 0)
+            final long endTimeMillis = parser.getAttributeLong(null, ATTR_END_TIME, 0)
                     + (cumulativeOverflowMillis != null ? cumulativeOverflowMillis[0] : 0);
             // Keep reading as subsequent records may start matching
             if (filterEndTimeMillis < beginTimeMillis) {
@@ -1280,12 +1282,12 @@
         }
 
         private @Nullable HistoricalOps readHistoricalUidOpsDLocked(
-                @Nullable HistoricalOps ops, @NonNull XmlPullParser parser, int filterUid,
+                @Nullable HistoricalOps ops, @NonNull TypedXmlPullParser parser, int filterUid,
                 @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
-            final int uid = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            final int uid = parser.getAttributeInt(null, ATTR_NAME);
             if ((filter & FILTER_BY_UID) != 0 && filterUid != uid) {
                 XmlUtils.skipCurrentTag(parser);
                 return null;
@@ -1305,7 +1307,7 @@
         }
 
         private @Nullable HistoricalOps readHistoricalPackageOpsDLocked(
-                @Nullable HistoricalOps ops, int uid, @NonNull XmlPullParser parser,
+                @Nullable HistoricalOps ops, int uid, @NonNull TypedXmlPullParser parser,
                 @Nullable String filterPackageName, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
@@ -1331,7 +1333,7 @@
 
         private @Nullable HistoricalOps readHistoricalAttributionOpsDLocked(
                 @Nullable HistoricalOps ops, int uid, String packageName,
-                @NonNull XmlPullParser parser, @Nullable String filterAttributionTag,
+                @NonNull TypedXmlPullParser parser, @Nullable String filterAttributionTag,
                 @Nullable String[] filterOpNames, @HistoricalOpsRequestFilter int filter,
                 @OpFlags int filterFlags, double filterScale)
                 throws IOException, XmlPullParserException {
@@ -1357,11 +1359,11 @@
 
         private @Nullable HistoricalOps readHistoricalOpDLocked(@Nullable HistoricalOps ops,
                 int uid, @NonNull String packageName, @Nullable String attributionTag,
-                @NonNull XmlPullParser parser, @Nullable String[] filterOpNames,
+                @NonNull TypedXmlPullParser parser, @Nullable String[] filterOpNames,
                 @HistoricalOpsRequestFilter int filter, @OpFlags int filterFlags,
                 double filterScale)
                 throws IOException, XmlPullParserException {
-            final int op = XmlUtils.readIntAttribute(parser, ATTR_NAME);
+            final int op = parser.getAttributeInt(null, ATTR_NAME);
             if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(filterOpNames,
                     AppOpsManager.opToPublicName(op))) {
                 XmlUtils.skipCurrentTag(parser);
@@ -1382,15 +1384,15 @@
 
         private @Nullable HistoricalOps readStateDLocked(@Nullable HistoricalOps ops,
                 int uid, @NonNull String packageName, @Nullable String attributionTag, int op,
-                @NonNull XmlPullParser parser, @OpFlags int filterFlags, double filterScale)
-                throws IOException {
-            final long key = XmlUtils.readLongAttribute(parser, ATTR_NAME);
+                @NonNull TypedXmlPullParser parser, @OpFlags int filterFlags, double filterScale)
+                throws IOException, XmlPullParserException {
+            final long key = parser.getAttributeLong(null, ATTR_NAME);
             final int flags = AppOpsManager.extractFlagsFromKey(key) & filterFlags;
             if (flags == 0) {
                 return null;
             }
             final int uidState = AppOpsManager.extractUidStateFromKey(key);
-            long accessCount = XmlUtils.readLongAttribute(parser, ATTR_ACCESS_COUNT, 0);
+            long accessCount = parser.getAttributeLong(null, ATTR_ACCESS_COUNT, 0);
             if (accessCount > 0) {
                 if (!Double.isNaN(filterScale)) {
                     accessCount = (long) HistoricalOps.round(
@@ -1402,7 +1404,7 @@
                 ops.increaseAccessCount(op, uid, packageName, attributionTag, uidState, flags,
                         accessCount);
             }
-            long rejectCount = XmlUtils.readLongAttribute(parser, ATTR_REJECT_COUNT, 0);
+            long rejectCount = parser.getAttributeLong(null, ATTR_REJECT_COUNT, 0);
             if (rejectCount > 0) {
                 if (!Double.isNaN(filterScale)) {
                     rejectCount = (long) HistoricalOps.round(
@@ -1414,7 +1416,7 @@
                 ops.increaseRejectCount(op, uid, packageName, attributionTag, uidState, flags,
                         rejectCount);
             }
-            long accessDuration =  XmlUtils.readLongAttribute(parser, ATTR_ACCESS_DURATION, 0);
+            long accessDuration =  parser.getAttributeLong(null, ATTR_ACCESS_DURATION, 0);
             if (accessDuration > 0) {
                 if (!Double.isNaN(filterScale)) {
                     accessDuration = (long) HistoricalOps.round(
@@ -1433,16 +1435,14 @@
                 long intervalOverflowMillis, @NonNull File file) throws IOException {
             final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file);
             try {
-                final XmlSerializer serializer = Xml.newSerializer();
-                serializer.setOutput(output, StandardCharsets.UTF_8.name());
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(output);
                 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
                         true);
                 serializer.startDocument(null, true);
                 serializer.startTag(null, TAG_HISTORY);
-                serializer.attribute(null, ATTR_VERSION, String.valueOf(CURRENT_VERSION));
+                serializer.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
                 if (intervalOverflowMillis != 0) {
-                    serializer.attribute(null, ATTR_OVERFLOW,
-                            Long.toString(intervalOverflowMillis));
+                    serializer.attributeLong(null, ATTR_OVERFLOW, intervalOverflowMillis);
                 }
                 if (allOps != null) {
                     final int opsCount = allOps.size();
@@ -1461,10 +1461,10 @@
         }
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOps ops,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_OPS);
-            serializer.attribute(null, ATTR_BEGIN_TIME, Long.toString(ops.getBeginTimeMillis()));
-            serializer.attribute(null, ATTR_END_TIME, Long.toString(ops.getEndTimeMillis()));
+            serializer.attributeLong(null, ATTR_BEGIN_TIME, ops.getBeginTimeMillis());
+            serializer.attributeLong(null, ATTR_END_TIME, ops.getEndTimeMillis());
             final int uidCount = ops.getUidCount();
             for (int i = 0; i < uidCount; i++) {
                 final HistoricalUidOps uidOp = ops.getUidOpsAt(i);
@@ -1474,9 +1474,9 @@
         }
 
         private void writeHistoricalUidOpsDLocked(@NonNull HistoricalUidOps uidOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_UID);
-            serializer.attribute(null, ATTR_NAME, Integer.toString(uidOps.getUid()));
+            serializer.attributeInt(null, ATTR_NAME, uidOps.getUid());
             final int packageCount = uidOps.getPackageCount();
             for (int i = 0; i < packageCount; i++) {
                 final HistoricalPackageOps packageOps = uidOps.getPackageOpsAt(i);
@@ -1486,7 +1486,7 @@
         }
 
         private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_PACKAGE);
             serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
             final int numAttributions = packageOps.getAttributedOpsCount();
@@ -1499,7 +1499,7 @@
 
         private void writeHistoricalAttributionOpsDLocked(
                 @NonNull AppOpsManager.AttributedHistoricalOps attributionOps,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_ATTRIBUTION);
             XmlUtils.writeStringAttribute(serializer, ATTR_NAME, attributionOps.getTag());
             final int opCount = attributionOps.getOpCount();
@@ -1511,13 +1511,13 @@
         }
 
         private void writeHistoricalOpDLocked(@NonNull HistoricalOp op,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             final LongSparseArray keys = op.collectKeys();
             if (keys == null || keys.size() <= 0) {
                 return;
             }
             serializer.startTag(null, TAG_OP);
-            serializer.attribute(null, ATTR_NAME, Integer.toString(op.getOpCode()));
+            serializer.attributeInt(null, ATTR_NAME, op.getOpCode());
             final int keyCount = keys.size();
             for (int i = 0; i < keyCount; i++) {
                 writeStateOnLocked(op, keys.keyAt(i), serializer);
@@ -1526,7 +1526,7 @@
         }
 
         private void writeStateOnLocked(@NonNull HistoricalOp op, long key,
-                @NonNull XmlSerializer serializer) throws IOException {
+                @NonNull TypedXmlSerializer serializer) throws IOException {
             final int uidState = AppOpsManager.extractUidStateFromKey(key);
             final int flags = AppOpsManager.extractFlagsFromKey(key);
 
@@ -1539,15 +1539,15 @@
             }
 
             serializer.startTag(null, TAG_STATE);
-            serializer.attribute(null, ATTR_NAME, Long.toString(key));
+            serializer.attributeLong(null, ATTR_NAME, key);
             if (accessCount > 0) {
-                serializer.attribute(null, ATTR_ACCESS_COUNT, Long.toString(accessCount));
+                serializer.attributeLong(null, ATTR_ACCESS_COUNT, accessCount);
             }
             if (rejectCount > 0) {
-                serializer.attribute(null, ATTR_REJECT_COUNT, Long.toString(rejectCount));
+                serializer.attributeLong(null, ATTR_REJECT_COUNT, rejectCount);
             }
             if (accessDuration > 0) {
-                serializer.attribute(null, ATTR_ACCESS_DURATION, Long.toString(accessDuration));
+                serializer.attributeLong(null, ATTR_ACCESS_DURATION, accessDuration);
             }
             serializer.endTag(null, TAG_STATE);
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index ff410fc..0943c9ca 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -49,7 +49,7 @@
 
     private final boolean mIsStrongBiometric;
     private final boolean mRequireConfirmation;
-    private final IActivityTaskManager mActivityTaskManager;
+    private final ActivityTaskManager mActivityTaskManager;
     @Nullable private final TaskStackListener mTaskStackListener;
     private final LockoutTracker mLockoutTracker;
     private final boolean mIsRestricted;
@@ -71,7 +71,7 @@
         mIsStrongBiometric = isStrongBiometric;
         mOperationId = operationId;
         mRequireConfirmation = requireConfirmation;
-        mActivityTaskManager = ActivityTaskManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getInstance();
         mTaskStackListener = taskStackListener;
         mLockoutTracker = lockoutTracker;
         mIsRestricted = restricted;
@@ -146,29 +146,24 @@
             // Ensure authentication only succeeds if the client activity is on top or is keyguard.
             boolean isBackgroundAuth = false;
             if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) {
-                try {
-                    final List<ActivityManager.RunningTaskInfo> tasks =
-                            mActivityTaskManager.getTasks(1);
-                    if (tasks == null || tasks.isEmpty()) {
-                        Slog.e(TAG, "No running tasks reported");
+                final List<ActivityManager.RunningTaskInfo> tasks =
+                        mActivityTaskManager.getTasks(1);
+                if (tasks == null || tasks.isEmpty()) {
+                    Slog.e(TAG, "No running tasks reported");
+                    isBackgroundAuth = true;
+                } else {
+                    final ComponentName topActivity = tasks.get(0).topActivity;
+                    if (topActivity == null) {
+                        Slog.e(TAG, "Unable to get top activity");
                         isBackgroundAuth = true;
                     } else {
-                        final ComponentName topActivity = tasks.get(0).topActivity;
-                        if (topActivity == null) {
-                            Slog.e(TAG, "Unable to get top activity");
+                        final String topPackage = topActivity.getPackageName();
+                        if (!topPackage.contentEquals(getOwnerString())) {
+                            Slog.e(TAG, "Background authentication detected, top: " + topPackage
+                                    + ", client: " + this);
                             isBackgroundAuth = true;
-                        } else {
-                            final String topPackage = topActivity.getPackageName();
-                            if (!topPackage.contentEquals(getOwnerString())) {
-                                Slog.e(TAG, "Background authentication detected, top: " + topPackage
-                                        + ", client: " + this);
-                                isBackgroundAuth = true;
-                            }
                         }
                     }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to get running tasks", e);
-                    isBackgroundAuth = true;
                 }
             }
 
@@ -198,11 +193,7 @@
                 }
 
                 if (mTaskStackListener != null) {
-                    try {
-                        mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Could not unregister task stack listener", e);
-                    }
+                    mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
                 }
 
                 final byte[] byteToken = new byte[hardwareAuthToken.size()];
@@ -290,11 +281,7 @@
         }
 
         if (mTaskStackListener != null) {
-            try {
-                mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Could not register task stack listener", e);
-            }
+            mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         }
 
         if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString());
@@ -309,11 +296,7 @@
         super.cancel();
 
         if (mTaskStackListener != null) {
-            try {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Could not unregister task stack listener", e);
-            }
+            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
         }
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
index a8250ac..d588b8d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java
@@ -22,6 +22,7 @@
 import android.os.AsyncTask;
 import android.os.Environment;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -80,7 +81,7 @@
     /**
      * @return
      */
-    protected abstract void parseBiometricsLocked(XmlPullParser parser)
+    protected abstract void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException;
 
 
@@ -176,8 +177,7 @@
             return;
         }
         try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parseStateLocked(parser);
 
         } catch (XmlPullParserException | IOException e) {
@@ -189,7 +189,7 @@
     }
 
     @GuardedBy("this")
-    private void parseStateLocked(XmlPullParser parser)
+    private void parseStateLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
index d30c3c8..78e875b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java
@@ -20,6 +20,8 @@
 import android.hardware.face.Face;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,7 +31,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -87,8 +88,7 @@
         try {
             out = destination.startWrite();
 
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, "utf-8");
+            TypedXmlSerializer serializer = Xml.resolveSerializer(out);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_FACES);
@@ -97,9 +97,9 @@
             for (int i = 0; i < count; i++) {
                 Face f = faces.get(i);
                 serializer.startTag(null, TAG_FACE);
-                serializer.attribute(null, ATTR_FACE_ID, Integer.toString(f.getBiometricId()));
+                serializer.attributeInt(null, ATTR_FACE_ID, f.getBiometricId());
                 serializer.attribute(null, ATTR_NAME, f.getName().toString());
-                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(f.getDeviceId()));
+                serializer.attributeLong(null, ATTR_DEVICE_ID, f.getDeviceId());
                 serializer.endTag(null, TAG_FACE);
             }
 
@@ -119,7 +119,7 @@
 
     @GuardedBy("this")
     @Override
-    protected void parseBiometricsLocked(XmlPullParser parser)
+    protected void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         int type;
@@ -132,9 +132,9 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_FACE)) {
                 String name = parser.getAttributeValue(null, ATTR_NAME);
-                String faceId = parser.getAttributeValue(null, ATTR_FACE_ID);
-                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
-                mBiometrics.add(new Face(name, Integer.parseInt(faceId), Integer.parseInt(deviceId)));
+                int faceId = parser.getAttributeInt(null, ATTR_FACE_ID);
+                int deviceId = parser.getAttributeInt(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Face(name, faceId, deviceId));
             }
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index cc92986..cec1cb8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -77,7 +77,7 @@
     @NonNull private final Handler mHandler;
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
     @NonNull private final UsageStats mUsageStats;
-    @NonNull private final IActivityTaskManager mActivityTaskManager;
+    @NonNull private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final BiometricTaskStackListener mTaskStackListener;
 
     @Nullable private IFace mDaemon;
@@ -97,22 +97,18 @@
                         continue; // Keyguard is always allowed
                     }
 
-                    try {
-                        final List<ActivityManager.RunningTaskInfo> runningTasks =
-                                mActivityTaskManager.getTasks(1);
-                        if (!runningTasks.isEmpty()) {
-                            final String topPackage =
-                                    runningTasks.get(0).topActivity.getPackageName();
-                            if (!topPackage.contentEquals(client.getOwnerString())
-                                    && !client.isAlreadyDone()) {
-                                Slog.e(getTag(), "Stopping background authentication, top: "
-                                        + topPackage + " currentClient: " + client);
-                                mSensors.valueAt(i).getScheduler()
-                                        .cancelAuthentication(client.getToken());
-                            }
+                    final List<ActivityManager.RunningTaskInfo> runningTasks =
+                            mActivityTaskManager.getTasks(1);
+                    if (!runningTasks.isEmpty()) {
+                        final String topPackage =
+                                runningTasks.get(0).topActivity.getPackageName();
+                        if (!topPackage.contentEquals(client.getOwnerString())
+                                && !client.isAlreadyDone()) {
+                            Slog.e(getTag(), "Stopping background authentication, top: "
+                                    + topPackage + " currentClient: " + client);
+                            mSensors.valueAt(i).getScheduler()
+                                    .cancelAuthentication(client.getToken());
                         }
-                    } catch (RemoteException e) {
-                        Slog.e(getTag(), "Unable to get running tasks", e);
                     }
                 }
             });
@@ -129,7 +125,7 @@
         mHandler = new Handler(Looper.getMainLooper());
         mUsageStats = new UsageStats(context);
         mLockoutResetDispatcher = lockoutResetDispatcher;
-        mActivityTaskManager = ActivityTaskManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getInstance();
         mTaskStackListener = new BiometricTaskStackListener();
 
         for (SensorProps prop : props) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
index e56c8d5..671e08b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java
@@ -20,6 +20,8 @@
 import android.hardware.fingerprint.Fingerprint;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,7 +31,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -88,8 +89,7 @@
         try {
             out = destination.startWrite();
 
-            XmlSerializer serializer = Xml.newSerializer();
-            serializer.setOutput(out, "utf-8");
+            TypedXmlSerializer serializer = Xml.resolveSerializer(out);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             serializer.startDocument(null, true);
             serializer.startTag(null, TAG_FINGERPRINTS);
@@ -98,10 +98,10 @@
             for (int i = 0; i < count; i++) {
                 Fingerprint fp = fingerprints.get(i);
                 serializer.startTag(null, TAG_FINGERPRINT);
-                serializer.attribute(null, ATTR_FINGER_ID, Integer.toString(fp.getBiometricId()));
+                serializer.attributeInt(null, ATTR_FINGER_ID, fp.getBiometricId());
                 serializer.attribute(null, ATTR_NAME, fp.getName().toString());
-                serializer.attribute(null, ATTR_GROUP_ID, Integer.toString(fp.getGroupId()));
-                serializer.attribute(null, ATTR_DEVICE_ID, Long.toString(fp.getDeviceId()));
+                serializer.attributeInt(null, ATTR_GROUP_ID, fp.getGroupId());
+                serializer.attributeLong(null, ATTR_DEVICE_ID, fp.getDeviceId());
                 serializer.endTag(null, TAG_FINGERPRINT);
             }
 
@@ -121,7 +121,7 @@
 
     @GuardedBy("this")
     @Override
-    protected void parseBiometricsLocked(XmlPullParser parser)
+    protected void parseBiometricsLocked(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
 
         final int outerDepth = parser.getDepth();
@@ -135,11 +135,10 @@
             String tagName = parser.getName();
             if (tagName.equals(TAG_FINGERPRINT)) {
                 String name = parser.getAttributeValue(null, ATTR_NAME);
-                String groupId = parser.getAttributeValue(null, ATTR_GROUP_ID);
-                String fingerId = parser.getAttributeValue(null, ATTR_FINGER_ID);
-                String deviceId = parser.getAttributeValue(null, ATTR_DEVICE_ID);
-                mBiometrics.add(new Fingerprint(name, Integer.parseInt(groupId),
-                        Integer.parseInt(fingerId), Long.parseLong(deviceId)));
+                int groupId = parser.getAttributeInt(null, ATTR_GROUP_ID);
+                int fingerId = parser.getAttributeInt(null, ATTR_FINGER_ID);
+                long deviceId = parser.getAttributeLong(null, ATTR_DEVICE_ID);
+                mBiometrics.add(new Fingerprint(name, groupId, fingerId, deviceId));
             }
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 98c32cb..99c662a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -77,7 +77,7 @@
     @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon;
     @NonNull private final Handler mHandler;
     @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
-    @NonNull private final IActivityTaskManager mActivityTaskManager;
+    @NonNull private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final BiometricTaskStackListener mTaskStackListener;
 
     @Nullable private IFingerprint mDaemon;
@@ -98,22 +98,18 @@
                         continue; // Keyguard is always allowed
                     }
 
-                    try {
-                        final List<ActivityManager.RunningTaskInfo> runningTasks =
-                                mActivityTaskManager.getTasks(1);
-                        if (!runningTasks.isEmpty()) {
-                            final String topPackage =
-                                    runningTasks.get(0).topActivity.getPackageName();
-                            if (!topPackage.contentEquals(client.getOwnerString())
-                                    && !client.isAlreadyDone()) {
-                                Slog.e(getTag(), "Stopping background authentication, top: "
-                                        + topPackage + " currentClient: " + client);
-                                mSensors.valueAt(i).getScheduler()
-                                        .cancelAuthentication(client.getToken());
-                            }
+                    final List<ActivityManager.RunningTaskInfo> runningTasks =
+                            mActivityTaskManager.getTasks(1);
+                    if (!runningTasks.isEmpty()) {
+                        final String topPackage =
+                                runningTasks.get(0).topActivity.getPackageName();
+                        if (!topPackage.contentEquals(client.getOwnerString())
+                                && !client.isAlreadyDone()) {
+                            Slog.e(getTag(), "Stopping background authentication, top: "
+                                    + topPackage + " currentClient: " + client);
+                            mSensors.valueAt(i).getScheduler()
+                                    .cancelAuthentication(client.getToken());
                         }
-                    } catch (RemoteException e) {
-                        Slog.e(getTag(), "Unable to get running tasks", e);
                     }
                 }
             });
@@ -129,7 +125,7 @@
         mLazyDaemon = this::getHalInstance;
         mHandler = new Handler(Looper.getMainLooper());
         mLockoutResetDispatcher = lockoutResetDispatcher;
-        mActivityTaskManager = ActivityTaskManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getInstance();
         mTaskStackListener = new BiometricTaskStackListener();
 
         for (SensorProps prop : props) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f38dd09..7c5b7c9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -97,7 +97,7 @@
     private boolean mTestHalEnabled;
 
     final Context mContext;
-    private final IActivityTaskManager mActivityTaskManager;
+    private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
     private final BiometricScheduler mScheduler;
     private final Handler mHandler;
@@ -125,20 +125,16 @@
                     return; // Keyguard is always allowed
                 }
 
-                try {
-                    final List<ActivityManager.RunningTaskInfo> runningTasks =
-                            mActivityTaskManager.getTasks(1);
-                    if (!runningTasks.isEmpty()) {
-                        final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                        if (!topPackage.contentEquals(client.getOwnerString())
-                                && !client.isAlreadyDone()) {
-                            Slog.e(TAG, "Stopping background authentication, top: "
-                                    + topPackage + " currentClient: " + client);
-                            mScheduler.cancelAuthentication(client.getToken());
-                        }
+                final List<ActivityManager.RunningTaskInfo> runningTasks =
+                        mActivityTaskManager.getTasks(1);
+                if (!runningTasks.isEmpty()) {
+                    final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                    if (!topPackage.contentEquals(client.getOwnerString())
+                            && !client.isAlreadyDone()) {
+                        Slog.e(TAG, "Stopping background authentication, top: "
+                                + topPackage + " currentClient: " + client);
+                        mScheduler.cancelAuthentication(client.getToken());
                     }
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Unable to get running tasks", e);
                 }
             });
         }
@@ -313,7 +309,7 @@
         mContext = context;
         mScheduler = scheduler;
         mHandler = handler;
-        mActivityTaskManager = ActivityTaskManager.getService();
+        mActivityTaskManager = ActivityTaskManager.getInstance();
 
         mTaskStackListener = new BiometricTaskStackListener();
         mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index eec286a..234dcc9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -122,7 +122,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.math.BigInteger;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -152,36 +151,13 @@
 public class Vpn {
     private static final String NETWORKTYPE = "VPN";
     private static final String TAG = "Vpn";
+    private static final String VPN_PROVIDER_NAME_BASE = "VpnNetworkProvider:";
     private static final boolean LOGD = true;
 
     // Length of time (in milliseconds) that an app hosting an always-on VPN is placed on
     // the device idle allowlist during service launch and VPN bootstrap.
     private static final long VPN_LAUNCH_IDLE_ALLOWLIST_DURATION_MS = 60 * 1000;
 
-    // Settings for how much of the address space should be routed so that Vpn considers
-    // "most" of the address space is routed. This is used to determine whether this Vpn
-    // should be marked with the INTERNET capability.
-    private static final long MOST_IPV4_ADDRESSES_COUNT;
-    private static final BigInteger MOST_IPV6_ADDRESSES_COUNT;
-    static {
-        // 85% of the address space must be routed for Vpn to consider this VPN to provide
-        // INTERNET access.
-        final int howManyPercentIsMost = 85;
-
-        final long twoPower32 = 1L << 32;
-        MOST_IPV4_ADDRESSES_COUNT = twoPower32 * howManyPercentIsMost / 100;
-        final BigInteger twoPower128 = BigInteger.ONE.shiftLeft(128);
-        MOST_IPV6_ADDRESSES_COUNT = twoPower128
-                .multiply(BigInteger.valueOf(howManyPercentIsMost))
-                .divide(BigInteger.valueOf(100));
-    }
-    // How many routes to evaluate before bailing and declaring this Vpn should provide
-    // the INTERNET capability. This is necessary because computing the address space is
-    // O(n²) and this is running in the system service, so a limit is needed to alleviate
-    // the risk of attack.
-    // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
-    // is actually O(n²)+O(n²).
-    private static final int MAX_ROUTES_TO_EVALUATE = 150;
     private static final String LOCKDOWN_ALLOWLIST_SETTING_NAME =
             Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST;
     /**
@@ -198,6 +174,7 @@
     // automated reconnection
 
     private final Context mContext;
+    private final ConnectivityManager mConnectivityManager;
     // The context is for specific user which is created from mUserId
     private final Context mUserIdContext;
     @VisibleForTesting final Dependencies mDeps;
@@ -218,6 +195,7 @@
     private final INetworkManagementService mNetd;
     @VisibleForTesting
     protected VpnConfig mConfig;
+    private final NetworkProvider mNetworkProvider;
     @VisibleForTesting
     protected NetworkAgent mNetworkAgent;
     private final Looper mLooper;
@@ -401,6 +379,7 @@
             int userId, @NonNull KeyStore keyStore, SystemServices systemServices,
             Ikev2SessionCreator ikev2SessionCreator) {
         mContext = context;
+        mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
         mUserIdContext = context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);
         mDeps = deps;
         mNetd = netService;
@@ -419,13 +398,16 @@
             Log.wtf(TAG, "Problem registering observer", e);
         }
 
+        mNetworkProvider = new NetworkProvider(context, looper, VPN_PROVIDER_NAME_BASE + mUserId);
+        // This constructor is called in onUserStart and registers the provider. The provider
+        // will be unregistered in onUserStop.
+        mConnectivityManager.registerNetworkProvider(mNetworkProvider);
         mLegacyState = LegacyVpnInfo.STATE_DISCONNECTED;
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0 /* subtype */, NETWORKTYPE,
                 "" /* subtypeName */);
         mNetworkCapabilities = new NetworkCapabilities();
         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
         mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
-        updateCapabilities(null /* defaultNetwork */);
 
         loadAlwaysOnPackage(keyStore);
     }
@@ -443,12 +425,39 @@
      * Update current state, dispatching event to listeners.
      */
     @VisibleForTesting
+    @GuardedBy("this")
     protected void updateState(DetailedState detailedState, String reason) {
         if (LOGD) Log.d(TAG, "setting state=" + detailedState + ", reason=" + reason);
         mLegacyState = LegacyVpnInfo.stateFromNetworkInfo(detailedState);
         mNetworkInfo.setDetailedState(detailedState, reason, null);
-        if (mNetworkAgent != null) {
-            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        // TODO : only accept transitions when the agent is in the correct state (non-null for
+        // CONNECTED, DISCONNECTED and FAILED, null for CONNECTED).
+        // This will require a way for tests to pretend the VPN is connected that's not
+        // calling this method with CONNECTED.
+        // It will also require audit of where the code calls this method with DISCONNECTED
+        // with a null agent, which it was doing historically to make sure the agent is
+        // disconnected as this was a no-op if the agent was null.
+        switch (detailedState) {
+            case CONNECTED:
+                if (null != mNetworkAgent) {
+                    mNetworkAgent.markConnected();
+                }
+                break;
+            case DISCONNECTED:
+            case FAILED:
+                if (null != mNetworkAgent) {
+                    mNetworkAgent.unregister();
+                    mNetworkAgent = null;
+                }
+                break;
+            case CONNECTING:
+                if (null != mNetworkAgent) {
+                    throw new IllegalStateException("VPN can only go to CONNECTING state when"
+                            + " the agent is null.");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Illegal state argument " + detailedState);
         }
         updateAlwaysOnNotification(detailedState);
     }
@@ -476,7 +485,7 @@
         final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
 
         applyUnderlyingCapabilities(
-                mContext.getSystemService(ConnectivityManager.class),
+                mConnectivityManager,
                 underlyingNetworks,
                 mNetworkCapabilities,
                 isAlwaysMetered);
@@ -486,10 +495,10 @@
 
     @VisibleForTesting
     public static void applyUnderlyingCapabilities(
-            ConnectivityManager cm,
-            Network[] underlyingNetworks,
-            NetworkCapabilities caps,
-            boolean isAlwaysMetered) {
+            @NonNull final ConnectivityManager cm,
+            @Nullable final Network[] underlyingNetworks,
+            @NonNull final NetworkCapabilities caps,
+            final boolean isAlwaysMetered) {
         int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
         int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
         int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
@@ -1016,7 +1025,7 @@
             }
             mConfig = null;
 
-            updateState(DetailedState.IDLE, "prepare");
+            updateState(DetailedState.DISCONNECTED, "prepare");
             setVpnForcedLocked(mLockdown);
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -1252,7 +1261,7 @@
         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
 
         mLegacyState = LegacyVpnInfo.STATE_CONNECTING;
-        mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
+        updateState(DetailedState.CONNECTING, "agentConnect");
 
         NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
         networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
@@ -1270,20 +1279,23 @@
             mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
         }
 
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
-                    mNetworkInfo, mNetworkCapabilities, lp,
-                    ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig,
-                    NetworkProvider.ID_VPN) {
-                            @Override
-                            public void unwanted() {
-                                // We are user controlled, not driven by NetworkRequest.
-                            }
-                        };
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
+        mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
+                mNetworkCapabilities, lp,
+                ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig, mNetworkProvider) {
+            @Override
+            public void unwanted() {
+                // We are user controlled, not driven by NetworkRequest.
+            }
+        };
+        Binder.withCleanCallingIdentity(() -> {
+            try {
+                mNetworkAgent.register();
+            } catch (final Exception e) {
+                // If register() throws, don't keep an unregistered agent.
+                mNetworkAgent = null;
+                throw e;
+            }
+        });
         mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
                 ? Arrays.asList(mConfig.underlyingNetworks) : null);
         mNetworkInfo.setIsAvailable(true);
@@ -1301,19 +1313,12 @@
 
     private void agentDisconnect(NetworkAgent networkAgent) {
         if (networkAgent != null) {
-            NetworkInfo networkInfo = new NetworkInfo(mNetworkInfo);
-            networkInfo.setIsAvailable(false);
-            networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
-            networkAgent.sendNetworkInfo(networkInfo);
+            networkAgent.unregister();
         }
     }
 
     private void agentDisconnect() {
-        if (mNetworkInfo.isConnected()) {
-            mNetworkInfo.setIsAvailable(false);
-            updateState(DetailedState.DISCONNECTED, "agentDisconnect");
-            mNetworkAgent = null;
-        }
+        updateState(DetailedState.DISCONNECTED, "agentDisconnect");
     }
 
     /**
@@ -1402,6 +1407,8 @@
                     && updateLinkPropertiesInPlaceIfPossible(mNetworkAgent, oldConfig)) {
                 // Keep mNetworkAgent unchanged
             } else {
+                // Initialize the state for a new agent, while keeping the old one connected
+                // in case this new connection fails.
                 mNetworkAgent = null;
                 updateState(DetailedState.CONNECTING, "establish");
                 // Set up forwarding and DNS rules.
@@ -1585,12 +1592,13 @@
                     try {
                         addUserToRanges(existingRanges, userId, mConfig.allowedApplications,
                                 mConfig.disallowedApplications);
-                        // ConnectivityService will call {@link #updateCapabilities} and apply
-                        // those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to add restricted user to owner", e);
                     }
+                    if (mNetworkAgent != null) {
+                        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+                    }
                 }
                 setVpnForcedLocked(mLockdown);
             }
@@ -1613,12 +1621,13 @@
                         final List<UidRange> removedRanges =
                                 uidRangesForUser(userId, existingRanges);
                         existingRanges.removeAll(removedRanges);
-                        // ConnectivityService will call {@link #updateCapabilities} and
-                        // apply those for VPN network.
                         mNetworkCapabilities.setUids(existingRanges);
                     } catch (Exception e) {
                         Log.wtf(TAG, "Failed to remove restricted user to owner", e);
                     }
+                    if (mNetworkAgent != null) {
+                        mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+                    }
                 }
                 setVpnForcedLocked(mLockdown);
             }
@@ -1635,6 +1644,9 @@
 
         // Quit any active connections
         agentDisconnect();
+
+        // The provider has been registered in the constructor, which is called in onUserStart.
+        mConnectivityManager.unregisterNetworkProvider(mNetworkProvider);
     }
 
     /**
@@ -2411,7 +2423,6 @@
             // When restricted to test networks, select any network with TRANSPORT_TEST. Since the
             // creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
             // this is considered safe.
-            final ConnectivityManager cm = ConnectivityManager.from(mContext);
             final NetworkRequest req;
 
             if (mProfile.isRestrictedToTestNetworks()) {
@@ -2430,7 +2441,7 @@
                         .build();
             }
 
-            cm.requestNetwork(req, mNetworkCallback);
+            mConnectivityManager.requestNetwork(req, mNetworkCallback);
         }
 
         private boolean isActiveNetwork(@Nullable Network network) {
@@ -2717,8 +2728,7 @@
 
             resetIkeState();
 
-            final ConnectivityManager cm = ConnectivityManager.from(mContext);
-            cm.unregisterNetworkCallback(mNetworkCallback);
+            mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
 
             mExecutor.shutdown();
         }
@@ -2799,13 +2809,12 @@
             mProfile = profile;
 
             if (!TextUtils.isEmpty(mOuterInterface)) {
-                final ConnectivityManager cm = ConnectivityManager.from(mContext);
-                for (Network network : cm.getAllNetworks()) {
-                    final LinkProperties lp = cm.getLinkProperties(network);
+                for (Network network : mConnectivityManager.getAllNetworks()) {
+                    final LinkProperties lp = mConnectivityManager.getLinkProperties(network);
                     if (lp != null && lp.getAllInterfaceNames().contains(mOuterInterface)) {
-                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
-                        if (networkInfo != null) {
-                            mOuterConnection.set(networkInfo.getType());
+                        final NetworkInfo netInfo = mConnectivityManager.getNetworkInfo(network);
+                        if (netInfo != null) {
+                            mOuterConnection.set(netInfo.getType());
                             break;
                         }
                     }
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 9a1f1e5..d27cb16 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -58,12 +58,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IntPair;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -71,7 +69,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.HashMap;
@@ -1650,37 +1647,23 @@
 
             String tagName = parser.getName();
             if ("accounts".equals(tagName)) {
-                String listen = parser.getAttributeValue(null, XML_ATTR_LISTEN_FOR_TICKLES);
-                String versionString = parser.getAttributeValue(null, "version");
-                int version;
-                try {
-                    version = (versionString == null) ? 0 : Integer.parseInt(versionString);
-                } catch (NumberFormatException e) {
-                    version = 0;
-                }
+                boolean listen = parser.getAttributeBoolean(
+                        null, XML_ATTR_LISTEN_FOR_TICKLES, true);
+                int version = parser.getAttributeInt(null, "version", 0);
 
                 if (version < 3) {
                     mGrantSyncAdaptersAccountAccess = true;
                 }
 
-                String nextIdString = parser.getAttributeValue(null, XML_ATTR_NEXT_AUTHORITY_ID);
-                try {
-                    int id = (nextIdString == null) ? 0 : Integer.parseInt(nextIdString);
-                    mNextAuthorityId = Math.max(mNextAuthorityId, id);
-                } catch (NumberFormatException e) {
-                    // don't care
-                }
-                String offsetString = parser.getAttributeValue(null, XML_ATTR_SYNC_RANDOM_OFFSET);
-                try {
-                    mSyncRandomOffset = (offsetString == null) ? 0 : Integer.parseInt(offsetString);
-                } catch (NumberFormatException e) {
-                    mSyncRandomOffset = 0;
-                }
+                int nextId = parser.getAttributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, 0);
+                mNextAuthorityId = Math.max(mNextAuthorityId, nextId);
+
+                mSyncRandomOffset = parser.getAttributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, 0);
                 if (mSyncRandomOffset == 0) {
                     Random random = new Random(System.currentTimeMillis());
                     mSyncRandomOffset = random.nextInt(86400);
                 }
-                mMasterSyncAutomatically.put(0, listen == null || Boolean.parseBoolean(listen));
+                mMasterSyncAutomatically.put(0, listen);
                 eventType = parser.next();
                 AuthorityInfo authority = null;
                 PeriodicSync periodicSync = null;
@@ -1804,27 +1787,23 @@
         return writeNeeded;
     }
 
-    private void parseListenForTickles(XmlPullParser parser) {
-        String user = parser.getAttributeValue(null, XML_ATTR_USER);
+    private void parseListenForTickles(TypedXmlPullParser parser) {
         int userId = 0;
         try {
-            userId = Integer.parseInt(user);
-        } catch (NumberFormatException e) {
+            parser.getAttributeInt(null, XML_ATTR_USER);
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "error parsing the user for listen-for-tickles", e);
-        } catch (NullPointerException e) {
-            Slog.e(TAG, "the user in listen-for-tickles is null", e);
         }
-        String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
-        boolean listen = enabled == null || Boolean.parseBoolean(enabled);
+        boolean listen = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true);
         mMasterSyncAutomatically.put(userId, listen);
     }
 
-    private AuthorityInfo parseAuthority(XmlPullParser parser, int version,
-            AccountAuthorityValidator validator) {
+    private AuthorityInfo parseAuthority(TypedXmlPullParser parser, int version,
+            AccountAuthorityValidator validator) throws XmlPullParserException {
         AuthorityInfo authority = null;
         int id = -1;
         try {
-            id = Integer.parseInt(parser.getAttributeValue(null, "id"));
+            id = parser.getAttributeInt(null, "id");
         } catch (NumberFormatException e) {
             Slog.e(TAG, "error parsing the id of the authority", e);
         } catch (NullPointerException e) {
@@ -1832,14 +1811,13 @@
         }
         if (id >= 0) {
             String authorityName = parser.getAttributeValue(null, "authority");
-            String enabled = parser.getAttributeValue(null, XML_ATTR_ENABLED);
+            boolean enabled = parser.getAttributeBoolean(null, XML_ATTR_ENABLED, true);
             String syncable = parser.getAttributeValue(null, "syncable");
             String accountName = parser.getAttributeValue(null, "account");
             String accountType = parser.getAttributeValue(null, "type");
-            String user = parser.getAttributeValue(null, XML_ATTR_USER);
+            int userId = parser.getAttributeInt(null, XML_ATTR_USER, 0);
             String packageName = parser.getAttributeValue(null, "package");
             String className = parser.getAttributeValue(null, "class");
-            int userId = user == null ? 0 : Integer.parseInt(user);
             if (accountType == null && packageName == null) {
                 accountType = "com.google";
                 syncable = String.valueOf(AuthorityInfo.NOT_INITIALIZED);
@@ -1884,7 +1862,7 @@
                 }
             }
             if (authority != null) {
-                authority.enabled = enabled == null || Boolean.parseBoolean(enabled);
+                authority.enabled = enabled;
                 try {
                     authority.syncable = (syncable == null) ?
                             AuthorityInfo.NOT_INITIALIZED : Integer.parseInt(syncable);
@@ -1912,32 +1890,22 @@
     /**
      * Parse a periodic sync from accounts.xml. Sets the bundle to be empty.
      */
-    private PeriodicSync parsePeriodicSync(XmlPullParser parser, AuthorityInfo authorityInfo) {
+    private PeriodicSync parsePeriodicSync(TypedXmlPullParser parser, AuthorityInfo authorityInfo) {
         Bundle extras = new Bundle(); // Gets filled in later.
-        String periodValue = parser.getAttributeValue(null, "period");
-        String flexValue = parser.getAttributeValue(null, "flex");
-        final long period;
+        long period;
         long flextime;
         try {
-            period = Long.parseLong(periodValue);
-        } catch (NumberFormatException e) {
+            period = parser.getAttributeLong(null, "period");
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "error parsing the period of a periodic sync", e);
             return null;
-        } catch (NullPointerException e) {
-            Slog.e(TAG, "the period of a periodic sync is null", e);
-            return null;
         }
         try {
-            flextime = Long.parseLong(flexValue);
-        } catch (NumberFormatException e) {
+            flextime = parser.getAttributeLong(null, "flex");
+        } catch (XmlPullParserException e) {
             flextime = calculateDefaultFlexTime(period);
-            Slog.e(TAG, "Error formatting value parsed for periodic sync flex: " + flexValue
-                    + ", using default: "
-                    + flextime);
-        } catch (NullPointerException expected) {
-            flextime = calculateDefaultFlexTime(period);
-            Slog.d(TAG, "No flex time specified for this sync, using a default. period: "
-                    + period + " flex: " + flextime);
+            Slog.e(TAG, "Error formatting value parsed for periodic sync flex, using default: "
+                    + flextime, e);
         }
         PeriodicSync periodicSync;
         periodicSync =
@@ -1949,7 +1917,7 @@
         return periodicSync;
     }
 
-    private void parseExtra(XmlPullParser parser, Bundle extras) {
+    private void parseExtra(TypedXmlPullParser parser, Bundle extras) {
         String name = parser.getAttributeValue(null, "name");
         String type = parser.getAttributeValue(null, "type");
         String value1 = parser.getAttributeValue(null, "value1");
@@ -1994,9 +1962,9 @@
             out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             out.startTag(null, "accounts");
-            out.attribute(null, "version", Integer.toString(ACCOUNTS_VERSION));
-            out.attribute(null, XML_ATTR_NEXT_AUTHORITY_ID, Integer.toString(mNextAuthorityId));
-            out.attribute(null, XML_ATTR_SYNC_RANDOM_OFFSET, Integer.toString(mSyncRandomOffset));
+            out.attributeInt(null, "version", ACCOUNTS_VERSION);
+            out.attributeInt(null, XML_ATTR_NEXT_AUTHORITY_ID, mNextAuthorityId);
+            out.attributeInt(null, XML_ATTR_SYNC_RANDOM_OFFSET, mSyncRandomOffset);
 
             // Write the Sync Automatically flags for each user
             final int M = mMasterSyncAutomatically.size();
@@ -2004,8 +1972,8 @@
                 int userId = mMasterSyncAutomatically.keyAt(m);
                 Boolean listen = mMasterSyncAutomatically.valueAt(m);
                 out.startTag(null, XML_TAG_LISTEN_FOR_TICKLES);
-                out.attribute(null, XML_ATTR_USER, Integer.toString(userId));
-                out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(listen));
+                out.attributeInt(null, XML_ATTR_USER, userId);
+                out.attributeBoolean(null, XML_ATTR_ENABLED, listen);
                 out.endTag(null, XML_TAG_LISTEN_FOR_TICKLES);
             }
 
@@ -2014,13 +1982,13 @@
                 AuthorityInfo authority = mAuthorities.valueAt(i);
                 EndPoint info = authority.target;
                 out.startTag(null, "authority");
-                out.attribute(null, "id", Integer.toString(authority.ident));
-                out.attribute(null, XML_ATTR_USER, Integer.toString(info.userId));
-                out.attribute(null, XML_ATTR_ENABLED, Boolean.toString(authority.enabled));
+                out.attributeInt(null, "id", authority.ident);
+                out.attributeInt(null, XML_ATTR_USER, info.userId);
+                out.attributeBoolean(null, XML_ATTR_ENABLED, authority.enabled);
                 out.attribute(null, "account", info.account.name);
                 out.attribute(null, "type", info.account.type);
                 out.attribute(null, "authority", info.provider);
-                out.attribute(null, "syncable", Integer.toString(authority.syncable));
+                out.attributeInt(null, "syncable", authority.syncable);
                 out.endTag(null, "authority");
             }
             out.endTag(null, "accounts");
diff --git a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
index 928799b..0f1e666 100644
--- a/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
+++ b/services/core/java/com/android/server/display/AmbientBrightnessStatsTracker.java
@@ -27,17 +27,14 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.time.LocalDate;
 import java.time.format.DateTimeParseException;
 import java.util.ArrayDeque;
@@ -179,7 +176,7 @@
                             entry.getKey());
                     if (userSerialNumber != -1 && userDayStats.getLocalDate().isAfter(cutOffDate)) {
                         out.startTag(null, TAG_AMBIENT_BRIGHTNESS_DAY_STATS);
-                        out.attribute(null, ATTR_USER, Integer.toString(userSerialNumber));
+                        out.attributeInt(null, ATTR_USER, userSerialNumber);
                         out.attribute(null, ATTR_LOCAL_DATE,
                                 userDayStats.getLocalDate().toString());
                         StringBuilder bucketBoundariesValues = new StringBuilder();
@@ -229,7 +226,7 @@
                     }
                     tag = parser.getName();
                     if (TAG_AMBIENT_BRIGHTNESS_DAY_STATS.equals(tag)) {
-                        String userSerialNumber = parser.getAttributeValue(null, ATTR_USER);
+                        int userSerialNumber = parser.getAttributeInt(null, ATTR_USER);
                         LocalDate localDate = LocalDate.parse(
                                 parser.getAttributeValue(null, ATTR_LOCAL_DATE));
                         String[] bucketBoundaries = parser.getAttributeValue(null,
@@ -246,8 +243,7 @@
                             parsedBucketBoundaries[i] = Float.parseFloat(bucketBoundaries[i]);
                             parsedBucketStats[i] = Float.parseFloat(bucketStats[i]);
                         }
-                        int userId = mInjector.getUserId(mUserManager,
-                                Integer.parseInt(userSerialNumber));
+                        int userId = mInjector.getUserId(mUserManager, userSerialNumber);
                         if (userId != -1 && localDate.isAfter(cutOffDate)) {
                             Deque<AmbientBrightnessDayStats> userStats = getOrCreateUserStats(
                                     parsedStats, userId);
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 3ae99ef..2a0e219 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -555,22 +555,22 @@
             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
                 mEvents.append(toWrite[i]);
                 out.startTag(null, TAG_EVENT);
-                out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
-                out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
+                out.attributeFloat(null, ATTR_NITS, toWrite[i].brightness);
+                out.attributeLong(null, ATTR_TIMESTAMP, toWrite[i].timeStamp);
                 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
-                out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
-                out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
-                out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
-                out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
-                        toWrite[i].colorTemperature));
-                out.attribute(null, ATTR_LAST_NITS,
-                        Float.toString(toWrite[i].lastBrightness));
-                out.attribute(null, ATTR_DEFAULT_CONFIG,
-                        Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
-                out.attribute(null, ATTR_POWER_SAVE,
-                        Float.toString(toWrite[i].powerBrightnessFactor));
-                out.attribute(null, ATTR_USER_POINT,
-                        Boolean.toString(toWrite[i].isUserSetBrightness));
+                out.attributeInt(null, ATTR_USER, userSerialNo);
+                out.attributeFloat(null, ATTR_BATTERY_LEVEL, toWrite[i].batteryLevel);
+                out.attributeBoolean(null, ATTR_NIGHT_MODE, toWrite[i].nightMode);
+                out.attributeInt(null, ATTR_COLOR_TEMPERATURE,
+                        toWrite[i].colorTemperature);
+                out.attributeFloat(null, ATTR_LAST_NITS,
+                        toWrite[i].lastBrightness);
+                out.attributeBoolean(null, ATTR_DEFAULT_CONFIG,
+                        toWrite[i].isDefaultBrightnessConfig);
+                out.attributeFloat(null, ATTR_POWER_SAVE,
+                        toWrite[i].powerBrightnessFactor);
+                out.attributeBoolean(null, ATTR_USER_POINT,
+                        toWrite[i].isUserSetBrightness);
                 StringBuilder luxValues = new StringBuilder();
                 StringBuilder luxTimestamps = new StringBuilder();
                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
@@ -585,8 +585,8 @@
                 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
                 if (toWrite[i].colorValueBuckets != null
                         && toWrite[i].colorValueBuckets.length > 0) {
-                    out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
-                            Long.toString(toWrite[i].colorSampleDuration));
+                    out.attributeLong(null, ATTR_COLOR_SAMPLE_DURATION,
+                            toWrite[i].colorSampleDuration);
                     StringBuilder buckets = new StringBuilder();
                     for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
                         if (j > 0) {
@@ -633,22 +633,16 @@
                 if (TAG_EVENT.equals(tag)) {
                     BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
 
-                    String brightness = parser.getAttributeValue(null, ATTR_NITS);
-                    builder.setBrightness(Float.parseFloat(brightness));
-                    String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
-                    builder.setTimeStamp(Long.parseLong(timestamp));
+                    builder.setBrightness(parser.getAttributeFloat(null, ATTR_NITS));
+                    builder.setTimeStamp(parser.getAttributeLong(null, ATTR_TIMESTAMP));
                     builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
-                    String user = parser.getAttributeValue(null, ATTR_USER);
-                    builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
-                    String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
-                    builder.setBatteryLevel(Float.parseFloat(batteryLevel));
-                    String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
-                    builder.setNightMode(Boolean.parseBoolean(nightMode));
-                    String colorTemperature =
-                            parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
-                    builder.setColorTemperature(Integer.parseInt(colorTemperature));
-                    String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
-                    builder.setLastBrightness(Float.parseFloat(lastBrightness));
+                    builder.setUserId(mInjector.getUserId(mUserManager,
+                            parser.getAttributeInt(null, ATTR_USER)));
+                    builder.setBatteryLevel(parser.getAttributeFloat(null, ATTR_BATTERY_LEVEL));
+                    builder.setNightMode(parser.getAttributeBoolean(null, ATTR_NIGHT_MODE));
+                    builder.setColorTemperature(
+                            parser.getAttributeInt(null, ATTR_COLOR_TEMPERATURE));
+                    builder.setLastBrightness(parser.getAttributeFloat(null, ATTR_LAST_NITS));
 
                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
                     String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
@@ -667,20 +661,12 @@
                     builder.setLuxValues(luxValues);
                     builder.setLuxTimestamps(luxTimestamps);
 
-                    String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
-                    if (defaultConfig != null) {
-                        builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
-                    }
-                    String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
-                    if (powerSave != null) {
-                        builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
-                    } else {
-                        builder.setPowerBrightnessFactor(1.0f);
-                    }
-                    String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
-                    if (userPoint != null) {
-                        builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
-                    }
+                    builder.setIsDefaultBrightnessConfig(
+                            parser.getAttributeBoolean(null, ATTR_DEFAULT_CONFIG, false));
+                    builder.setPowerBrightnessFactor(
+                            parser.getAttributeFloat(null, ATTR_POWER_SAVE, 1.0f));
+                    builder.setUserBrightnessPoint(
+                            parser.getAttributeBoolean(null, ATTR_USER_POINT, false));
 
                     String colorSampleDurationString =
                             parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 1aced07..329081a 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -38,6 +38,7 @@
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -49,6 +50,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.display.utils.AmbientFilter;
 import com.android.server.display.utils.AmbientFilterFactory;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -68,9 +70,11 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
-    private static final int MSG_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
+    private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
     private static final int MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED = 3;
-    private static final int MSG_REFRESH_RATE_IN_ZONE_CHANGED = 4;
+    private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
+    private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
+    private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
 
     // Special ID used to indicate that given vote is to be applied globally, rather than to a
     // specific display.
@@ -83,6 +87,13 @@
     private final Context mContext;
 
     private final DisplayModeDirectorHandler mHandler;
+    private final Injector mInjector;
+
+    private final AppRequestObserver mAppRequestObserver;
+    private final SettingsObserver mSettingsObserver;
+    private final DisplayObserver mDisplayObserver;
+    private final DeviceConfigInterface mDeviceConfig;
+    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
 
     // A map from the display ID to the collection of votes and their priority. The latter takes
     // the form of another map from the priority to the vote itself so that each priority is
@@ -93,12 +104,8 @@
     // A map from the display ID to the default mode of that display.
     private SparseArray<Display.Mode> mDefaultModeByDisplay;
 
-    private final AppRequestObserver mAppRequestObserver;
-    private final SettingsObserver mSettingsObserver;
-    private final DisplayObserver mDisplayObserver;
     private BrightnessObserver mBrightnessObserver;
 
-    private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
     private DesiredDisplayModeSpecsListener mDesiredDisplayModeSpecsListener;
 
     private boolean mAlwaysRespectAppRequest;
@@ -127,8 +134,14 @@
     private int mModeSwitchingType = SWITCHING_TYPE_WITHIN_GROUPS;
 
     public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler) {
+        this(context, handler, new RealInjector());
+    }
+
+    public DisplayModeDirector(@NonNull Context context, @NonNull Handler handler,
+            @NonNull Injector injector) {
         mContext = context;
         mHandler = new DisplayModeDirectorHandler(handler.getLooper());
+        mInjector = injector;
         mVotesByDisplay = new SparseArray<>();
         mSupportedModesByDisplay = new SparseArray<>();
         mDefaultModeByDisplay = new SparseArray<>();
@@ -137,6 +150,7 @@
         mDisplayObserver = new DisplayObserver(context, handler);
         mBrightnessObserver = new BrightnessObserver(context, handler);
         mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+        mDeviceConfig = injector.getDeviceConfig();
         mAlwaysRespectAppRequest = false;
     }
 
@@ -455,6 +469,23 @@
     }
 
     /**
+     * Retrieve the Vote for the given display and priority. Intended only for testing purposes.
+     *
+     * @param displayId the display to query for
+     * @param priority the priority of the vote to return
+     * @return the vote corresponding to the given {@code displayId} and {@code priority},
+     *         or {@code null} if there isn't one
+     */
+    @VisibleForTesting
+    @Nullable
+    Vote getVote(int displayId, int priority) {
+        synchronized (mLock) {
+            SparseArray<Vote> votes = getVotesLocked(displayId);
+            return votes.get(priority);
+        }
+    }
+
+    /**
      * Print the object's state and debug information into the given stream.
      *
      * @param pw The stream to dump information to.
@@ -586,6 +617,17 @@
     }
 
     @VisibleForTesting
+    BrightnessObserver getBrightnessObserver() {
+        return mBrightnessObserver;
+    }
+
+    @VisibleForTesting
+    SettingsObserver getSettingsObserver() {
+        return mSettingsObserver;
+    }
+
+
+    @VisibleForTesting
     DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
             float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
         synchronized (mLock) {
@@ -613,16 +655,35 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_BRIGHTNESS_THRESHOLDS_CHANGED:
+                case MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED: {
+                    Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
+                    mBrightnessObserver.onDeviceConfigLowBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+                    break;
+                }
+
+                case MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInLowZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
+
+                case MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED: {
                     Pair<int[], int[]> thresholds = (Pair<int[], int[]>) msg.obj;
 
-                    if (thresholds != null) {
-                        mBrightnessObserver.onDeviceConfigThresholdsChanged(
-                                thresholds.first, thresholds.second);
-                    } else {
-                        mBrightnessObserver.onDeviceConfigThresholdsChanged(null, null);
-                    }
+                    mBrightnessObserver.onDeviceConfigHighBrightnessThresholdsChanged(
+                            thresholds.first, thresholds.second);
+
                     break;
+                }
+
+                case MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED: {
+                    int refreshRateInZone = msg.arg1;
+                    mBrightnessObserver.onDeviceConfigRefreshRateInHighZoneChanged(
+                            refreshRateInZone);
+                    break;
+                }
 
                 case MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED:
                     Float defaultPeakRefreshRate = (Float) msg.obj;
@@ -630,12 +691,6 @@
                             defaultPeakRefreshRate);
                     break;
 
-                case MSG_REFRESH_RATE_IN_ZONE_CHANGED:
-                    int refreshRateInZone = msg.arg1;
-                    mBrightnessObserver.onDeviceConfigRefreshRateInZoneChanged(
-                            refreshRateInZone);
-                    break;
-
                 case MSG_REFRESH_RATE_RANGE_CHANGED:
                     DesiredDisplayModeSpecsListener desiredDisplayModeSpecsListener =
                             (DesiredDisplayModeSpecsListener) msg.obj;
@@ -822,10 +877,11 @@
         // by all other considerations. It acts to set a default frame rate for a device.
         public static final int PRIORITY_DEFAULT_REFRESH_RATE = 0;
 
-        // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null.
+        // FLICKER votes for a single refresh rate like [60,60], [90,90] or null.
         // If the higher voters result is a range, it will fix the rate to a single choice.
-        // It's used to avoid rate switch in certain conditions.
-        public static final int PRIORITY_LOW_BRIGHTNESS = 1;
+        // It's used to avoid refresh rate switches in certain conditions which may result in the
+        // user seeing the display flickering when the switches occur.
+        public static final int PRIORITY_FLICKER = 1;
 
         // SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
         // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
@@ -898,8 +954,8 @@
             switch (priority) {
                 case PRIORITY_DEFAULT_REFRESH_RATE:
                     return "PRIORITY_DEFAULT_REFRESH_RATE";
-                case PRIORITY_LOW_BRIGHTNESS:
-                    return "PRIORITY_LOW_BRIGHTNESS";
+                case PRIORITY_FLICKER:
+                    return "PRIORITY_FLICKER";
                 case PRIORITY_USER_SETTING_MIN_REFRESH_RATE:
                     return "PRIORITY_USER_SETTING_MIN_REFRESH_RATE";
                 case PRIORITY_APP_REQUEST_REFRESH_RATE:
@@ -924,7 +980,8 @@
         }
     }
 
-    private final class SettingsObserver extends ContentObserver {
+    @VisibleForTesting
+    final class SettingsObserver extends ContentObserver {
         private final Uri mPeakRefreshRateSetting =
                 Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
         private final Uri mMinRefreshRateSetting =
@@ -949,8 +1006,7 @@
 
         public void observe() {
             final ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(mPeakRefreshRateSetting, false /*notifyDescendants*/, this,
-                    UserHandle.USER_SYSTEM);
+            mInjector.registerPeakRefreshRateObserver(cr, this);
             cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this,
                     UserHandle.USER_SYSTEM);
             cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this,
@@ -971,6 +1027,13 @@
             }
         }
 
+        public void setDefaultRefreshRate(float refreshRate) {
+            synchronized (mLock) {
+                mDefaultRefreshRate = refreshRate;
+                updateRefreshRateSettingLocked();
+            }
+        }
+
         public void onDeviceConfigDefaultPeakRefreshRateChanged(Float defaultPeakRefreshRate) {
             if (defaultPeakRefreshRate == null) {
                 defaultPeakRefreshRate = (float) mContext.getResources().getInteger(
@@ -1189,6 +1252,7 @@
         @Override
         public void onDisplayChanged(int displayId) {
             updateDisplayModes(displayId);
+            // TODO: Break the coupling between DisplayObserver and BrightnessObserver.
             mBrightnessObserver.onDisplayChanged(displayId);
         }
 
@@ -1227,15 +1291,16 @@
      */
     @VisibleForTesting
     public class BrightnessObserver extends ContentObserver {
-        // TODO: brightnessfloat: change this to the float setting
-        private final Uri mDisplayBrightnessSetting =
-                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
         private final static int LIGHT_SENSOR_RATE_MS = 250;
-        private int[] mDisplayBrightnessThresholds;
-        private int[] mAmbientBrightnessThresholds;
+        private int[] mLowDisplayBrightnessThresholds;
+        private int[] mLowAmbientBrightnessThresholds;
+        private int[] mHighDisplayBrightnessThresholds;
+        private int[] mHighAmbientBrightnessThresholds;
         // valid threshold if any item from the array >= 0
-        private boolean mShouldObserveDisplayChange;
-        private boolean mShouldObserveAmbientChange;
+        private boolean mShouldObserveDisplayLowChange;
+        private boolean mShouldObserveAmbientLowChange;
+        private boolean mShouldObserveDisplayHighChange;
+        private boolean mShouldObserveAmbientHighChange;
 
         private SensorManager mSensorManager;
         private Sensor mLightSensor;
@@ -1243,46 +1308,122 @@
         // Take it as low brightness before valid sensor data comes
         private float mAmbientLux = -1.0f;
         private AmbientFilter mAmbientFilter;
+        private int mBrightness = -1;
 
         private final Context mContext;
 
-        // Enable light sensor only when mShouldObserveAmbientChange is true, screen is on, peak
-        // refresh rate changeable and low power mode off. After initialization, these states will
+        // Enable light sensor only when mShouldObserveAmbientLowChange is true or
+        // mShouldObserveAmbientHighChange is true, screen is on, peak refresh rate
+        // changeable and low power mode off. After initialization, these states will
         // be updated from the same handler thread.
-        private boolean mScreenOn = false;
+        private boolean mDefaultDisplayOn = false;
         private boolean mRefreshRateChangeable = false;
         private boolean mLowPowerModeEnabled = false;
 
-        private int mRefreshRateInZone;
+        private int mRefreshRateInLowZone;
+        private int mRefreshRateInHighZone;
 
         BrightnessObserver(Context context, Handler handler) {
             super(handler);
             mContext = context;
-            mDisplayBrightnessThresholds = context.getResources().getIntArray(
+            mLowDisplayBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_brightnessThresholdsOfPeakRefreshRate);
-            mAmbientBrightnessThresholds = context.getResources().getIntArray(
+            mLowAmbientBrightnessThresholds = context.getResources().getIntArray(
                     R.array.config_ambientThresholdsOfPeakRefreshRate);
 
-            if (mDisplayBrightnessThresholds.length != mAmbientBrightnessThresholds.length) {
-                throw new RuntimeException("display brightness threshold array and ambient "
-                        + "brightness threshold array have different length");
+            if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display low brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mLowDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mLowAmbientBrightnessThresholds));
             }
+
+            mHighDisplayBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+            mHighAmbientBrightnessThresholds = context.getResources().getIntArray(
+                    R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            if (mHighDisplayBrightnessThresholds.length
+                    != mHighAmbientBrightnessThresholds.length) {
+                throw new RuntimeException("display high brightness threshold array and ambient "
+                        + "brightness threshold array have different length: "
+                        + "displayBrightnessThresholds="
+                        + Arrays.toString(mHighDisplayBrightnessThresholds)
+                        + ", ambientBrightnessThresholds="
+                        + Arrays.toString(mHighAmbientBrightnessThresholds));
+            }
+            mRefreshRateInHighZone = context.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+        }
+
+        /**
+         * @return the refresh to lock to when in a low brightness zone
+         */
+        @VisibleForTesting
+        int getRefreshRateInLowZone() {
+            return mRefreshRateInLowZone;
+        }
+
+        /**
+         * @return the display brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowDisplayBrightnessThresholds() {
+            return mLowDisplayBrightnessThresholds;
+        }
+
+        /**
+         * @return the ambient brightness thresholds for the low brightness zones
+         */
+        @VisibleForTesting
+        int[] getLowAmbientBrightnessThresholds() {
+            return mLowAmbientBrightnessThresholds;
+        }
+
+        public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
+            mSensorManager = sensorManager;
+            mLightSensor = lightSensor;
+
+            mSensorManager.registerListener(mLightSensorListener,
+                    mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
         }
 
         public void observe(SensorManager sensorManager) {
             mSensorManager = sensorManager;
+            final ContentResolver cr = mContext.getContentResolver();
+            mBrightness = Settings.System.getIntForUser(cr,
+                    Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
 
             // DeviceConfig is accessible after system ready.
-            int[] brightnessThresholds = mDeviceConfigDisplaySettings.getBrightnessThresholds();
-            int[] ambientThresholds = mDeviceConfigDisplaySettings.getAmbientThresholds();
+            int[] lowDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
 
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
+                    && lowDisplayBrightnessThresholds.length
+                    == lowAmbientBrightnessThresholds.length) {
+                mLowDisplayBrightnessThresholds = lowDisplayBrightnessThresholds;
+                mLowAmbientBrightnessThresholds = lowAmbientBrightnessThresholds;
             }
 
-            mRefreshRateInZone = mDeviceConfigDisplaySettings.getRefreshRateInZone();
+
+            int[] highDisplayBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds =
+                    mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+
+            if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
+                    && highDisplayBrightnessThresholds.length
+                    == highAmbientBrightnessThresholds.length) {
+                mHighDisplayBrightnessThresholds = highDisplayBrightnessThresholds;
+                mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
+            }
+
+            mRefreshRateInLowZone = mDeviceConfigDisplaySettings.getRefreshRateInLowZone();
+            mRefreshRateInHighZone = mDeviceConfigDisplaySettings.getRefreshRateInHighZone();
+
             restartObserver();
             mDeviceConfigDisplaySettings.startListening();
         }
@@ -1294,7 +1435,7 @@
                 updateSensorStatus();
                 if (!changeable) {
                     // Revoke previous vote from BrightnessObserver
-                    updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, null);
+                    updateVoteLocked(Vote.PRIORITY_FLICKER, null);
                 }
             }
         }
@@ -1306,25 +1447,48 @@
             }
         }
 
-        public void onDeviceConfigThresholdsChanged(int[] brightnessThresholds,
+        public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds,
                 int[] ambientThresholds) {
-            if (brightnessThresholds != null && ambientThresholds != null
-                    && brightnessThresholds.length == ambientThresholds.length) {
-                mDisplayBrightnessThresholds = brightnessThresholds;
-                mAmbientBrightnessThresholds = ambientThresholds;
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mLowDisplayBrightnessThresholds = displayThresholds;
+                mLowAmbientBrightnessThresholds = ambientThresholds;
             } else {
                 // Invalid or empty. Use device default.
-                mDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowDisplayBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_brightnessThresholdsOfPeakRefreshRate);
-                mAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                mLowAmbientBrightnessThresholds = mContext.getResources().getIntArray(
                         R.array.config_ambientThresholdsOfPeakRefreshRate);
             }
             restartObserver();
         }
 
-        public void onDeviceConfigRefreshRateInZoneChanged(int refreshRate) {
-            if (refreshRate != mRefreshRateInZone) {
-                mRefreshRateInZone = refreshRate;
+        public void onDeviceConfigRefreshRateInLowZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInLowZone) {
+                mRefreshRateInLowZone = refreshRate;
+                restartObserver();
+            }
+        }
+
+        public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds,
+                int[] ambientThresholds) {
+            if (displayThresholds != null && ambientThresholds != null
+                    && displayThresholds.length == ambientThresholds.length) {
+                mHighDisplayBrightnessThresholds = displayThresholds;
+                mHighAmbientBrightnessThresholds = ambientThresholds;
+            } else {
+                // Invalid or empty. Use device default.
+                mHighDisplayBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate);
+                mHighAmbientBrightnessThresholds = mContext.getResources().getIntArray(
+                        R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate);
+            }
+            restartObserver();
+        }
+
+        public void onDeviceConfigRefreshRateInHighZoneChanged(int refreshRate) {
+            if (refreshRate != mRefreshRateInHighZone) {
+                mRefreshRateInHighZone = refreshRate;
                 restartObserver();
             }
         }
@@ -1332,48 +1496,95 @@
         public void dumpLocked(PrintWriter pw) {
             pw.println("  BrightnessObserver");
             pw.println("    mAmbientLux: " + mAmbientLux);
-            pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
+            pw.println("    mBrightness: " + mBrightness);
+            pw.println("    mDefaultDisplayOn: " + mDefaultDisplayOn);
+            pw.println("    mLowPowerModeEnabled: " + mLowPowerModeEnabled);
+            pw.println("    mRefreshRateChangeable: " + mRefreshRateChangeable);
+            pw.println("    mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
+            pw.println("    mShouldObserveAmbientLowChange: " + mShouldObserveAmbientLowChange);
+            pw.println("    mRefreshRateInLowZone: " + mRefreshRateInLowZone);
 
-            for (int d: mDisplayBrightnessThresholds) {
-                pw.println("    mDisplayBrightnessThreshold: " + d);
+            for (int d : mLowDisplayBrightnessThresholds) {
+                pw.println("    mDisplayLowBrightnessThreshold: " + d);
             }
 
-            for (int d: mAmbientBrightnessThresholds) {
-                pw.println("    mAmbientBrightnessThreshold: " + d);
+            for (int d : mLowAmbientBrightnessThresholds) {
+                pw.println("    mAmbientLowBrightnessThreshold: " + d);
+            }
+
+            pw.println("    mShouldObserveDisplayHighChange: " + mShouldObserveDisplayHighChange);
+            pw.println("    mShouldObserveAmbientHighChange: " + mShouldObserveAmbientHighChange);
+            pw.println("    mRefreshRateInHighZone: " + mRefreshRateInHighZone);
+
+            for (int d : mHighDisplayBrightnessThresholds) {
+                pw.println("    mDisplayHighBrightnessThresholds: " + d);
+            }
+
+            for (int d : mHighAmbientBrightnessThresholds) {
+                pw.println("    mAmbientHighBrightnessThresholds: " + d);
             }
 
             mLightSensorListener.dumpLocked(pw);
+
+            if (mAmbientFilter != null) {
+                IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+                ipw.setIndent("    ");
+                mAmbientFilter.dump(ipw);
+            }
         }
 
         public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
-                onScreenOn(isDefaultDisplayOn());
+                updateDefaultDisplayState();
             }
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
             synchronized (mLock) {
-                onBrightnessChangedLocked();
+                final ContentResolver cr = mContext.getContentResolver();
+                int brightness = Settings.System.getIntForUser(cr,
+                        Settings.System.SCREEN_BRIGHTNESS, -1 /*default*/, cr.getUserId());
+                if (brightness != mBrightness) {
+                    mBrightness = brightness;
+                    onBrightnessChangedLocked();
+                }
             }
         }
 
         private void restartObserver() {
-            mShouldObserveDisplayChange = checkShouldObserve(mDisplayBrightnessThresholds);
-            mShouldObserveAmbientChange = checkShouldObserve(mAmbientBrightnessThresholds);
-
             final ContentResolver cr = mContext.getContentResolver();
-            if (mShouldObserveDisplayChange) {
-                // Content Service does not check if an listener has already been registered.
-                // To ensure only one listener is registered, force an unregistration first.
-                cr.unregisterContentObserver(this);
-                cr.registerContentObserver(mDisplayBrightnessSetting,
-                        false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM);
+
+            if (mRefreshRateInLowZone > 0) {
+                mShouldObserveDisplayLowChange = hasValidThreshold(
+                        mLowDisplayBrightnessThresholds);
+                mShouldObserveAmbientLowChange = hasValidThreshold(
+                        mLowAmbientBrightnessThresholds);
             } else {
-                cr.unregisterContentObserver(this);
+                mShouldObserveDisplayLowChange = false;
+                mShouldObserveAmbientLowChange = false;
             }
 
-            if (mShouldObserveAmbientChange) {
+            if (mRefreshRateInHighZone > 0) {
+                mShouldObserveDisplayHighChange = hasValidThreshold(
+                        mHighDisplayBrightnessThresholds);
+                mShouldObserveAmbientHighChange = hasValidThreshold(
+                        mHighAmbientBrightnessThresholds);
+            } else {
+                mShouldObserveDisplayHighChange = false;
+                mShouldObserveAmbientHighChange = false;
+            }
+
+            if (mShouldObserveDisplayLowChange || mShouldObserveDisplayHighChange) {
+                // Content Service does not check if an listener has already been registered.
+                // To ensure only one listener is registered, force an unregistration first.
+                mInjector.unregisterBrightnessObserver(cr, this);
+                mInjector.registerBrightnessObserver(cr, this);
+            } else {
+                mInjector.unregisterBrightnessObserver(cr, this);
+            }
+
+            if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
                 Resources resources = mContext.getResources();
                 String lightSensorType = resources.getString(
                         com.android.internal.R.string.config_displayLightSensorType);
@@ -1399,8 +1610,6 @@
 
                     mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
                     mLightSensor = lightSensor;
-
-                    onScreenOn(isDefaultDisplayOn());
                 }
             } else {
                 mAmbientFilter = null;
@@ -1419,11 +1628,7 @@
          * Checks to see if at least one value is positive, in which case it is necessary to listen
          * to value changes.
          */
-        private boolean checkShouldObserve(int[] a) {
-            if (mRefreshRateInZone <= 0) {
-                return false;
-            }
-
+        private boolean hasValidThreshold(int[] a) {
             for (int d: a) {
                 if (d >= 0) {
                     return true;
@@ -1433,13 +1638,13 @@
             return false;
         }
 
-        private boolean isInsideZone(int brightness, float lux) {
-            for (int i = 0; i < mDisplayBrightnessThresholds.length; i++) {
-                int disp = mDisplayBrightnessThresholds[i];
-                int ambi = mAmbientBrightnessThresholds[i];
+        private boolean isInsideLowZone(int brightness, float lux) {
+            for (int i = 0; i < mLowDisplayBrightnessThresholds.length; i++) {
+                int disp = mLowDisplayBrightnessThresholds[i];
+                int ambi = mLowAmbientBrightnessThresholds[i];
 
                 if (disp >= 0 && ambi >= 0) {
-                    if (brightness <= disp && mAmbientLux <= ambi) {
+                    if (brightness <= disp && lux <= ambi) {
                         return true;
                     }
                 } else if (disp >= 0) {
@@ -1447,7 +1652,7 @@
                         return true;
                     }
                 } else if (ambi >= 0) {
-                    if (mAmbientLux <= ambi) {
+                    if (lux <= ambi) {
                         return true;
                     }
                 }
@@ -1455,28 +1660,77 @@
 
             return false;
         }
-        // TODO: brightnessfloat: make it use float not int
-        private void onBrightnessChangedLocked() {
-            final ContentResolver cr = mContext.getContentResolver();
-            int brightness = Settings.System.getIntForUser(cr,
-                    Settings.System.SCREEN_BRIGHTNESS, -1, cr.getUserId());
 
+        private boolean isInsideHighZone(int brightness, float lux) {
+            for (int i = 0; i < mHighDisplayBrightnessThresholds.length; i++) {
+                int disp = mHighDisplayBrightnessThresholds[i];
+                int ambi = mHighAmbientBrightnessThresholds[i];
+
+                if (disp >= 0 && ambi >= 0) {
+                    if (brightness >= disp && lux >= ambi) {
+                        return true;
+                    }
+                } else if (disp >= 0) {
+                    if (brightness >= disp) {
+                        return true;
+                    }
+                } else if (ambi >= 0) {
+                    if (lux >= ambi) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+        private void onBrightnessChangedLocked() {
             Vote vote = null;
-            boolean insideZone = isInsideZone(brightness, mAmbientLux);
-            if (insideZone) {
-                vote = Vote.forRefreshRates(mRefreshRateInZone, mRefreshRateInZone);
+
+            if (mBrightness < 0) {
+                // Either the setting isn't available or we shouldn't be observing yet anyways.
+                // Either way, just bail out since there's nothing we can do here.
+                return;
+            }
+
+            boolean insideLowZone = hasValidLowZone() && isInsideLowZone(mBrightness, mAmbientLux);
+            if (insideLowZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInLowZone, mRefreshRateInLowZone);
+            }
+
+            boolean insideHighZone = hasValidHighZone()
+                    && isInsideHighZone(mBrightness, mAmbientLux);
+            if (insideHighZone) {
+                vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
             }
 
             if (DEBUG) {
-                Slog.d(TAG, "Display brightness " + brightness + ", ambient lux " +  mAmbientLux +
-                        ", Vote " + vote);
+                Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " +  mAmbientLux
+                        + ", Vote " + vote);
             }
-            updateVoteLocked(Vote.PRIORITY_LOW_BRIGHTNESS, vote);
+            updateVoteLocked(Vote.PRIORITY_FLICKER, vote);
         }
 
-        private void onScreenOn(boolean on) {
-            if (mScreenOn != on) {
-                mScreenOn = on;
+        private boolean hasValidLowZone() {
+            return mRefreshRateInLowZone > 0
+                    && (mShouldObserveDisplayLowChange || mShouldObserveAmbientLowChange);
+        }
+
+        private boolean hasValidHighZone() {
+            return mRefreshRateInHighZone > 0
+                    && (mShouldObserveDisplayHighChange || mShouldObserveAmbientHighChange);
+        }
+
+        private void updateDefaultDisplayState() {
+            Display display = mContext.getSystemService(DisplayManager.class)
+                    .getDisplay(Display.DEFAULT_DISPLAY);
+            boolean defaultDisplayOn = display != null && display.getState() != Display.STATE_OFF;
+            setDefaultDisplayState(defaultDisplayOn);
+        }
+
+        @VisibleForTesting
+        public void setDefaultDisplayState(boolean on) {
+            if (mDefaultDisplayOn != on) {
+                mDefaultDisplayOn = on;
                 updateSensorStatus();
             }
         }
@@ -1486,8 +1740,8 @@
                 return;
             }
 
-            if (mShouldObserveAmbientChange && mScreenOn && !mLowPowerModeEnabled
-                    && mRefreshRateChangeable) {
+            if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
+                     && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
                 mSensorManager.registerListener(mLightSensorListener,
                         mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
             } else {
@@ -1496,11 +1750,8 @@
             }
         }
 
-        private boolean isDefaultDisplayOn() {
-            final Display display = mContext.getSystemService(DisplayManager.class)
-                    .getDisplay(Display.DEFAULT_DISPLAY);
-            return display.getState() != Display.STATE_OFF
-                    && mContext.getSystemService(PowerManager.class).isInteractive();
+        private boolean isDeviceActive() {
+            return mDefaultDisplayOn && mInjector.isDeviceInteractive(mContext);
         }
 
         private final class LightSensorEventListener implements SensorEventListener {
@@ -1518,23 +1769,33 @@
                     Slog.d(TAG, "On sensor changed: " + mLastSensorData);
                 }
 
-                boolean zoneChanged = isDifferentZone(mLastSensorData, mAmbientLux);
-                if (zoneChanged && mLastSensorData < mAmbientLux) {
-                    // Easier to see flicker at lower brightness environment. Forget the history to
-                    // get immediate response.
-                    mAmbientFilter.clear();
+                boolean lowZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mLowAmbientBrightnessThresholds);
+                boolean highZoneChanged = isDifferentZone(mLastSensorData, mAmbientLux,
+                        mHighAmbientBrightnessThresholds);
+                if ((lowZoneChanged && mLastSensorData < mAmbientLux)
+                        || (highZoneChanged && mLastSensorData > mAmbientLux)) {
+                    // Easier to see flicker at lower brightness environment or high brightness
+                    // environment. Forget the history to get immediate response.
+                    if (mAmbientFilter != null) {
+                        mAmbientFilter.clear();
+                    }
                 }
 
                 long now = SystemClock.uptimeMillis();
-                mAmbientFilter.addValue(now, mLastSensorData);
+                if (mAmbientFilter != null) {
+                    mAmbientFilter.addValue(now, mLastSensorData);
+                }
 
                 mHandler.removeCallbacks(mInjectSensorEventRunnable);
                 processSensorData(now);
 
-                if (zoneChanged && mLastSensorData > mAmbientLux) {
+                if ((lowZoneChanged && mLastSensorData > mAmbientLux)
+                        || (highZoneChanged && mLastSensorData < mAmbientLux)) {
                     // Sensor may not report new event if there is no brightness change.
                     // Need to keep querying the temporal filter for the latest estimation,
-                    // until enter in higher lux zone or is interrupted by a new sensor event.
+                    // until sensor readout and filter estimation are in the same zone or
+                    // is interrupted by a new sensor event.
                     mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                 }
             }
@@ -1549,17 +1810,19 @@
             }
 
             private void processSensorData(long now) {
-                mAmbientLux = mAmbientFilter.getEstimate(now);
+                if (mAmbientFilter != null) {
+                    mAmbientLux = mAmbientFilter.getEstimate(now);
+                } else {
+                    mAmbientLux = mLastSensorData;
+                }
 
                 synchronized (mLock) {
                     onBrightnessChangedLocked();
                 }
             }
 
-            private boolean isDifferentZone(float lux1, float lux2) {
-                for (int z = 0; z < mAmbientBrightnessThresholds.length; z++) {
-                    final float boundary = mAmbientBrightnessThresholds[z];
-
+            private boolean isDifferentZone(float lux1, float lux2, int[] luxThresholds) {
+                for (final float boundary : luxThresholds) {
                     // Test each boundary. See if the current value and the new value are at
                     // different sides.
                     if ((lux1 <= boundary && lux2 > boundary)
@@ -1579,7 +1842,10 @@
                     processSensorData(now);
 
                     // Inject next event if there is a possible zone change.
-                    if (isDifferentZone(mLastSensorData, mAmbientLux)) {
+                    if (isDifferentZone(mLastSensorData, mAmbientLux,
+                            mLowAmbientBrightnessThresholds)
+                            || isDifferentZone(mLastSensorData, mAmbientLux,
+                            mHighAmbientBrightnessThresholds)) {
                         mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS);
                     }
                 }
@@ -1592,33 +1858,75 @@
         }
 
         public void startListening() {
-            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+            mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     BackgroundThread.getExecutor(), this);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getBrightnessThresholds() {
+        public int[] getLowDisplayBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_DISPLAY_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
         }
 
         /*
          * Return null if no such property or wrong format (not comma separated integers).
          */
-        public int[] getAmbientThresholds() {
+        public int[] getLowAmbientBrightnessThresholds() {
             return getIntArrayProperty(
                     DisplayManager.DeviceConfig.
-                            KEY_PEAK_REFRESH_RATE_AMBIENT_BRIGHTNESS_THRESHOLDS);
+                            KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInLowZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_defaultRefreshRateInZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighDisplayBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+        }
+
+        /*
+         * Return null if no such property or wrong format (not comma separated integers).
+         */
+        public int[] getHighAmbientBrightnessThresholds() {
+            return getIntArrayProperty(
+                    DisplayManager.DeviceConfig
+                            .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+        }
+
+        public int getRefreshRateInHighZone() {
+            int defaultRefreshRateInZone = mContext.getResources().getInteger(
+                    R.integer.config_fixedRefreshRateInHighZone);
+
+            int refreshRate = mDeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    defaultRefreshRateInZone);
+
+            return refreshRate;
         }
 
         /*
          * Return null if no such property
          */
         public Float getDefaultPeakRefreshRate() {
-            float defaultPeakRefreshRate = DeviceConfig.getFloat(
+            float defaultPeakRefreshRate = mDeviceConfig.getFloat(
                     DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
                     DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
 
@@ -1628,36 +1936,35 @@
             return defaultPeakRefreshRate;
         }
 
-        public int getRefreshRateInZone() {
-            int defaultRefreshRateInZone = mContext.getResources().getInteger(
-                    R.integer.config_defaultRefreshRateInZone);
-
-            int refreshRate = DeviceConfig.getInt(
-                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
-                    DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_ZONE,
-                    defaultRefreshRateInZone);
-
-            return refreshRate;
-        }
-
         @Override
         public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
-            int[] brightnessThresholds = getBrightnessThresholds();
-            int[] ambientThresholds = getAmbientThresholds();
             Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
-            int refreshRateInZone = getRefreshRateInZone();
-
-            mHandler.obtainMessage(MSG_BRIGHTNESS_THRESHOLDS_CHANGED,
-                    new Pair<int[], int[]>(brightnessThresholds, ambientThresholds))
-                    .sendToTarget();
             mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
                     defaultPeakRefreshRate).sendToTarget();
-            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_ZONE_CHANGED, refreshRateInZone,
-                    0).sendToTarget();
+
+            int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
+            int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
+            int refreshRateInLowZone = getRefreshRateInLowZone();
+
+            mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone, 0)
+                    .sendToTarget();
+
+            int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
+            int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
+            int refreshRateInHighZone = getRefreshRateInHighZone();
+
+            mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
+                    new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
+                    .sendToTarget();
+            mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
+                    .sendToTarget();
         }
 
         private int[] getIntArrayProperty(String prop) {
-            String strArray = DeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+            String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
                     null);
 
             if (strArray != null) {
@@ -1684,4 +1991,59 @@
         }
     }
 
+    interface Injector {
+        // TODO: brightnessfloat: change this to the float setting
+        Uri DISPLAY_BRIGHTNESS_URI = Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+        Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE);
+
+        @NonNull
+        DeviceConfigInterface getDeviceConfig();
+
+        void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer);
+
+        boolean isDeviceInteractive(@NonNull Context context);
+    }
+
+    @VisibleForTesting
+    static class RealInjector implements Injector {
+
+        @Override
+        @NonNull
+        public DeviceConfigInterface getDeviceConfig() {
+            return DeviceConfigInterface.REAL;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(DISPLAY_BRIGHTNESS_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.unregisterContentObserver(observer);
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
+                    observer, UserHandle.USER_SYSTEM);
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context ctx) {
+            return ctx.getSystemService(PowerManager.class).isInteractive();
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index 494dd39..b0820e8 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -25,6 +25,7 @@
 import android.util.SparseArray;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.Display;
@@ -322,7 +323,7 @@
             return;
         }
 
-        XmlPullParser parser;
+        TypedXmlPullParser parser;
         try {
             parser = Xml.resolvePullParser(is);
             loadFromXml(parser);
@@ -355,7 +356,7 @@
         }
     }
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, TAG_DISPLAY_MANAGER_STATE);
         final int outerDepth = parser.getDepth();
@@ -375,7 +376,7 @@
         }
     }
 
-    private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser)
+    private void loadRememberedWifiDisplaysFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -399,7 +400,7 @@
         }
     }
 
-    private void loadDisplaysFromXml(XmlPullParser parser)
+    private void loadDisplaysFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -420,7 +421,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, TAG_DISPLAY_MANAGER_STATE);
@@ -491,7 +492,7 @@
             return mColorMode;
         }
 
-        public void loadFromXml(XmlPullParser parser)
+        public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
 
@@ -503,7 +504,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_COLOR_MODE);
             serializer.text(Integer.toString(mColorMode));
             serializer.endTag(null, TAG_COLOR_MODE);
@@ -531,7 +532,8 @@
             return false;
         }
 
-        public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+        public void loadFromXml(TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 switch (parser.getName()) {
@@ -545,7 +547,7 @@
             }
         }
 
-        private static int loadIntValue(XmlPullParser parser)
+        private static int loadIntValue(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
             try {
                 String value = parser.nextText();
@@ -555,7 +557,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             if (mWidth > 0 && mHeight > 0) {
                 serializer.startTag(null, TAG_STABLE_DISPLAY_WIDTH);
                 serializer.text(Integer.toString(mWidth));
@@ -612,14 +614,14 @@
             return mConfigurations.get(userSerial);
         }
 
-        public void loadFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+        public void loadFromXml(TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                 if (TAG_BRIGHTNESS_CONFIGURATION.equals(parser.getName())) {
                     int userSerial;
                     try {
-                        userSerial = Integer.parseInt(
-                                parser.getAttributeValue(null, ATTR_USER_SERIAL));
+                        userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL);
                     } catch (NumberFormatException nfe) {
                         userSerial = -1;
                         Slog.e(TAG, "Failed to read in brightness configuration", nfe);
@@ -655,20 +657,20 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             for (int i = 0; i < mConfigurations.size(); i++) {
                 final int userSerial = mConfigurations.keyAt(i);
                 final BrightnessConfiguration config = mConfigurations.valueAt(i);
 
                 serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATION);
-                serializer.attribute(null, ATTR_USER_SERIAL, Integer.toString(userSerial));
+                serializer.attributeInt(null, ATTR_USER_SERIAL, userSerial);
                 String packageName = mPackageNames.get(userSerial);
                 if (packageName != null) {
                     serializer.attribute(null, ATTR_PACKAGE_NAME, packageName);
                 }
                 long timestamp = mTimeStamps.get(userSerial, -1);
                 if (timestamp != -1) {
-                    serializer.attribute(null, ATTR_TIME_STAMP, Long.toString(timestamp));
+                    serializer.attributeLong(null, ATTR_TIME_STAMP, timestamp);
                 }
                 config.saveToXml(serializer);
                 serializer.endTag(null, TAG_BRIGHTNESS_CONFIGURATION);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 8d42a90..2374ece 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -92,7 +92,7 @@
 
     @NonNull private final Context mContext;
     @NonNull private final StorageAdapter mStorageAdapter;
-    @Nullable private final CecSettings mSystemConfig;
+    @Nullable private final CecSettings mProductConfig;
     @Nullable private final CecSettings mVendorOverride;
 
     private final ArrayMap<Setting, Set<SettingChangeListener>>
@@ -198,14 +198,14 @@
     @VisibleForTesting
     HdmiCecConfig(@NonNull Context context,
                   @NonNull StorageAdapter storageAdapter,
-                  @Nullable CecSettings systemConfig,
+                  @Nullable CecSettings productConfig,
                   @Nullable CecSettings vendorOverride) {
         mContext = context;
         mStorageAdapter = storageAdapter;
-        mSystemConfig = systemConfig;
+        mProductConfig = productConfig;
         mVendorOverride = vendorOverride;
-        if (mSystemConfig == null) {
-            Slog.i(TAG, "CEC system configuration XML missing.");
+        if (mProductConfig == null) {
+            Slog.i(TAG, "CEC master configuration XML missing.");
         }
         if (mVendorOverride == null) {
             Slog.i(TAG, "CEC OEM configuration override XML missing.");
@@ -214,7 +214,7 @@
 
     HdmiCecConfig(@NonNull Context context) {
         this(context, new StorageAdapter(context),
-             readSettingsFromFile(Environment.buildPath(Environment.getRootDirectory(),
+             readSettingsFromFile(Environment.buildPath(Environment.getProductDirectory(),
                                                         ETC_DIR, CONFIG_FILE)),
              readSettingsFromFile(Environment.buildPath(Environment.getVendorDirectory(),
                                                         ETC_DIR, CONFIG_FILE)));
@@ -241,14 +241,14 @@
     @VisibleForTesting
     static HdmiCecConfig createFromStrings(@NonNull Context context,
                                            @NonNull StorageAdapter storageAdapter,
-                                           @Nullable String systemConfigXml,
+                                           @Nullable String productConfigXml,
                                            @Nullable String vendorOverrideXml) {
-        CecSettings systemConfig = null;
+        CecSettings productConfig = null;
         CecSettings vendorOverride = null;
         try {
-            if (systemConfigXml != null) {
-                systemConfig = XmlParser.read(
-                        new ByteArrayInputStream(systemConfigXml.getBytes()));
+            if (productConfigXml != null) {
+                productConfig = XmlParser.read(
+                        new ByteArrayInputStream(productConfigXml.getBytes()));
             }
             if (vendorOverrideXml != null) {
                 vendorOverride = XmlParser.read(
@@ -257,12 +257,12 @@
         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
             Slog.e(TAG, "Encountered an error while reading/parsing CEC config strings", e);
         }
-        return new HdmiCecConfig(context, storageAdapter, systemConfig, vendorOverride);
+        return new HdmiCecConfig(context, storageAdapter, productConfig, vendorOverride);
     }
 
     @Nullable
     private Setting getSetting(@NonNull String name) {
-        if (mSystemConfig == null) {
+        if (mProductConfig == null) {
             return null;
         }
         if (mVendorOverride != null) {
@@ -273,8 +273,8 @@
                 }
             }
         }
-        // If not found, try the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
+        // If not found, try the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
             if (setting.getName().equals(name)) {
                 return setting;
             }
@@ -456,11 +456,11 @@
      * Returns a list of all settings based on the XML metadata.
      */
     public @CecSettingName List<String> getAllSettings() {
-        if (mSystemConfig == null) {
+        if (mProductConfig == null) {
             return new ArrayList<String>();
         }
         List<String> allSettings = new ArrayList<String>();
-        for (Setting setting : mSystemConfig.getSetting()) {
+        for (Setting setting : mProductConfig.getSetting()) {
             allSettings.add(setting.getName());
         }
         return allSettings;
@@ -470,12 +470,12 @@
      * Returns a list of user-modifiable settings based on the XML metadata.
      */
     public @CecSettingName List<String> getUserSettings() {
-        if (mSystemConfig == null) {
+        if (mProductConfig == null) {
             return new ArrayList<String>();
         }
         Set<String> userSettings = new HashSet<String>();
-        // First read from the system config.
-        for (Setting setting : mSystemConfig.getSetting()) {
+        // First read from the product config.
+        for (Setting setting : mProductConfig.getSetting()) {
             if (setting.getUserConfigurable()) {
                 userSettings.add(setting.getName());
             }
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index e47b544..52a804a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -25,6 +25,7 @@
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.util.HexDump;
@@ -571,14 +572,13 @@
         // return a list of devices config
         public static List<DeviceConfig> parse(InputStream in)
                 throws XmlPullParserException, IOException {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parser.nextTag();
             return readDevices(parser);
         }
 
-        private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+        private static void skip(TypedXmlPullParser parser)
+                throws XmlPullParserException, IOException {
             if (parser.getEventType() != XmlPullParser.START_TAG) {
                 throw new IllegalStateException();
             }
@@ -595,7 +595,7 @@
             }
         }
 
-        private static List<DeviceConfig> readDevices(XmlPullParser parser)
+        private static List<DeviceConfig> readDevices(TypedXmlPullParser parser)
                 throws XmlPullParserException, IOException {
             List<DeviceConfig> devices = new ArrayList<>();
 
@@ -624,7 +624,7 @@
 
         // Processes device tags in the config.
         @Nullable
-        private static DeviceConfig readDeviceConfig(XmlPullParser parser, String deviceType)
+        private static DeviceConfig readDeviceConfig(TypedXmlPullParser parser, String deviceType)
                 throws XmlPullParserException, IOException {
             List<CodecSad> codecSads = new ArrayList<>();
             int format;
diff --git a/services/core/java/com/android/server/input/ConfigurationProcessor.java b/services/core/java/com/android/server/input/ConfigurationProcessor.java
index 3888b1b..0563806 100644
--- a/services/core/java/com/android/server/input/ConfigurationProcessor.java
+++ b/services/core/java/com/android/server/input/ConfigurationProcessor.java
@@ -18,15 +18,13 @@
 
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-
 import java.io.InputStream;
-import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -38,9 +36,8 @@
 
     static List<String> processExcludedDeviceNames(InputStream xml) throws Exception {
         List<String> names = new ArrayList<>();
-        try (InputStreamReader confReader = new InputStreamReader(xml)) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(confReader);
+        {
+            TypedXmlPullParser parser = Xml.resolvePullParser(xml);
             XmlUtils.beginDocument(parser, "devices");
             while (true) {
                 XmlUtils.nextElement(parser);
@@ -90,9 +87,8 @@
     static Map<String, Integer> processInputPortAssociations(InputStream xml)
             throws Exception {
         Map<String, Integer> associations = new HashMap<String, Integer>();
-        try (InputStreamReader confReader = new InputStreamReader(xml)) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(confReader);
+        {
+            TypedXmlPullParser parser = Xml.resolvePullParser(xml);
             XmlUtils.beginDocument(parser, "ports");
 
             while (true) {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index f61662d..a735a8f 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -253,7 +253,7 @@
         }
     }
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, "input-manager-state");
         final int outerDepth = parser.getDepth();
@@ -264,7 +264,7 @@
         }
     }
 
-    private void loadInputDevicesFromXml(XmlPullParser parser)
+    private void loadInputDevicesFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -285,7 +285,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, "input-manager-state");
@@ -422,7 +422,7 @@
             return changed;
         }
 
-        public void loadFromXml(XmlPullParser parser)
+        public void loadFromXml(TypedXmlPullParser parser)
                 throws IOException, XmlPullParserException {
             final int outerDepth = parser.getDepth();
             while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -505,7 +505,7 @@
             }
         }
 
-        public void saveToXml(XmlSerializer serializer) throws IOException {
+        public void saveToXml(TypedXmlSerializer serializer) throws IOException {
             for (String layout : mKeyboardLayouts) {
                 serializer.startTag(null, "keyboard-layout");
                 serializer.attribute(null, "descriptor", layout);
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
index a077b04..24b8e34 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeUtils.java
@@ -31,17 +31,13 @@
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
-import com.android.internal.util.FastXmlSerializer;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -158,20 +154,17 @@
                     final InputMethodSubtype subtype = subtypesList.get(i);
                     out.startTag(null, NODE_SUBTYPE);
                     if (subtype.hasSubtypeId()) {
-                        out.attribute(null, ATTR_IME_SUBTYPE_ID,
-                                String.valueOf(subtype.getSubtypeId()));
+                        out.attributeInt(null, ATTR_IME_SUBTYPE_ID, subtype.getSubtypeId());
                     }
-                    out.attribute(null, ATTR_ICON, String.valueOf(subtype.getIconResId()));
-                    out.attribute(null, ATTR_LABEL, String.valueOf(subtype.getNameResId()));
+                    out.attributeInt(null, ATTR_ICON, subtype.getIconResId());
+                    out.attributeInt(null, ATTR_LABEL, subtype.getNameResId());
                     out.attribute(null, ATTR_IME_SUBTYPE_LOCALE, subtype.getLocale());
                     out.attribute(null, ATTR_IME_SUBTYPE_LANGUAGE_TAG,
                             subtype.getLanguageTag());
                     out.attribute(null, ATTR_IME_SUBTYPE_MODE, subtype.getMode());
                     out.attribute(null, ATTR_IME_SUBTYPE_EXTRA_VALUE, subtype.getExtraValue());
-                    out.attribute(null, ATTR_IS_AUXILIARY,
-                            String.valueOf(subtype.isAuxiliary() ? 1 : 0));
-                    out.attribute(null, ATTR_IS_ASCII_CAPABLE,
-                            String.valueOf(subtype.isAsciiCapable() ? 1 : 0));
+                    out.attributeInt(null, ATTR_IS_AUXILIARY, subtype.isAuxiliary() ? 1 : 0);
+                    out.attributeInt(null, ATTR_IS_ASCII_CAPABLE, subtype.isAsciiCapable() ? 1 : 0);
                     out.endTag(null, NODE_SUBTYPE);
                 }
                 out.endTag(null, NODE_IMI);
@@ -243,10 +236,8 @@
                         Slog.w(TAG, "IME uninstalled or not valid.: " + currentImiId);
                         continue;
                     }
-                    final int icon = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_ICON));
-                    final int label = Integer.parseInt(
-                            parser.getAttributeValue(null, ATTR_LABEL));
+                    final int icon = parser.getAttributeInt(null, ATTR_ICON);
+                    final int label = parser.getAttributeInt(null, ATTR_LABEL);
                     final String imeSubtypeLocale =
                             parser.getAttributeValue(null, ATTR_IME_SUBTYPE_LOCALE);
                     final String languageTag =
@@ -269,10 +260,10 @@
                             .setSubtypeExtraValue(imeSubtypeExtraValue)
                             .setIsAuxiliary(isAuxiliary)
                             .setIsAsciiCapable(isAsciiCapable);
-                    final String subtypeIdString =
-                            parser.getAttributeValue(null, ATTR_IME_SUBTYPE_ID);
-                    if (subtypeIdString != null) {
-                        builder.setSubtypeId(Integer.parseInt(subtypeIdString));
+                    final int subtypeId = parser.getAttributeInt(null, ATTR_IME_SUBTYPE_ID,
+                            InputMethodSubtype.SUBTYPE_ID_NONE);
+                    if (subtypeId != InputMethodSubtype.SUBTYPE_ID_NONE) {
+                        builder.setSubtypeId(subtypeId);
                     }
                     tempSubtypesArray.add(builder.build());
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 0ceaf77..a257cde 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -158,6 +158,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.inputmethod.CallbackUtils;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 import com.android.internal.inputmethod.IInputContentUriToken;
 import com.android.internal.inputmethod.IInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodDebug;
@@ -208,6 +210,7 @@
 import java.util.WeakHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
 
 /**
  * This class provides a system service that manages input methods.
@@ -2284,6 +2287,8 @@
                 }
 
                 if (mCurClient == cs) {
+                    hideCurrentInputLocked(
+                            mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_REMOVE_CLIENT);
                     if (mBoundToMethod) {
                         mBoundToMethod = false;
                         if (mCurMethod != null) {
@@ -3345,63 +3350,68 @@
 
     @NonNull
     @Override
-    public InputBindResult startInputOrWindowGainedFocus(
+    public void startInputOrWindowGainedFocus(
             @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
             @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
             int windowFlags, @Nullable EditorInfo attribute, IInputContext inputContext,
-            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion) {
-        if (windowToken == null) {
-            Slog.e(TAG, "windowToken cannot be null.");
-            return InputBindResult.NULL;
-        }
-        try {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
-                    "IMMS.startInputOrWindowGainedFocus");
-            ImeTracing.getInstance().triggerManagerServiceDump(
-                    "InputMethodManagerService#startInputOrWindowGainedFocus");
-            final int callingUserId = UserHandle.getCallingUserId();
-            final int userId;
-            if (attribute != null && attribute.targetInputMethodUser != null
-                    && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
-                mContext.enforceCallingPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        "Using EditorInfo.targetInputMethodUser requires"
-                                + " INTERACT_ACROSS_USERS_FULL.");
-                userId = attribute.targetInputMethodUser.getIdentifier();
-                if (!mUserManagerInternal.isUserRunning(userId)) {
-                    // There is a chance that we hit here because of race condition.  Let's just
-                    // return an error code instead of crashing the caller process, which at least
-                    // has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an important
-                    // process.
-                    Slog.e(TAG, "User #" + userId + " is not running.");
-                    return InputBindResult.INVALID_USER;
-                }
-            } else {
-                userId = callingUserId;
-            }
-            final InputBindResult result;
-            synchronized (mMethodMap) {
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    result = startInputOrWindowGainedFocusInternalLocked(startInputReason, client,
-                            windowToken, startInputFlags, softInputMode, windowFlags, attribute,
-                            inputContext, missingMethods, unverifiedTargetSdkVersion, userId);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-            if (result == null) {
-                // This must never happen, but just in case.
-                Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
-                        + InputMethodDebug.startInputReasonToString(startInputReason)
-                        + " windowFlags=#" + Integer.toHexString(windowFlags)
-                        + " editorInfo=" + attribute);
+            @MissingMethodFlags int missingMethods, int unverifiedTargetSdkVersion,
+            IInputBindResultResultCallback resultCallback) {
+        CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () -> {
+            if (windowToken == null) {
+                Slog.e(TAG, "windowToken cannot be null.");
                 return InputBindResult.NULL;
             }
+            try {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
+                        "IMMS.startInputOrWindowGainedFocus");
+                ImeTracing.getInstance().triggerManagerServiceDump(
+                        "InputMethodManagerService#startInputOrWindowGainedFocus");
+                final int callingUserId = UserHandle.getCallingUserId();
+                final int userId;
+                if (attribute != null && attribute.targetInputMethodUser != null
+                        && attribute.targetInputMethodUser.getIdentifier() != callingUserId) {
+                    mContext.enforceCallingPermission(
+                            Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                            "Using EditorInfo.targetInputMethodUser requires"
+                                    + " INTERACT_ACROSS_USERS_FULL.");
+                    userId = attribute.targetInputMethodUser.getIdentifier();
+                    if (!mUserManagerInternal.isUserRunning(userId)) {
+                        // There is a chance that we hit here because of race condition. Let's just
+                        // return an error code instead of crashing the caller process, which at
+                        // least has INTERACT_ACROSS_USERS_FULL permission thus is likely to be an
+                        // important process.
+                        Slog.e(TAG, "User #" + userId + " is not running.");
+                        return InputBindResult.INVALID_USER;
+                    }
+                } else {
+                    userId = callingUserId;
+                }
+                final InputBindResult result;
+                synchronized (mMethodMap) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        result = startInputOrWindowGainedFocusInternalLocked(startInputReason,
+                                client, windowToken, startInputFlags, softInputMode, windowFlags,
+                                attribute, inputContext, missingMethods, unverifiedTargetSdkVersion,
+                                userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                }
+                if (result == null) {
+                    // This must never happen, but just in case.
+                    Slog.wtf(TAG, "InputBindResult is @NonNull. startInputReason="
+                            + InputMethodDebug.startInputReasonToString(startInputReason)
+                            + " windowFlags=#" + Integer.toHexString(windowFlags)
+                            + " editorInfo=" + attribute);
+                    return InputBindResult.NULL;
+                }
 
-            return result;
-        } finally {
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
+                return result;
+            } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            }
+        });
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 62d817c..6bdae63 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -72,6 +72,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.inputmethod.CallbackUtils;
+import com.android.internal.inputmethod.IInputBindResultResultCallback;
 import com.android.internal.inputmethod.IMultiClientInputMethod;
 import com.android.internal.inputmethod.IMultiClientInputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.IMultiClientInputMethodSession;
@@ -104,6 +106,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.WeakHashMap;
+import java.util.function.Supplier;
 
 /**
  * Actual implementation of multi-client InputMethodManagerService.
@@ -1588,7 +1591,26 @@
 
         @BinderThread
         @Override
-        public InputBindResult startInputOrWindowGainedFocus(
+        public void startInputOrWindowGainedFocus(
+                @StartInputReason int startInputReason,
+                @Nullable IInputMethodClient client,
+                @Nullable IBinder windowToken,
+                @StartInputFlags int startInputFlags,
+                @SoftInputModeFlags int softInputMode,
+                int windowFlags,
+                @Nullable EditorInfo editorInfo,
+                @Nullable IInputContext inputContext,
+                @MissingMethodFlags int missingMethods,
+                int unverifiedTargetSdkVersion,
+                IInputBindResultResultCallback resultCallback) {
+            CallbackUtils.onResult(resultCallback, (Supplier<InputBindResult>) () ->
+                    startInputOrWindowGainedFocusInternal(startInputReason, client, windowToken,
+                            startInputFlags, softInputMode, windowFlags, editorInfo, inputContext,
+                            missingMethods, unverifiedTargetSdkVersion));
+        }
+
+        @BinderThread
+        private InputBindResult startInputOrWindowGainedFocusInternal(
                 @StartInputReason int startInputReason,
                 @Nullable IInputMethodClient client,
                 @Nullable IBinder windowToken,
@@ -1676,8 +1698,7 @@
                                 clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
                                         inputContext, missingMethods, editorInfo, startInputFlags,
                                         softInputMode, windowHandle);
-                            } catch (RemoteException e) {
-                            }
+                            } catch (RemoteException ignored) { }
                             break;
                     }
                     return InputBindResult.NULL_EDITOR_INFO;
@@ -1708,8 +1729,7 @@
                             clientInfo.mMSInputMethodSession.startInputOrWindowGainedFocus(
                                     inputContext, missingMethods, editorInfo, startInputFlags,
                                     softInputMode, windowHandle);
-                        } catch (RemoteException e) {
-                        }
+                        } catch (RemoteException ignored) { }
                         clientInfo.mState = InputMethodClientState.ALREADY_SENT_BIND_RESULT;
                         return new InputBindResult(
                                 InputBindResult.ResultCode.SUCCESS_WITH_IME_SESSION,
diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
index 28d2e69..ab91290 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java
@@ -17,6 +17,7 @@
 package com.android.server.integrity.parser;
 
 import android.annotation.Nullable;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.server.integrity.model.RuleMetadata;
@@ -26,7 +27,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 
 /** Helper class for parsing rule metadata. */
 public class RuleMetadataParser {
@@ -42,8 +42,7 @@
         String ruleProvider = "";
         String version = "";
 
-        XmlPullParser xmlPullParser = Xml.newPullParser();
-        xmlPullParser.setInput(inputStream, StandardCharsets.UTF_8.name());
+        TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream);
 
         int eventType;
         while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
index 5c51f31..7aed352 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java
@@ -19,6 +19,7 @@
 import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG;
 import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG;
 
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.server.integrity.model.RuleMetadata;
@@ -34,8 +35,7 @@
     /** Serialize the rule metadata to an output stream. */
     public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream)
             throws IOException {
-        XmlSerializer xmlSerializer = Xml.newSerializer();
-        xmlSerializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
+        TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);
 
         serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider());
         serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion());
@@ -43,8 +43,8 @@
         xmlSerializer.endDocument();
     }
 
-    private static void serializeTaggedValue(XmlSerializer xmlSerializer, String tag, String value)
-            throws IOException {
+    private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag,
+            String value) throws IOException {
         xmlSerializer.startTag(/* namespace= */ null, tag);
         xmlSerializer.text(value);
         xmlSerializer.endTag(/* namespace= */ null, tag);
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index a8889fd..9e12667 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -987,21 +987,17 @@
             // update client uids
             updateClientUids(mProviderRequest.getWorkSource());
 
-            mFixInterval = (int) mProviderRequest.getIntervalMillis();
-            // check for overflow
-            if (mFixInterval != mProviderRequest.getIntervalMillis()) {
+            if (mProviderRequest.getIntervalMillis() <= Integer.MAX_VALUE) {
+                mFixInterval = (int) mProviderRequest.getIntervalMillis();
+            } else {
                 Log.w(TAG, "interval overflow: " + mProviderRequest.getIntervalMillis());
                 mFixInterval = Integer.MAX_VALUE;
             }
-            // requested batch size, or zero to disable batching
-            int batchSize;
-            try {
-                batchSize = mBatchingEnabled ? Math.toIntExact(
-                        mProviderRequest.getMaxUpdateDelayMillis() / mFixInterval) : 0;
-            } catch (ArithmeticException e) {
-                batchSize = Integer.MAX_VALUE;
-            }
 
+            // requested batch size, or zero to disable batching
+            long batchSize =
+                    mBatchingEnabled ? mProviderRequest.getMaxUpdateDelayMillis() / Math.max(
+                            mFixInterval, 1) : 0;
             if (batchSize < getBatchSize()) {
                 batchSize = 0;
             }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
index 8a19d62..0c209c5 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
@@ -46,6 +45,7 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Base64;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -84,8 +84,7 @@
 
     private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException,
             XmlPullParserException, KeyChainSnapshotParserException {
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(inputStream, OUTPUT_ENCODING);
+        TypedXmlPullParser parser = Xml.resolvePullParser(inputStream);
 
         parser.nextTag();
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
@@ -156,7 +155,7 @@
         }
     }
 
-    private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser)
+    private static List<WrappedApplicationKey> readWrappedApplicationKeys(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
         ArrayList<WrappedApplicationKey> keys = new ArrayList<>();
@@ -170,7 +169,7 @@
         return keys;
     }
 
-    private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser)
+    private static WrappedApplicationKey readWrappedApplicationKey(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY);
         WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder();
@@ -209,7 +208,7 @@
     }
 
     private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList(
-            XmlPullParser parser) throws IOException, XmlPullParserException,
+            TypedXmlPullParser parser) throws IOException, XmlPullParserException,
             KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
 
@@ -225,7 +224,7 @@
         return keyChainProtectionParamsList;
     }
 
-    private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser)
+    private static KeyChainProtectionParams readKeyChainProtectionParams(TypedXmlPullParser parser)
         throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
 
@@ -269,7 +268,7 @@
         }
     }
 
-    private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser)
+    private static KeyDerivationParams readKeyDerivationParams(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
 
@@ -331,7 +330,7 @@
         return keyDerivationParams;
     }
 
-    private static int readIntTag(XmlPullParser parser, String tagName)
+    private static int readIntTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -345,7 +344,7 @@
         }
     }
 
-    private static long readLongTag(XmlPullParser parser, String tagName)
+    private static long readLongTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -359,7 +358,7 @@
         }
     }
 
-    private static String readStringTag(XmlPullParser parser, String tagName)
+    private static String readStringTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -367,7 +366,7 @@
         return text;
     }
 
-    private static byte[] readBlobTag(XmlPullParser parser, String tagName)
+    private static byte[] readBlobTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
         String text = readText(parser);
@@ -384,7 +383,7 @@
         }
     }
 
-    private static CertPath readCertPathTag(XmlPullParser parser, String tagName)
+    private static CertPath readCertPathTag(TypedXmlPullParser parser, String tagName)
             throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
         byte[] bytes = readBlobTag(parser, tagName);
         try {
@@ -396,7 +395,7 @@
         }
     }
 
-    private static String readText(XmlPullParser parser)
+    private static String readText(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         String result = "";
         if (parser.next() == XmlPullParser.TEXT) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
index 8f85a27..6475d9e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -22,8 +22,6 @@
 class KeyChainSnapshotSchema {
     static final String NAMESPACE = null;
 
-    static final String OUTPUT_ENCODING = "UTF-8";
-
     static final String CERTIFICATE_FACTORY_TYPE = "X.509";
     static final String CERT_PATH_ENCODING = "PkiPath";
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
index 527e879..eb34e98 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -19,7 +19,6 @@
 
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
-import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
 import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
@@ -47,10 +46,9 @@
 import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Base64;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
-import org.xmlpull.v1.XmlSerializer;
-
 import java.io.IOException;
 import java.io.OutputStream;
 import java.security.cert.CertPath;
@@ -71,8 +69,7 @@
      */
     public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream)
             throws IOException, CertificateEncodingException {
-        XmlSerializer xmlSerializer = Xml.newSerializer();
-        xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING);
+        TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream);
         xmlSerializer.startDocument(
                 /*encoding=*/ null,
                 /*standalone=*/ null);
@@ -87,7 +84,7 @@
     }
 
     private static void writeApplicationKeys(
-            XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
+            TypedXmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS);
         for (WrappedApplicationKey key : wrappedApplicationKeys) {
@@ -98,15 +95,15 @@
         xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS);
     }
 
-    private static void writeApplicationKeyProperties(
-            XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
+    private static void writeApplicationKeyProperties(TypedXmlSerializer xmlSerializer,
+            WrappedApplicationKey applicationKey) throws IOException {
         writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
         writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
         writePropertyTag(xmlSerializer, TAG_KEY_METADATA, applicationKey.getMetadata());
     }
 
     private static void writeKeyChainProtectionParams(
-            XmlSerializer xmlSerializer,
+            TypedXmlSerializer xmlSerializer,
             List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
         for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) {
@@ -118,7 +115,7 @@
     }
 
     private static void writeKeyChainProtectionParamsProperties(
-            XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
+            TypedXmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
             throws IOException {
         writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE,
                 keyChainProtectionParams.getUserSecretType());
@@ -132,7 +129,7 @@
     }
 
     private static void writeKeyDerivationParams(
-            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
         writeKeyDerivationParamsProperties(
@@ -141,7 +138,7 @@
     }
 
     private static void writeKeyDerivationParamsProperties(
-            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            TypedXmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
             throws IOException {
         writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm());
         writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt());
@@ -150,7 +147,7 @@
     }
 
     private static void writeKeyChainSnapshotProperties(
-            XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
+            TypedXmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
             throws IOException, CertificateEncodingException {
 
         writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION,
@@ -165,7 +162,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, long propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, long propertyValue)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, propertyName);
         xmlSerializer.text(Long.toString(propertyValue));
@@ -173,7 +170,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, String propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, String propertyValue)
             throws IOException {
         xmlSerializer.startTag(NAMESPACE, propertyName);
         xmlSerializer.text(propertyValue);
@@ -181,7 +178,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
+            TypedXmlSerializer xmlSerializer, String propertyName, @Nullable byte[] propertyValue)
             throws IOException {
         if (propertyValue == null) {
             return;
@@ -192,7 +189,7 @@
     }
 
     private static void writePropertyTag(
-            XmlSerializer xmlSerializer, String propertyName, CertPath certPath)
+            TypedXmlSerializer xmlSerializer, String propertyName, CertPath certPath)
             throws IOException, CertificateEncodingException {
         writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING));
     }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index bdf0fb9..8200ca0 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -34,6 +34,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -110,7 +111,7 @@
     }
 
     @Override
-    void writeDefaults(XmlSerializer out) throws IOException {
+    void writeDefaults(TypedXmlSerializer out) throws IOException {
         synchronized (mDefaultsLock) {
             String defaults = String.join(ENABLED_SERVICES_SEPARATOR, mDefaultPackages);
             out.attribute(null, ATT_DEFAULTS, defaults);
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index f2e3708..a7ee272 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -60,6 +60,8 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
@@ -443,7 +445,7 @@
         }
     }
 
-    void writeDefaults(XmlSerializer out) throws IOException {
+    void writeDefaults(TypedXmlSerializer out) throws IOException {
         synchronized (mDefaultsLock) {
             List<String> componentStrings = new ArrayList<>(mDefaultComponents.size());
             for (int i = 0; i < mDefaultComponents.size(); i++) {
@@ -454,10 +456,10 @@
         }
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
         out.startTag(null, getConfig().xmlTag);
 
-        out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+        out.attributeInt(null, ATT_VERSION, DB_VERSION);
 
         writeDefaults(out);
 
@@ -485,8 +487,8 @@
                                     : String.join(ENABLED_SERVICES_SEPARATOR, approved);
                             out.startTag(null, TAG_MANAGED_SERVICES);
                             out.attribute(null, ATT_APPROVED_LIST, allowedItems);
-                            out.attribute(null, ATT_USER_ID, Integer.toString(approvedUserId));
-                            out.attribute(null, ATT_IS_PRIMARY, Boolean.toString(isPrimary));
+                            out.attributeInt(null, ATT_USER_ID, approvedUserId);
+                            out.attributeBoolean(null, ATT_IS_PRIMARY, isPrimary);
                             if (userSet != null) {
                                 String userSetItems =
                                         String.join(ENABLED_SERVICES_SEPARATOR, userSet);
@@ -516,23 +518,23 @@
     /**
      * Writes extra xml attributes to {@link #TAG_MANAGED_SERVICES} tag.
      */
-    protected void writeExtraAttributes(XmlSerializer out, int userId) throws IOException {}
+    protected void writeExtraAttributes(TypedXmlSerializer out, int userId) throws IOException {}
 
     /**
      * Writes extra xml tags within the parent tag specified in {@link Config#xmlTag}.
      */
-    protected void writeExtraXmlTags(XmlSerializer out) throws IOException {}
+    protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {}
 
     /**
      * This is called to process tags other than {@link #TAG_MANAGED_SERVICES}.
      */
-    protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {}
+    protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {}
 
     protected void migrateToXml() {
         loadAllowedComponentsFromSettings();
     }
 
-    void readDefaults(XmlPullParser parser) {
+    void readDefaults(TypedXmlPullParser parser) {
         String defaultComponents = XmlUtils.readStringAttribute(parser, ATT_DEFAULTS);
 
         if (!TextUtils.isEmpty(defaultComponents)) {
@@ -554,7 +556,7 @@
     }
 
     public void readXml(
-            XmlPullParser parser,
+            TypedXmlPullParser parser,
             TriPredicate<String, Integer, String> allowedManagedServicePackages,
             boolean forRestore,
             int userId)
@@ -577,9 +579,9 @@
                     final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
                     // Ignore parser's user id for restore.
                     final int resolvedUserId = forRestore
-                            ? userId : XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
+                            ? userId : parser.getAttributeInt(null, ATT_USER_ID, 0);
                     final boolean isPrimary =
-                            XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true);
+                            parser.getAttributeBoolean(null, ATT_IS_PRIMARY, true);
                     final String userSet = XmlUtils.readStringAttribute(parser, ATT_USER_SET);
                     readExtraAttributes(tag, parser, resolvedUserId);
                     if (allowedManagedServicePackages == null || allowedManagedServicePackages.test(
@@ -631,7 +633,7 @@
     /**
      * Read extra attributes in the {@link #TAG_MANAGED_SERVICES} tag.
      */
-    protected void readExtraAttributes(String tag, XmlPullParser parser, int userId)
+    protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
             throws IOException {}
 
     protected abstract String getRequiredPermission();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b1289dd..76b9c86 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -231,6 +231,8 @@
 import android.util.SparseArray;
 import android.util.StatsEvent;
 import android.util.Xml;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -743,8 +745,13 @@
 
     void readPolicyXml(InputStream stream, boolean forRestore, int userId)
             throws XmlPullParserException, NumberFormatException, IOException {
-        final XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(stream, StandardCharsets.UTF_8.name());
+        final TypedXmlPullParser parser;
+        if (forRestore) {
+            parser = Xml.newFastPullParser();
+            parser.setInput(stream, StandardCharsets.UTF_8.name());
+        } else {
+            parser = Xml.resolvePullParser(stream);
+        }
         XmlUtils.beginDocument(parser, TAG_NOTIFICATION_POLICY);
         boolean migratedManagedServices = false;
         boolean ineligibleForManagedServices = forRestore && mUm.isManagedProfile(userId);
@@ -781,9 +788,8 @@
                 if (forRestore && userId != UserHandle.USER_SYSTEM) {
                     continue;
                 }
-                mLockScreenAllowSecureNotifications =
-                        safeBoolean(parser.getAttributeValue(null,
-                                        LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE), true);
+                mLockScreenAllowSecureNotifications = parser.getAttributeBoolean(null,
+                        LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE, true);
             }
         }
 
@@ -856,11 +862,16 @@
 
     private void writePolicyXml(OutputStream stream, boolean forBackup, int userId)
             throws IOException {
-        final XmlSerializer out = new FastXmlSerializer();
-        out.setOutput(stream, StandardCharsets.UTF_8.name());
+        final TypedXmlSerializer out;
+        if (forBackup) {
+            out = Xml.newFastSerializer();
+            out.setOutput(stream, StandardCharsets.UTF_8.name());
+        } else {
+            out = Xml.resolveSerializer(stream);
+        }
         out.startDocument(null, true);
         out.startTag(null, TAG_NOTIFICATION_POLICY);
-        out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
+        out.attributeInt(null, ATTR_VERSION, DB_VERSION);
         mZenModeHelper.writeXml(out, forBackup, null, userId);
         mPreferencesHelper.writeXml(out, forBackup, userId);
         mListeners.writeXml(out, forBackup, userId);
@@ -8975,7 +8986,7 @@
         }
 
         @Override
-        protected void writeExtraXmlTags(XmlSerializer out) throws IOException {
+        protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
             synchronized (mLock) {
                 out.startTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES);
                 out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustments));
@@ -8984,7 +8995,7 @@
         }
 
         @Override
-        protected void readExtraTag(String tag, XmlPullParser parser) throws IOException {
+        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
             if (TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
                 final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
                 synchronized (mLock) {
@@ -9104,9 +9115,9 @@
         }
 
         @Override
-        protected void readExtraAttributes(String tag, XmlPullParser parser, int userId)
+        protected void readExtraAttributes(String tag, TypedXmlPullParser parser, int userId)
                 throws IOException {
-            boolean userSet = XmlUtils.readBooleanAttribute(parser, ATT_USER_SET, false);
+            boolean userSet = parser.getAttributeBoolean(null, ATT_USER_SET, false);
             setUserSet(userId, userSet);
         }
 
@@ -10106,18 +10117,13 @@
         }
     }
 
-    private void writeSecureNotificationsPolicy(XmlSerializer out) throws IOException {
+    private void writeSecureNotificationsPolicy(TypedXmlSerializer out) throws IOException {
         out.startTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG);
-        out.attribute(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE,
-                Boolean.toString(mLockScreenAllowSecureNotifications));
+        out.attributeBoolean(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_VALUE,
+                mLockScreenAllowSecureNotifications);
         out.endTag(null, LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG);
     }
 
-    private static boolean safeBoolean(String val, boolean defValue) {
-        if (TextUtils.isEmpty(val)) return defValue;
-        return Boolean.parseBoolean(val);
-    }
-
     /**
      * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too
      * aggressive and annoying the user.
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 2f990c6..1c0349d 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -56,6 +56,8 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -195,21 +197,15 @@
         syncChannelsBypassingDnd(mContext.getUserId());
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore, int userId)
+    public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
             throws XmlPullParserException, IOException {
         int type = parser.getEventType();
         if (type != XmlPullParser.START_TAG) return;
         String tag = parser.getName();
         if (!TAG_RANKING.equals(tag)) return;
 
-        boolean upgradeForBubbles = false;
-        if (parser.getAttributeCount() > 0) {
-            String attribute = parser.getAttributeName(0);
-            if (ATT_VERSION.equals(attribute)) {
-                int xmlVersion = Integer.parseInt(parser.getAttributeValue(0));
-                upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
-            }
-        }
+        final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1);
+        boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE;
         synchronized (mPackagePreferences) {
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                 tag = parser.getName();
@@ -221,10 +217,10 @@
                         if (forRestore && userId != UserHandle.USER_SYSTEM) {
                             continue;
                         }
-                        mHideSilentStatusBarIcons = XmlUtils.readBooleanAttribute(
-                                parser, ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
+                        mHideSilentStatusBarIcons = parser.getAttributeBoolean(null,
+                                ATT_HIDE_SILENT, DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS);
                     } else if (TAG_PACKAGE.equals(tag)) {
-                        int uid = XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+                        int uid = parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID);
                         String name = parser.getAttributeValue(null, ATT_NAME);
                         if (!TextUtils.isEmpty(name)) {
                             if (forRestore) {
@@ -243,36 +239,36 @@
                             }
                             int bubblePref = hasSAWPermission
                                     ? BUBBLE_PREFERENCE_ALL
-                                    : XmlUtils.readIntAttribute(parser, ATT_ALLOW_BUBBLE,
-                                            DEFAULT_BUBBLE_PREFERENCE);
+                                    : parser.getAttributeInt(
+                                            null, ATT_ALLOW_BUBBLE, DEFAULT_BUBBLE_PREFERENCE);
 
                             PackagePreferences r = getOrCreatePackagePreferencesLocked(
                                     name, userId, uid,
-                                    XmlUtils.readIntAttribute(
-                                            parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
-                                    XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
-                                            DEFAULT_PRIORITY),
-                                    XmlUtils.readIntAttribute(
-                                            parser, ATT_VISIBILITY, DEFAULT_VISIBILITY),
-                                    XmlUtils.readBooleanAttribute(
-                                            parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
+                                    parser.getAttributeInt(
+                                            null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
+                                    parser.getAttributeInt(
+                                            null, ATT_PRIORITY, DEFAULT_PRIORITY),
+                                    parser.getAttributeInt(
+                                            null, ATT_VISIBILITY, DEFAULT_VISIBILITY),
+                                    parser.getAttributeBoolean(
+                                            null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE),
                                     bubblePref);
-                            r.importance = XmlUtils.readIntAttribute(
-                                    parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
-                            r.priority = XmlUtils.readIntAttribute(
-                                    parser, ATT_PRIORITY, DEFAULT_PRIORITY);
-                            r.visibility = XmlUtils.readIntAttribute(
-                                    parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
-                            r.showBadge = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
-                            r.lockedAppFields = XmlUtils.readIntAttribute(parser,
-                                    ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
-                            r.hasSentInvalidMessage = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SENT_INVALID_MESSAGE, false);
-                            r.hasSentValidMessage = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_SENT_VALID_MESSAGE, false);
-                            r.userDemotedMsgApp = XmlUtils.readBooleanAttribute(
-                                    parser, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
+                            r.importance = parser.getAttributeInt(
+                                    null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                            r.priority = parser.getAttributeInt(
+                                    null, ATT_PRIORITY, DEFAULT_PRIORITY);
+                            r.visibility = parser.getAttributeInt(
+                                    null, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+                            r.showBadge = parser.getAttributeBoolean(
+                                    null, ATT_SHOW_BADGE, DEFAULT_SHOW_BADGE);
+                            r.lockedAppFields = parser.getAttributeInt(
+                                    null, ATT_APP_USER_LOCKED_FIELDS, DEFAULT_LOCKED_APP_FIELDS);
+                            r.hasSentInvalidMessage = parser.getAttributeBoolean(
+                                    null, ATT_SENT_INVALID_MESSAGE, false);
+                            r.hasSentValidMessage = parser.getAttributeBoolean(
+                                    null, ATT_SENT_VALID_MESSAGE, false);
+                            r.userDemotedMsgApp = parser.getAttributeBoolean(
+                                    null, ATT_USER_DEMOTED_INVALID_MSG_APP, false);
 
                             final int innerDepth = parser.getDepth();
                             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -307,8 +303,8 @@
                                     }
                                     String id = parser.getAttributeValue(null, ATT_ID);
                                     String channelName = parser.getAttributeValue(null, ATT_NAME);
-                                    int channelImportance = XmlUtils.readIntAttribute(
-                                            parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
+                                    int channelImportance = parser.getAttributeInt(
+                                            null, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
                                     if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
                                         NotificationChannel channel = new NotificationChannel(id,
                                                 channelName, channelImportance);
@@ -338,14 +334,13 @@
                                 // Delegate
                                 if (TAG_DELEGATE.equals(tagName)) {
                                     int delegateId =
-                                            XmlUtils.readIntAttribute(parser, ATT_UID, UNKNOWN_UID);
+                                            parser.getAttributeInt(null, ATT_UID, UNKNOWN_UID);
                                     String delegateName =
                                             XmlUtils.readStringAttribute(parser, ATT_NAME);
-                                    boolean delegateEnabled = XmlUtils.readBooleanAttribute(
-                                            parser, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
-                                    boolean userAllowed = XmlUtils.readBooleanAttribute(
-                                            parser, ATT_USER_ALLOWED,
-                                            Delegate.DEFAULT_USER_ALLOWED);
+                                    boolean delegateEnabled = parser.getAttributeBoolean(
+                                            null, ATT_ENABLED, Delegate.DEFAULT_ENABLED);
+                                    boolean userAllowed = parser.getAttributeBoolean(
+                                            null, ATT_USER_ALLOWED, Delegate.DEFAULT_USER_ALLOWED);
                                     Delegate d = null;
                                     if (delegateId != UNKNOWN_UID && !TextUtils.isEmpty(
                                             delegateName)) {
@@ -502,13 +497,13 @@
         return true;
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, int userId) throws IOException {
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, int userId) throws IOException {
         out.startTag(null, TAG_RANKING);
-        out.attribute(null, ATT_VERSION, Integer.toString(XML_VERSION));
+        out.attributeInt(null, ATT_VERSION, XML_VERSION);
         if (mHideSilentStatusBarIcons != DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS
                 && (!forBackup || userId == UserHandle.USER_SYSTEM)) {
             out.startTag(null, TAG_STATUS_ICONS);
-            out.attribute(null, ATT_HIDE_SILENT, String.valueOf(mHideSilentStatusBarIcons));
+            out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons);
             out.endTag(null, TAG_STATUS_ICONS);
         }
 
@@ -536,42 +531,41 @@
                     out.startTag(null, TAG_PACKAGE);
                     out.attribute(null, ATT_NAME, r.pkg);
                     if (r.importance != DEFAULT_IMPORTANCE) {
-                        out.attribute(null, ATT_IMPORTANCE, Integer.toString(r.importance));
+                        out.attributeInt(null, ATT_IMPORTANCE, r.importance);
                     }
                     if (r.priority != DEFAULT_PRIORITY) {
-                        out.attribute(null, ATT_PRIORITY, Integer.toString(r.priority));
+                        out.attributeInt(null, ATT_PRIORITY, r.priority);
                     }
                     if (r.visibility != DEFAULT_VISIBILITY) {
-                        out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
+                        out.attributeInt(null, ATT_VISIBILITY, r.visibility);
                     }
                     if (r.bubblePreference != DEFAULT_BUBBLE_PREFERENCE) {
-                        out.attribute(null, ATT_ALLOW_BUBBLE, Integer.toString(r.bubblePreference));
+                        out.attributeInt(null, ATT_ALLOW_BUBBLE, r.bubblePreference);
                     }
-                    out.attribute(null, ATT_SHOW_BADGE, Boolean.toString(r.showBadge));
-                    out.attribute(null, ATT_APP_USER_LOCKED_FIELDS,
-                            Integer.toString(r.lockedAppFields));
-                    out.attribute(null, ATT_SENT_INVALID_MESSAGE,
-                            Boolean.toString(r.hasSentInvalidMessage));
-                    out.attribute(null, ATT_SENT_VALID_MESSAGE,
-                            Boolean.toString(r.hasSentValidMessage));
-                    out.attribute(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
-                            Boolean.toString(r.userDemotedMsgApp));
+                    out.attributeBoolean(null, ATT_SHOW_BADGE, r.showBadge);
+                    out.attributeInt(null, ATT_APP_USER_LOCKED_FIELDS,
+                            r.lockedAppFields);
+                    out.attributeBoolean(null, ATT_SENT_INVALID_MESSAGE,
+                            r.hasSentInvalidMessage);
+                    out.attributeBoolean(null, ATT_SENT_VALID_MESSAGE,
+                            r.hasSentValidMessage);
+                    out.attributeBoolean(null, ATT_USER_DEMOTED_INVALID_MSG_APP,
+                            r.userDemotedMsgApp);
 
                     if (!forBackup) {
-                        out.attribute(null, ATT_UID, Integer.toString(r.uid));
+                        out.attributeInt(null, ATT_UID, r.uid);
                     }
 
                     if (r.delegate != null) {
                         out.startTag(null, TAG_DELEGATE);
 
                         out.attribute(null, ATT_NAME, r.delegate.mPkg);
-                        out.attribute(null, ATT_UID, Integer.toString(r.delegate.mUid));
+                        out.attributeInt(null, ATT_UID, r.delegate.mUid);
                         if (r.delegate.mEnabled != Delegate.DEFAULT_ENABLED) {
-                            out.attribute(null, ATT_ENABLED, Boolean.toString(r.delegate.mEnabled));
+                            out.attributeBoolean(null, ATT_ENABLED, r.delegate.mEnabled);
                         }
                         if (r.delegate.mUserAllowed != Delegate.DEFAULT_USER_ALLOWED) {
-                            out.attribute(null, ATT_USER_ALLOWED,
-                                    Boolean.toString(r.delegate.mUserAllowed));
+                            out.attributeBoolean(null, ATT_USER_ALLOWED, r.delegate.mUserAllowed);
                         }
                         out.endTag(null, TAG_DELEGATE);
                     }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index f7d69fd..b5ca2ab 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -32,6 +32,8 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -57,7 +59,7 @@
  * NotificationManagerService helper for handling snoozed notifications.
  */
 public class SnoozeHelper {
-    public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+    public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1;
 
     protected static final String XML_TAG_NAME = "snoozed-notifications";
 
@@ -547,7 +549,7 @@
         }
     }
 
-    protected void writeXml(XmlSerializer out) throws IOException {
+    protected void writeXml(TypedXmlSerializer out) throws IOException {
         synchronized (mLock) {
             final long currentTime = System.currentTimeMillis();
             out.startTag(null, XML_TAG_NAME);
@@ -573,7 +575,7 @@
         void insert(T t) throws IOException;
     }
 
-    private <T> void writeXml(XmlSerializer out,
+    private <T> void writeXml(TypedXmlSerializer out,
             ArrayMap<String, ArrayMap<String, T>> targets, String tag,
             Inserter<T> attributeInserter)
             throws IOException {
@@ -596,21 +598,18 @@
 
                 attributeInserter.insert(value);
 
-                out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+                out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
                         XML_SNOOZED_NOTIFICATION_VERSION);
                 out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
-
-
                 out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
-                out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
-                        String.valueOf(userId));
+                out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId);
 
                 out.endTag(null, tag);
             }
         }
     }
 
-    protected void readXml(XmlPullParser parser, long currentTime)
+    protected void readXml(TypedXmlPullParser parser, long currentTime)
             throws XmlPullParserException, IOException {
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -622,16 +621,16 @@
             if (type == XmlPullParser.START_TAG
                     && (XML_SNOOZED_NOTIFICATION.equals(tag)
                         || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
-                    && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
-                        .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+                    && parser.getAttributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, -1)
+                        == XML_SNOOZED_NOTIFICATION_VERSION) {
                 try {
                     final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
                     final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
-                    final int userId = XmlUtils.readIntAttribute(
-                            parser, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
+                    final int userId = parser.getAttributeInt(
+                            null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL);
                     if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
-                        final Long time = XmlUtils.readLongAttribute(
-                                parser, XML_SNOOZED_NOTIFICATION_TIME, 0);
+                        final Long time = parser.getAttributeLong(
+                                null, XML_SNOOZED_NOTIFICATION_TIME, 0);
                         if (time > currentTime) { //only read new stuff
                             synchronized (mLock) {
                                 storeRecordLocked(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 13cd6e5..94f46ba 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -72,6 +72,8 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -702,7 +704,7 @@
         }
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore, int userId)
+    public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
             throws XmlPullParserException, IOException {
         ZenModeConfig config = ZenModeConfig.readXml(parser);
         String reason = "readXml";
@@ -761,7 +763,7 @@
         }
     }
 
-    public void writeXml(XmlSerializer out, boolean forBackup, Integer version, int userId)
+    public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
             throws IOException {
         synchronized (mConfigs) {
             final int n = mConfigs.size();
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 8a2d823..0613dff 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -411,7 +411,7 @@
                 table.clear();
                 final TypedXmlPullParser parser = Xml.resolvePullParser(is);
                 XmlUtils.beginDocument(parser, TAG_OVERLAYS);
-                int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION);
+                int version = parser.getAttributeInt(null, ATTR_VERSION);
                 if (version != CURRENT_VERSION) {
                     upgrade(version);
                 }
@@ -445,19 +445,19 @@
             }
         }
 
-        private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth)
-                throws IOException {
+        private static SettingsItem restoreRow(@NonNull final TypedXmlPullParser parser,
+                final int depth) throws IOException, XmlPullParserException {
             final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME);
-            final int userId = XmlUtils.readIntAttribute(parser, ATTR_USER_ID);
+            final int userId = parser.getAttributeInt(null, ATTR_USER_ID);
             final String targetPackageName = XmlUtils.readStringAttribute(parser,
                     ATTR_TARGET_PACKAGE_NAME);
             final String targetOverlayableName = XmlUtils.readStringAttribute(parser,
                     ATTR_TARGET_OVERLAYABLE_NAME);
             final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH);
-            final int state = XmlUtils.readIntAttribute(parser, ATTR_STATE);
-            final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED);
-            final boolean isStatic = XmlUtils.readBooleanAttribute(parser, ATTR_IS_STATIC);
-            final int priority = XmlUtils.readIntAttribute(parser, ATTR_PRIORITY);
+            final int state = parser.getAttributeInt(null, ATTR_STATE);
+            final boolean isEnabled = parser.getAttributeBoolean(null, ATTR_IS_ENABLED, false);
+            final boolean isStatic = parser.getAttributeBoolean(null, ATTR_IS_STATIC, false);
+            final int priority = parser.getAttributeInt(null, ATTR_PRIORITY);
             final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);
 
             return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
@@ -470,7 +470,7 @@
             xml.startDocument(null, true);
             xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
             xml.startTag(null, TAG_OVERLAYS);
-            XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION);
+            xml.attributeInt(null, ATTR_VERSION, CURRENT_VERSION);
 
             final int n = table.size();
             for (int i = 0; i < n; i++) {
@@ -485,15 +485,15 @@
                 @NonNull final SettingsItem item) throws IOException {
             xml.startTag(null, TAG_ITEM);
             XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.mPackageName);
-            XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.mUserId);
+            xml.attributeInt(null, ATTR_USER_ID, item.mUserId);
             XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.mTargetPackageName);
             XmlUtils.writeStringAttribute(xml, ATTR_TARGET_OVERLAYABLE_NAME,
                     item.mTargetOverlayableName);
             XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath);
-            XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState);
+            xml.attributeInt(null, ATTR_STATE, item.mState);
             XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled);
             XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
-            XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority);
+            xml.attributeInt(null, ATTR_PRIORITY, item.mPriority);
             XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
             xml.endTag(null, TAG_ITEM);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db5eb84..e95b1a2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -21,7 +21,6 @@
 import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
-import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_IGNORED;
 import static android.content.Intent.ACTION_MAIN;
@@ -44,7 +43,6 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
@@ -343,7 +341,6 @@
 import com.android.internal.content.om.OverlayConfig;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.os.Zygote;
 import com.android.internal.telephony.CarrierAppUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
@@ -2172,7 +2169,7 @@
 
     private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
             boolean killApp, boolean virtualPreload,
-            String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
+            String[] grantedPermissions, List<String> allowlistedRestrictedPermissions,
             int autoRevokePermissionsMode,
             boolean launchedForRestore, String installerPackage,
             IPackageInstallObserver2 installObserver, int dataLoaderType) {
@@ -2205,31 +2202,22 @@
                 res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
             }
 
-            // Allowlist any restricted permissions first as some may be runtime
-            // that the installer requested to be granted at install time.
-            if (whitelistedRestrictedPermissions != null
-                    && !whitelistedRestrictedPermissions.isEmpty()) {
-                mPermissionManager.setAllowlistedRestrictedPermissions(res.pkg,
-                        whitelistedRestrictedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER,
-                        res.newUsers);
-            }
-
-            if (autoRevokePermissionsMode == MODE_ALLOWED
-                    || autoRevokePermissionsMode == MODE_IGNORED) {
-                mPermissionManager.setAutoRevokeExempted(res.pkg,
-                        autoRevokePermissionsMode == MODE_IGNORED, res.newUsers);
-            }
-
-            // Now that we successfully installed the package, grant runtime
-            // permissions if requested before broadcasting the install. Also
-            // for legacy apps in permission review mode we clear the permission
-            // review flag which is used to emulate runtime permissions for
-            // legacy apps.
+            final List<String> grantedPermissionsList;
             if (grantPermissions) {
-                final int callingUid = Binder.getCallingUid();
-                mPermissionManager.grantRequestedRuntimePermissions(res.pkg,
-                        grantedPermissions != null ? Arrays.asList(grantedPermissions) : null,
-                        res.newUsers);
+                if (grantedPermissions != null) {
+                    grantedPermissionsList = Arrays.asList(grantedPermissions);
+                } else {
+                    grantedPermissionsList = res.pkg.getRequestedPermissions();
+                }
+            } else {
+                grantedPermissionsList = Collections.emptyList();
+            }
+            if (allowlistedRestrictedPermissions == null) {
+                allowlistedRestrictedPermissions = Collections.emptyList();
+            }
+            for (final int userId : res.newUsers) {
+                mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList,
+                        allowlistedRestrictedPermissions, autoRevokePermissionsMode, userId);
             }
 
             final String installerPackageName =
@@ -4105,10 +4093,7 @@
         // feature flags should cause us to invalidate any caches.
         final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug"
                 : injector.getSystemWrapper().digestOfProperties(
-                        "ro.build.fingerprint",
-                        StorageManager.PROP_ISOLATED_STORAGE,
-                        StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT
-                );
+                        "ro.build.fingerprint");
 
         // Reconcile cache directories, keeping only what we'd actually use.
         for (File cacheDir : FileUtils.listFilesOrEmpty(cacheBaseDir)) {
@@ -13611,11 +13596,12 @@
     int installExistingPackageAsUser(@Nullable String packageName, @UserIdInt int userId,
             @PackageManager.InstallFlags int installFlags,
             @PackageManager.InstallReason int installReason,
-            @Nullable List<String> whiteListedPermissions, @Nullable IntentSender intentSender) {
+            @Nullable List<String> allowlistedRestrictedPermissions,
+            @Nullable IntentSender intentSender) {
         if (DEBUG_INSTALL) {
             Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId
                     + " installFlags=" + installFlags + " installReason=" + installReason
-                    + " whiteListedPermissions=" + whiteListedPermissions);
+                    + " allowlistedRestrictedPermissions=" + allowlistedRestrictedPermissions);
         }
 
         final int callingUid = Binder.getCallingUid();
@@ -13681,11 +13667,12 @@
                 if (pkgSetting.pkg != null) {
                     if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
                             != 0) {
-                        whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions();
+                        allowlistedRestrictedPermissions = pkgSetting.pkg.getRequestedPermissions();
+                    } else if (allowlistedRestrictedPermissions == null) {
+                        allowlistedRestrictedPermissions = Collections.emptyList();
                     }
-                    mPermissionManager.setAllowlistedRestrictedPermissions(pkgSetting.pkg,
-                            whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER,
-                            new int[] { userId });
+                    mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(),
+                            allowlistedRestrictedPermissions, MODE_DEFAULT, userId);
                 }
 
                 if (pkgSetting.pkg != null) {
@@ -22221,22 +22208,6 @@
         mInstallerService.systemReady();
         mPackageDexOptimizer.systemReady();
 
-        mInjector.getLocalService(StorageManagerInternal.class).addExternalStoragePolicy(
-                new StorageManagerInternal.ExternalStorageMountPolicy() {
-                    @Override
-                    public int getMountMode(int uid, String packageName) {
-                        if (Process.isIsolated(uid)) {
-                            return Zygote.MOUNT_EXTERNAL_NONE;
-                        }
-                        return Zygote.MOUNT_EXTERNAL_DEFAULT;
-                    }
-
-                    @Override
-                    public boolean hasExternalStorage(int uid, String packageName) {
-                        return true;
-                    }
-        });
-
         // Now that we're mostly running, clean up stale users and apps
         mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
         reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
diff --git a/services/core/java/com/android/server/pm/PackageProperty.java b/services/core/java/com/android/server/pm/PackageProperty.java
index d18a02d..ee9ed3b 100644
--- a/services/core/java/com/android/server/pm/PackageProperty.java
+++ b/services/core/java/com/android/server/pm/PackageProperty.java
@@ -274,7 +274,7 @@
 
     private Property getApplicationProperty(String propertyName, String packageName) {
         final ArrayMap<String, ArrayList<Property>> packagePropertyMap =
-                mApplicationProperties.get(propertyName);
+                mApplicationProperties != null ? mApplicationProperties.get(propertyName) : null;
         if (packagePropertyMap == null) {
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/ShareTargetInfo.java b/services/core/java/com/android/server/pm/ShareTargetInfo.java
index fdfee77..660874e 100644
--- a/services/core/java/com/android/server/pm/ShareTargetInfo.java
+++ b/services/core/java/com/android/server/pm/ShareTargetInfo.java
@@ -17,10 +17,11 @@
 
 import android.annotation.NonNull;
 import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -123,7 +124,7 @@
         return strBuilder.toString();
     }
 
-    void saveToXml(@NonNull XmlSerializer out) throws IOException {
+    void saveToXml(@NonNull TypedXmlSerializer out) throws IOException {
         out.startTag(null, TAG_SHARE_TARGET);
 
         ShortcutService.writeAttr(out, ATTR_TARGET_CLASS, mTargetClass);
@@ -149,7 +150,7 @@
         out.endTag(null, TAG_SHARE_TARGET);
     }
 
-    static ShareTargetInfo loadFromXml(XmlPullParser parser)
+    static ShareTargetInfo loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final String targetClass = ShortcutService.parseStringAttribute(parser, ATTR_TARGET_CLASS);
         final ArrayList<ShareTargetInfo.TargetData> targetData = new ArrayList<>();
@@ -178,7 +179,7 @@
                 targetClass, categories.toArray(new String[categories.size()]));
     }
 
-    private static ShareTargetInfo.TargetData parseTargetData(XmlPullParser parser) {
+    private static ShareTargetInfo.TargetData parseTargetData(TypedXmlPullParser parser) {
         final String scheme = ShortcutService.parseStringAttribute(parser, ATTR_SCHEME);
         final String host = ShortcutService.parseStringAttribute(parser, ATTR_HOST);
         final String port = ShortcutService.parseStringAttribute(parser, ATTR_PORT);
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 0ebe5961..2960bc9 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -24,6 +24,8 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,15 +38,12 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -225,7 +224,7 @@
      * Persist.
      */
     @Override
-    public void saveToXml(XmlSerializer out, boolean forBackup)
+    public void saveToXml(TypedXmlSerializer out, boolean forBackup)
             throws IOException {
         if (forBackup && !getPackageInfo().isBackupAllowed()) {
             // If an launcher app doesn't support backup&restore, then nothing to do.
@@ -278,11 +277,8 @@
         }
 
         try {
-            final BufferedInputStream bis = new BufferedInputStream(in);
-
             ShortcutLauncher ret = null;
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(bis, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -313,7 +309,7 @@
     /**
      * Load.
      */
-    public static ShortcutLauncher loadFromXml(XmlPullParser parser, ShortcutUser shortcutUser,
+    public static ShortcutLauncher loadFromXml(TypedXmlPullParser parser, ShortcutUser shortcutUser,
             int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException {
         final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
                 ATTR_PACKAGE_NAME);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index f6c60ad..0ac0c8d 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -35,6 +35,8 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -52,15 +54,12 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -1571,7 +1570,7 @@
     }
 
     @Override
-    public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+    public void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
         final int size = mShortcuts.size();
         final int shareTargetSize = mShareTargets.size();
@@ -1601,7 +1600,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    private void saveShortcut(XmlSerializer out, ShortcutInfo si, boolean forBackup,
+    private void saveShortcut(TypedXmlSerializer out, ShortcutInfo si, boolean forBackup,
             boolean appSupportsBackup)
             throws IOException, XmlPullParserException {
 
@@ -1734,11 +1733,8 @@
         }
 
         try {
-            final BufferedInputStream bis = new BufferedInputStream(in);
-
             ShortcutPackage ret = null;
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(bis, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -1767,7 +1763,7 @@
     }
 
     public static ShortcutPackage loadFromXml(ShortcutService s, ShortcutUser shortcutUser,
-            XmlPullParser parser, boolean fromBackup)
+            TypedXmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
 
         final String packageName = ShortcutService.parseStringAttribute(parser,
@@ -1814,7 +1810,7 @@
         return ret;
     }
 
-    private static ShortcutInfo parseShortcut(XmlPullParser parser, String packageName,
+    private static ShortcutInfo parseShortcut(TypedXmlPullParser parser, String packageName,
             @UserIdInt int userId, boolean fromBackup)
             throws IOException, XmlPullParserException {
         String id;
@@ -1948,7 +1944,7 @@
                 disabledReason, persons.toArray(new Person[persons.size()]), locusId);
     }
 
-    private static Intent parseIntent(XmlPullParser parser)
+    private static Intent parseIntent(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
 
         Intent intent = ShortcutService.parseIntentAttribute(parser,
@@ -1978,7 +1974,7 @@
         return intent;
     }
 
-    private static Person parsePerson(XmlPullParser parser)
+    private static Person parsePerson(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         CharSequence name = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_NAME);
         String uri = ShortcutService.parseStringAttribute(parser, ATTR_PERSON_URI);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 8c7871f..fce6610 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -23,6 +23,8 @@
 import android.content.pm.Signature;
 import android.content.pm.SigningInfo;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
@@ -32,7 +34,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -205,7 +206,7 @@
         mSigHashes = BackupUtils.hashSignatureArray(signatures);
     }
 
-    public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
+    public void saveToXml(ShortcutService s, TypedXmlSerializer out, boolean forBackup)
             throws IOException {
         if (forBackup && !mBackupAllowedInitialized) {
             s.wtf("Backup happened before mBackupAllowed is initialized.");
@@ -236,7 +237,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    public void loadFromXml(XmlPullParser parser, boolean fromBackup)
+    public void loadFromXml(TypedXmlPullParser parser, boolean fromBackup)
             throws IOException, XmlPullParserException {
         // Don't use the version code from the backup file.
         final long versionCode = ShortcutService.parseLongAttribute(parser, ATTR_VERSION,
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 801c6cb..829133c 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -20,16 +20,15 @@
 import android.content.pm.ShortcutInfo;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
 
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -146,7 +145,7 @@
 
     protected abstract void onRestored(int restoreBlockReason);
 
-    public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+    public abstract void saveToXml(@NonNull TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException;
 
     public void saveToFile(File path, boolean forBackup) {
@@ -154,18 +153,21 @@
         FileOutputStream os = null;
         try {
             os = file.startWrite();
-            final BufferedOutputStream bos = new BufferedOutputStream(os);
 
             // Write to XML
-            XmlSerializer itemOut = new FastXmlSerializer();
-            itemOut.setOutput(bos, StandardCharsets.UTF_8.name());
+            final TypedXmlSerializer itemOut;
+            if (forBackup) {
+                itemOut = Xml.newFastSerializer();
+                itemOut.setOutput(os, StandardCharsets.UTF_8.name());
+            } else {
+                itemOut = Xml.resolveSerializer(os);
+            }
             itemOut.startDocument(null, true);
 
             saveToXml(itemOut, forBackup);
 
             itemOut.endDocument();
 
-            bos.flush();
             os.flush();
             file.finishWrite(os);
         } catch (XmlPullParserException | IOException e) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2d77182..c68fe81 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -95,6 +95,8 @@
 import android.util.SparseIntArray;
 import android.util.SparseLongArray;
 import android.util.TypedValue;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.IWindowManager;
 
@@ -104,7 +106,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.StatLogger;
 import com.android.server.LocalServices;
@@ -119,10 +120,7 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -799,31 +797,31 @@
     // === Persisting ===
 
     @Nullable
-    static String parseStringAttribute(XmlPullParser parser, String attribute) {
+    static String parseStringAttribute(TypedXmlPullParser parser, String attribute) {
         return parser.getAttributeValue(null, attribute);
     }
 
-    static boolean parseBooleanAttribute(XmlPullParser parser, String attribute) {
+    static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute) {
         return parseLongAttribute(parser, attribute) == 1;
     }
 
-    static boolean parseBooleanAttribute(XmlPullParser parser, String attribute, boolean def) {
+    static boolean parseBooleanAttribute(TypedXmlPullParser parser, String attribute, boolean def) {
         return parseLongAttribute(parser, attribute, (def ? 1 : 0)) == 1;
     }
 
-    static int parseIntAttribute(XmlPullParser parser, String attribute) {
+    static int parseIntAttribute(TypedXmlPullParser parser, String attribute) {
         return (int) parseLongAttribute(parser, attribute);
     }
 
-    static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
+    static int parseIntAttribute(TypedXmlPullParser parser, String attribute, int def) {
         return (int) parseLongAttribute(parser, attribute, def);
     }
 
-    static long parseLongAttribute(XmlPullParser parser, String attribute) {
+    static long parseLongAttribute(TypedXmlPullParser parser, String attribute) {
         return parseLongAttribute(parser, attribute, 0);
     }
 
-    static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
+    static long parseLongAttribute(TypedXmlPullParser parser, String attribute, long def) {
         final String value = parseStringAttribute(parser, attribute);
         if (TextUtils.isEmpty(value)) {
             return def;
@@ -837,7 +835,7 @@
     }
 
     @Nullable
-    static ComponentName parseComponentNameAttribute(XmlPullParser parser, String attribute) {
+    static ComponentName parseComponentNameAttribute(TypedXmlPullParser parser, String attribute) {
         final String value = parseStringAttribute(parser, attribute);
         if (TextUtils.isEmpty(value)) {
             return null;
@@ -846,7 +844,7 @@
     }
 
     @Nullable
-    static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
+    static Intent parseIntentAttributeNoDefault(TypedXmlPullParser parser, String attribute) {
         final String value = parseStringAttribute(parser, attribute);
         Intent parsed = null;
         if (!TextUtils.isEmpty(value)) {
@@ -860,7 +858,7 @@
     }
 
     @Nullable
-    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+    static Intent parseIntentAttribute(TypedXmlPullParser parser, String attribute) {
         Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
         if (parsed == null) {
             // Default intent.
@@ -869,7 +867,7 @@
         return parsed;
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, String value) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, String value) throws IOException {
         if (TextUtils.isEmpty(value)) return;
 
         out.startTag(null, tag);
@@ -877,16 +875,17 @@
         out.endTag(null, tag);
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, long value) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, long value) throws IOException {
         writeTagValue(out, tag, Long.toString(value));
     }
 
-    static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
+    static void writeTagValue(TypedXmlSerializer out, String tag, ComponentName name)
+            throws IOException {
         if (name == null) return;
         writeTagValue(out, tag, name.flattenToString());
     }
 
-    static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
+    static void writeTagExtra(TypedXmlSerializer out, String tag, PersistableBundle bundle)
             throws IOException, XmlPullParserException {
         if (bundle == null) return;
 
@@ -895,17 +894,18 @@
         out.endTag(null, tag);
     }
 
-    static void writeAttr(XmlSerializer out, String name, CharSequence value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, CharSequence value)
+            throws IOException {
         if (TextUtils.isEmpty(value)) return;
 
         out.attribute(null, name, value.toString());
     }
 
-    static void writeAttr(XmlSerializer out, String name, long value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, long value) throws IOException {
         writeAttr(out, name, String.valueOf(value));
     }
 
-    static void writeAttr(XmlSerializer out, String name, boolean value) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, boolean value) throws IOException {
         if (value) {
             writeAttr(out, name, "1");
         } else {
@@ -913,12 +913,13 @@
         }
     }
 
-    static void writeAttr(XmlSerializer out, String name, ComponentName comp) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, ComponentName comp)
+            throws IOException {
         if (comp == null) return;
         writeAttr(out, name, comp.flattenToString());
     }
 
-    static void writeAttr(XmlSerializer out, String name, Intent intent) throws IOException {
+    static void writeAttr(TypedXmlSerializer out, String name, Intent intent) throws IOException {
         if (intent == null) return;
 
         writeAttr(out, name, intent.toUri(/* flags =*/ 0));
@@ -937,8 +938,7 @@
             outs = file.startWrite();
 
             // Write to XML
-            XmlSerializer out = new FastXmlSerializer();
-            out.setOutput(outs, StandardCharsets.UTF_8.name());
+            TypedXmlSerializer out = Xml.resolveSerializer(outs);
             out.startDocument(null, true);
             out.startTag(null, TAG_ROOT);
 
@@ -966,8 +966,7 @@
             Slog.d(TAG, "Loading from " + file.getBaseFile());
         }
         try (FileInputStream in = file.openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, StandardCharsets.UTF_8.name());
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
 
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -1043,18 +1042,20 @@
     private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
             boolean forBackup) throws IOException, XmlPullParserException {
 
-        final BufferedOutputStream bos = new BufferedOutputStream(os);
-
         // Write to XML
-        XmlSerializer out = new FastXmlSerializer();
-        out.setOutput(bos, StandardCharsets.UTF_8.name());
+        final TypedXmlSerializer out;
+        if (forBackup) {
+            out = Xml.newFastSerializer();
+            out.setOutput(os, StandardCharsets.UTF_8.name());
+        } else {
+            out = Xml.resolveSerializer(os);
+        }
         out.startDocument(null, true);
 
         getUserShortcutsLocked(userId).saveToXml(out, forBackup);
 
         out.endDocument();
 
-        bos.flush();
         os.flush();
     }
 
@@ -1098,11 +1099,14 @@
             boolean fromBackup) throws XmlPullParserException, IOException,
             InvalidFileFormatException {
 
-        final BufferedInputStream bis = new BufferedInputStream(is);
-
         ShortcutUser ret = null;
-        XmlPullParser parser = Xml.newPullParser();
-        parser.setInput(bis, StandardCharsets.UTF_8.name());
+        TypedXmlPullParser parser;
+        if (fromBackup) {
+            parser = Xml.newFastPullParser();
+            parser.setInput(is, StandardCharsets.UTF_8.name());
+        } else {
+            parser = Xml.resolvePullParser(is);
+        }
 
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 5c1d8fb..3e3aa67 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -26,6 +26,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -38,7 +40,6 @@
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.IOException;
@@ -329,7 +330,7 @@
         });
     }
 
-    public void saveToXml(XmlSerializer out, boolean forBackup)
+    public void saveToXml(TypedXmlSerializer out, boolean forBackup)
             throws IOException, XmlPullParserException {
         out.startTag(null, TAG_ROOT);
 
@@ -371,7 +372,7 @@
         out.endTag(null, TAG_ROOT);
     }
 
-    private void saveShortcutPackageItem(XmlSerializer out, ShortcutPackageItem spi,
+    private void saveShortcutPackageItem(TypedXmlSerializer out, ShortcutPackageItem spi,
             boolean forBackup) throws IOException, XmlPullParserException {
         if (forBackup) {
             if (spi.getPackageUserId() != spi.getOwnerUserId()) {
@@ -408,7 +409,7 @@
         return new File(path, fileName);
     }
 
-    public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
+    public static ShortcutUser loadFromXml(ShortcutService s, TypedXmlPullParser parser, int userId,
             boolean fromBackup) throws IOException, XmlPullParserException, InvalidFileFormatException {
         final ShortcutUser ret = new ShortcutUser(s, userId);
         boolean readShortcutItems = false;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c51e75c..cc814bcc 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -119,7 +119,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -175,6 +174,7 @@
     private static final String ATTR_CONVERTED_FROM_PRE_CREATED = "convertedFromPreCreated";
     private static final String ATTR_GUEST_TO_REMOVE = "guestToRemove";
     private static final String ATTR_USER_VERSION = "version";
+    private static final String ATTR_USER_TYPE_VERSION = "userTypeConfigVersion";
     private static final String ATTR_PROFILE_GROUP_ID = "profileGroupId";
     private static final String ATTR_PROFILE_BADGE = "profileBadge";
     private static final String ATTR_RESTRICTED_PROFILE_PARENT_ID = "restrictedProfileParentId";
@@ -422,6 +422,7 @@
     @GuardedBy("mPackagesLock")
     private int mNextSerialNumber;
     private int mUserVersion = 0;
+    private int mUserTypeVersion = 0;
 
     private IAppOpsService mAppOpsService;
 
@@ -2565,6 +2566,8 @@
                         parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
                 mUserVersion =
                         parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
+                mUserTypeVersion =
+                        parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
             }
 
             // Pre-O global user restriction were stored as a single bundle (as opposed to per-user
@@ -2627,7 +2630,7 @@
      */
     @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     private void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions) {
-        upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion);
+        upgradeIfNecessaryLP(oldGlobalUserRestrictions, mUserVersion, mUserTypeVersion);
     }
 
     /**
@@ -2636,9 +2639,11 @@
      */
     @GuardedBy({"mRestrictionsLock", "mPackagesLock"})
     @VisibleForTesting
-    void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion) {
+    void upgradeIfNecessaryLP(Bundle oldGlobalUserRestrictions, int userVersion,
+            int userTypeVersion) {
         Set<Integer> userIdsToWrite = new ArraySet<>();
         final int originalVersion = mUserVersion;
+        final int originalUserTypeVersion = mUserTypeVersion;
         if (userVersion < 1) {
             // Assign a proper name for the owner, if not initialized correctly before
             UserData userData = getUserDataNoChecks(UserHandle.USER_SYSTEM);
@@ -2771,13 +2776,24 @@
             userVersion = 9;
         }
 
+        // Done with userVersion changes, moving on to deal with userTypeVersion upgrades
+        // Upgrade from previous user type to a new user type
+        final int newUserTypeVersion = UserTypeFactory.getUserTypeVersion();
+        if (newUserTypeVersion > userTypeVersion) {
+            synchronized (mUsersLock) {
+                upgradeUserTypesLU(UserTypeFactory.getUserTypeUpgrades(), mUserTypes,
+                        userTypeVersion, userIdsToWrite);
+            }
+        }
+
         if (userVersion < USER_VERSION) {
             Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                     + USER_VERSION);
         } else {
             mUserVersion = userVersion;
+            mUserTypeVersion = newUserTypeVersion;
 
-            if (originalVersion < mUserVersion) {
+            if (originalVersion < mUserVersion || originalUserTypeVersion < mUserTypeVersion) {
                 for (int userId : userIdsToWrite) {
                     UserData userData = getUserDataNoChecks(userId);
                     if (userData != null) {
@@ -2801,6 +2817,7 @@
         UserData userData = putUserInfo(system);
         mNextSerialNumber = MIN_USER_ID;
         mUserVersion = USER_VERSION;
+        mUserTypeVersion = UserTypeFactory.getUserTypeVersion();
 
         Bundle restrictions = new Bundle();
         try {
@@ -2991,6 +3008,7 @@
             serializer.startTag(null, TAG_USERS);
             serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
             serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
+            serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
 
             serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
             synchronized (mGuestRestrictions) {
@@ -4957,6 +4975,7 @@
 
         // Dump UserTypes
         pw.println();
+        pw.println("User types version: " + mUserTypeVersion);
         pw.println("User types (" + mUserTypes.size() + " types):");
         for (int i = 0; i < mUserTypes.size(); i++) {
             pw.println("    " + mUserTypes.keyAt(i) + ": ");
@@ -5447,6 +5466,9 @@
      * Returns the maximum number of users allowed for the given userTypeDetails per parent user.
      * This is applicable for user types that are {@link UserTypeDetails#isProfile()}.
      * If there is no maximum, {@link UserTypeDetails#UNLIMITED_NUMBER_OF_USERS} is returned.
+     * Under certain circumstances (such as after a change-user-type) the max value can actually
+     * be exceeded: this is allowed in order to keep the device in a usable state.
+     * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU}
      */
     private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) {
         final int defaultMax = userTypeDetails.getMaxAllowedPerParent();
@@ -5534,4 +5556,98 @@
         }
         return mDevicePolicyManagerInternal;
     }
+
+    @GuardedBy("mUsersLock")
+    @VisibleForTesting
+    void upgradeUserTypesLU(@NonNull List<UserTypeFactory.UserTypeUpgrade> upgradeOps,
+            @NonNull ArrayMap<String, UserTypeDetails> userTypes,
+            final int formerUserTypeVersion,
+            @NonNull Set<Integer> userIdsToWrite) {
+        for (UserTypeFactory.UserTypeUpgrade userTypeUpgrade : upgradeOps) {
+            if (DBG) {
+                Slog.i(LOG_TAG, "Upgrade: " + userTypeUpgrade.getFromType() + " to: "
+                        + userTypeUpgrade.getToType() + " maxVersion: "
+                        + userTypeUpgrade.getUpToVersion());
+            }
+
+            // upgrade user type if version up to getUpToVersion()
+            if (formerUserTypeVersion <= userTypeUpgrade.getUpToVersion()) {
+                for (int i = 0; i < mUsers.size(); i++) {
+                    UserData userData = mUsers.valueAt(i);
+                    if (userTypeUpgrade.getFromType().equals(userData.info.userType)) {
+                        final UserTypeDetails newUserType = userTypes.get(
+                                userTypeUpgrade.getToType());
+
+                        if (newUserType == null) {
+                            throw new IllegalStateException(
+                                    "Upgrade destination user type not defined: "
+                                            + userTypeUpgrade.getToType());
+                        }
+
+                        upgradeProfileToTypeLU(userData.info, newUserType);
+                        userIdsToWrite.add(userData.info.id);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Changes the user type of a profile to a new user type.
+     * @param userInfo    The user to be updated.
+     * @param newUserType The new user type.
+     */
+    @GuardedBy("mUsersLock")
+    @VisibleForTesting
+    void upgradeProfileToTypeLU(@NonNull UserInfo userInfo, @NonNull UserTypeDetails newUserType) {
+        Slog.i(LOG_TAG, "Upgrading user " + userInfo.id
+                + " from " + userInfo.userType
+                + " to " + newUserType.getName());
+
+        if (!userInfo.isProfile()) {
+            throw new IllegalStateException(
+                    "Can only upgrade profile types. " + userInfo.userType
+                            + " is not a profile type.");
+        }
+
+        // Exceeded maximum profiles for parent user: log error, but allow upgrade
+        if (!canAddMoreProfilesToUser(newUserType.getName(), userInfo.profileGroupId, false)) {
+            Slog.w(LOG_TAG,
+                    "Exceeded maximum profiles of type " + newUserType.getName() + " for user "
+                            + userInfo.id + ". Maximum allowed= "
+                            + newUserType.getMaxAllowedPerParent());
+        }
+
+        final UserTypeDetails oldUserType = mUserTypes.get(userInfo.userType);
+        final int oldFlags;
+        if (oldUserType != null) {
+            oldFlags = oldUserType.getDefaultUserInfoFlags();
+        } else {
+            // if oldUserType is missing from config_user_types.xml -> can only assume FLAG_PROFILE
+            oldFlags = UserInfo.FLAG_PROFILE;
+        }
+
+        //convert userData to newUserType
+        userInfo.userType = newUserType.getName();
+        // remove old default flags and add newUserType's default flags
+        userInfo.flags = newUserType.getDefaultUserInfoFlags() | (userInfo.flags ^ oldFlags);
+
+        // merge existing base restrictions with the new type's default restrictions
+        synchronized (mRestrictionsLock) {
+            if (!UserRestrictionsUtils.isEmpty(newUserType.getDefaultRestrictions())) {
+                final Bundle newRestrictions = UserRestrictionsUtils.clone(
+                        mBaseUserRestrictions.getRestrictions(userInfo.id));
+                UserRestrictionsUtils.merge(newRestrictions,
+                        newUserType.getDefaultRestrictions());
+                updateUserRestrictionsInternalLR(newRestrictions, userInfo.id);
+                if (DBG) {
+                    Slog.i(LOG_TAG, "Updated user " + userInfo.id
+                            + " restrictions to " + newRestrictions);
+                }
+            }
+        }
+
+        // re-compute badge index
+        userInfo.profileBadge = getFreeProfileBadgeLU(userInfo.profileGroupId, userInfo.userType);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index d0c3a95..0ac3030 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -34,7 +34,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.Global;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
@@ -670,15 +669,6 @@
                                 Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, 0, userId);
                     }
                     break;
-                case UserManager.DISALLOW_CONFIG_LOCATION:
-                    // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global
-                    // kill switch.
-                    if (newValue) {
-                        android.provider.Settings.Global.putString(
-                                context.getContentResolver(),
-                                Global.LOCATION_GLOBAL_KILL_SWITCH, "0");
-                    }
-                    break;
                 case UserManager.DISALLOW_APPS_CONTROL:
                     // Intentional fall-through
                 case UserManager.DISALLOW_UNINSTALL_APPS:
@@ -774,14 +764,6 @@
                 restriction = UserManager.DISALLOW_AMBIENT_DISPLAY;
                 break;
 
-            case android.provider.Settings.Global.LOCATION_GLOBAL_KILL_SWITCH:
-                if ("0".equals(value)) {
-                    return false;
-                }
-                restriction = UserManager.DISALLOW_CONFIG_LOCATION;
-                checkAllUser = true;
-                break;
-
             case android.provider.Settings.System.SCREEN_BRIGHTNESS:
             case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT:
             case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE:
diff --git a/services/core/java/com/android/server/pm/UserTypeDetails.java b/services/core/java/com/android/server/pm/UserTypeDetails.java
index d840e5d..5fa46b9 100644
--- a/services/core/java/com/android/server/pm/UserTypeDetails.java
+++ b/services/core/java/com/android/server/pm/UserTypeDetails.java
@@ -174,6 +174,9 @@
     /**
      * Returns the maximum number of this user type allowed per parent (for user types, like
      * profiles, that have parents).
+     * Under certain circumstances (such as after a change-user-type) the max value can actually
+     * be exceeded: this is allowed in order to keep the device in a usable state.
+     * An error is logged in {@link UserManagerService#upgradeProfileToTypeLU}
      * <p>Returns {@link #UNLIMITED_NUMBER_OF_USERS} to indicate that there is no hard limit.
      */
     public int getMaxAllowedPerParent() {
diff --git a/services/core/java/com/android/server/pm/UserTypeFactory.java b/services/core/java/com/android/server/pm/UserTypeFactory.java
index ba8a2ba..1d3aecd 100644
--- a/services/core/java/com/android/server/pm/UserTypeFactory.java
+++ b/services/core/java/com/android/server/pm/UserTypeFactory.java
@@ -49,6 +49,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Consumer;
 
 /**
@@ -73,14 +74,7 @@
      * @return mapping from the name of each user type to its {@link UserTypeDetails} object
      */
     public static ArrayMap<String, UserTypeDetails> getUserTypes() {
-        final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
-        builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged());
-        builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem());
-        builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary());
-        builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest());
-        builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo());
-        builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted());
-        builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
+        final ArrayMap<String, UserTypeDetails.Builder> builders = getDefaultBuilders();
 
         try (XmlResourceParser parser =
                      Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
@@ -94,6 +88,20 @@
         return types;
     }
 
+    private static ArrayMap<String, UserTypeDetails.Builder> getDefaultBuilders() {
+        final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
+
+        builders.put(USER_TYPE_PROFILE_MANAGED, getDefaultTypeProfileManaged());
+        builders.put(USER_TYPE_FULL_SYSTEM, getDefaultTypeFullSystem());
+        builders.put(USER_TYPE_FULL_SECONDARY, getDefaultTypeFullSecondary());
+        builders.put(USER_TYPE_FULL_GUEST, getDefaultTypeFullGuest());
+        builders.put(USER_TYPE_FULL_DEMO, getDefaultTypeFullDemo());
+        builders.put(USER_TYPE_FULL_RESTRICTED, getDefaultTypeFullRestricted());
+        builders.put(USER_TYPE_SYSTEM_HEADLESS, getDefaultTypeSystemHeadless());
+
+        return builders;
+    }
+
     /**
      * Returns the Builder for the default {@link UserManager#USER_TYPE_PROFILE_MANAGED}
      * configuration.
@@ -232,6 +240,10 @@
                     isProfile = true;
                 } else if ("full-type".equals(elementName)) {
                     isProfile = false;
+                } else if ("change-user-type".equals(elementName)) {
+                    // parsed in parseUserUpgrades
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
                 } else {
                     Slog.w(LOG_TAG, "Skipping unknown element " + elementName + " in "
                                 + parser.getPositionDescription());
@@ -291,7 +303,8 @@
                 while (XmlUtils.nextElementWithin(parser, depth)) {
                     final String childName = parser.getName();
                     if ("default-restrictions".equals(childName)) {
-                        final Bundle restrictions = UserRestrictionsUtils.readRestrictions(parser);
+                        final Bundle restrictions = UserRestrictionsUtils
+                                .readRestrictions(XmlUtils.makeTyped(parser));
                         builder.setDefaultRestrictions(restrictions);
                     } else if (isProfile && "badge-labels".equals(childName)) {
                         setResAttributeArray(parser, builder::setBadgeLabels);
@@ -387,4 +400,132 @@
         }
         fcn.accept(result);
     }
+
+    /**
+     * Returns the user type version of the config XML file.
+     * @return user type version defined in XML file, 0 if none.
+     */
+    public static int getUserTypeVersion() {
+        try (XmlResourceParser parser =
+                     Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
+            return getUserTypeVersion(parser);
+        }
+    }
+
+    @VisibleForTesting
+    static int getUserTypeVersion(XmlResourceParser parser) {
+        int version = 0;
+
+        try {
+            XmlUtils.beginDocument(parser, "user-types");
+            String versionValue = parser.getAttributeValue(null, "version");
+            if (versionValue != null) {
+                try {
+                    version = Integer.parseInt(versionValue);
+                } catch (NumberFormatException e) {
+                    Slog.e(LOG_TAG, "Cannot parse value of '" + versionValue + "' for version in "
+                            + parser.getPositionDescription(), e);
+                    throw e;
+                }
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(LOG_TAG, "Cannot read user type configuration file.", e);
+        }
+
+        return version;
+    }
+
+    /**
+     * Obtains the user type upgrades for this device.
+     * @return The list of user type upgrades.
+     */
+    public static List<UserTypeUpgrade> getUserTypeUpgrades() {
+        final List<UserTypeUpgrade> userUpgrades;
+        try (XmlResourceParser parser =
+                     Resources.getSystem().getXml(com.android.internal.R.xml.config_user_types)) {
+            userUpgrades = parseUserUpgrades(getDefaultBuilders(), parser);
+        }
+        return userUpgrades;
+    }
+
+    @VisibleForTesting
+    static List<UserTypeUpgrade> parseUserUpgrades(
+            ArrayMap<String, UserTypeDetails.Builder> builders, XmlResourceParser parser) {
+        final List<UserTypeUpgrade> userUpgrades = new ArrayList<>();
+
+        try {
+            XmlUtils.beginDocument(parser, "user-types");
+            for (XmlUtils.nextElement(parser);
+                    parser.getEventType() != XmlResourceParser.END_DOCUMENT;
+                    XmlUtils.nextElement(parser)) {
+                final String elementName = parser.getName();
+                if ("change-user-type".equals(elementName)) {
+                    final String fromType = parser.getAttributeValue(null, "from");
+                    final String toType = parser.getAttributeValue(null, "to");
+                    // Check that the base type doesn't change.
+                    // Currently, only the base type of PROFILE is supported.
+                    validateUserTypeIsProfile(fromType, builders);
+                    validateUserTypeIsProfile(toType, builders);
+
+                    final int maxVersionToConvert;
+                    try {
+                        maxVersionToConvert = Integer.parseInt(
+                                parser.getAttributeValue(null, "whenVersionLeq"));
+                    } catch (NumberFormatException e) {
+                        Slog.e(LOG_TAG, "Cannot parse value of whenVersionLeq in "
+                                + parser.getPositionDescription(), e);
+                        throw e;
+                    }
+
+                    UserTypeUpgrade userTypeUpgrade = new UserTypeUpgrade(fromType, toType,
+                            maxVersionToConvert);
+                    userUpgrades.add(userTypeUpgrade);
+                    continue;
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+            }
+        } catch (XmlPullParserException | IOException e) {
+            Slog.w(LOG_TAG, "Cannot read user type configuration file.", e);
+        }
+
+        return userUpgrades;
+    }
+
+    private static void validateUserTypeIsProfile(String userType,
+            ArrayMap<String, UserTypeDetails.Builder> builders) {
+        UserTypeDetails.Builder builder = builders.get(userType);
+        if (builder != null && builder.getBaseType() != FLAG_PROFILE) {
+            throw new IllegalArgumentException("Illegal upgrade of user type " + userType
+                    + " : Can only upgrade profiles user types");
+        }
+    }
+
+    /**
+     * Contains details required for an upgrade operation for {@link UserTypeDetails};
+     */
+    public static class UserTypeUpgrade {
+        private final String mFromType;
+        private final String mToType;
+        private final int mUpToVersion;
+
+        public UserTypeUpgrade(String fromType, String toType, int upToVersion) {
+            mFromType = fromType;
+            mToType = toType;
+            mUpToVersion = upToVersion;
+        }
+
+        public String getFromType() {
+            return mFromType;
+        }
+
+        public String getToType() {
+            return mToType;
+        }
+
+        public int getUpToVersion() {
+            return mUpToVersion;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index e5c93a3..1656472 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -63,6 +63,7 @@
 import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
+import com.android.internal.R;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.LocalServices;
@@ -72,7 +73,6 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -766,9 +766,14 @@
             grantSystemFixedPermissionsToSystemPackage(pm, wearPackage, userId, PHONE_PERMISSIONS);
 
             // Fitness tracking on watches
-            grantPermissionsToSystemPackage(pm,
+            if (mContext.getResources().getBoolean(R.bool.config_trackerAppNeedsPermissions)) {
+                Log.d(TAG, "Wear: Skipping permission grant for Default fitness tracker app : "
+                        + wearPackage);
+            } else {
+                grantPermissionsToSystemPackage(pm,
                     getDefaultSystemHandlerActivityPackage(pm, ACTION_TRACK, userId), userId,
                     SENSORS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS);
+            }
         }
 
         // Print Spooler
@@ -1408,11 +1413,8 @@
                 Slog.w(TAG, "Default permissions file " + file + " cannot be read");
                 continue;
             }
-            try (
-                InputStream str = new BufferedInputStream(new FileInputStream(file))
-            ) {
-                TypedXmlPullParser parser = Xml.newFastPullParser();
-                parser.setInput(str, null);
+            try (InputStream str = new FileInputStream(file)) {
+                TypedXmlPullParser parser = Xml.resolvePullParser(str);
                 parse(pm, parser, grantExceptions);
             } catch (XmlPullParserException | IOException e) {
                 Slog.w(TAG, "Error reading default permissions file " + file, e);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index fb1ed2f..52bb3d7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -205,6 +205,9 @@
     private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET
             | FLAG_PERMISSION_USER_FIXED;
 
+    /** All storage permissions */
+    private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>();
+
     /** If the permission of the value is granted, so is the key */
     private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
 
@@ -213,6 +216,9 @@
                 Manifest.permission.ACCESS_FINE_LOCATION);
         FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS,
                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+        STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+        STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
     }
 
     /** Lock to protect internal data access */
@@ -1243,50 +1249,56 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            synchronized (mLock) {
-                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
-                if (uidState == null) {
-                    Slog.e(TAG, "Missing permissions state for " + packageName + " and user "
-                            + userId);
-                    return null;
-                }
-
-                int queryFlags = 0;
-                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
-                    queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-                }
-                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
-                    queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-                }
-                if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
-                    queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-                }
-                if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
-                    queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                }
-
-                ArrayList<String> whitelistedPermissions = null;
-
-                final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
-                for (int i = 0; i < permissionCount; i++) {
-                    final String permissionName = pkg.getRequestedPermissions().get(i);
-                    final int currentFlags =
-                            uidState.getPermissionFlags(permissionName);
-                    if ((currentFlags & queryFlags) != 0) {
-                        if (whitelistedPermissions == null) {
-                            whitelistedPermissions = new ArrayList<>();
-                        }
-                        whitelistedPermissions.add(permissionName);
-                    }
-                }
-
-                return whitelistedPermissions;
-            }
+            return getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
+    @Nullable
+    private List<String> getAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg,
+            @PermissionWhitelistFlags int flags, @UserIdInt int userId) {
+        synchronized (mLock) {
+            final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+            if (uidState == null) {
+                Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user "
+                        + userId);
+                return null;
+            }
+
+            int queryFlags = 0;
+            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
+                queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+            }
+            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
+                queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+            }
+            if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
+                queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+            }
+            if ((flags & PackageManager.FLAG_PERMISSION_ALLOWLIST_ROLE) != 0) {
+                queryFlags |=  FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+            }
+
+            ArrayList<String> allowlistedPermissions = null;
+
+            final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions());
+            for (int i = 0; i < permissionCount; i++) {
+                final String permissionName = pkg.getRequestedPermissions().get(i);
+                final int currentFlags =
+                        uidState.getPermissionFlags(permissionName);
+                if ((currentFlags & queryFlags) != 0) {
+                    if (allowlistedPermissions == null) {
+                        allowlistedPermissions = new ArrayList<>();
+                    }
+                    allowlistedPermissions.add(permissionName);
+                }
+            }
+
+            return allowlistedPermissions;
+        }
+    }
+
     @Override
     public boolean addWhitelistedRestrictedPermission(@NonNull String packageName,
             @NonNull String permName, @PermissionWhitelistFlags int flags,
@@ -1429,8 +1441,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags,
-                    new int[] { userId });
+            setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -1453,13 +1464,6 @@
         return setAutoRevokeExemptedInternal(pkg, whitelisted, userId);
     }
 
-    private void setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted,
-            @NonNull int[] userIds) {
-        for (final int userId : userIds) {
-            setAutoRevokeExemptedInternal(pkg, exempted, userId);
-        }
-    }
-
     private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted,
             @UserIdInt int userId) {
         final int packageUid = UserHandle.getUid(userId, pkg.getUid());
@@ -2358,6 +2362,48 @@
     }
 
     /**
+     * If the app is updated, and has scoped storage permissions, then it is possible that the
+     * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+     * @param newPackage The new package that was installed
+     * @param oldPackage The old package that was updated
+     */
+    private void revokeStoragePermissionsIfScopeExpandedInternal(
+            @NonNull AndroidPackage newPackage,
+            @NonNull AndroidPackage oldPackage) {
+        boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+                && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q;
+        boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q
+                && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q;
+        boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage()
+                && newPackage.isRequestLegacyExternalStorage();
+
+        if (!newlyRequestsLegacy && !downgradedSdk) {
+            return;
+        }
+
+        final int callingUid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(newPackage.getUid());
+        int numRequestedPermissions = newPackage.getRequestedPermissions().size();
+        for (int i = 0; i < numRequestedPermissions; i++) {
+            PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i),
+                    newPackage.getPackageName(), 0);
+            if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+                continue;
+            }
+
+            EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
+                    "Revoking permission " + permInfo.name + " from package "
+                            + newPackage.getPackageName() + " as either the sdk downgraded "
+                            + downgradedSdk + " or newly requested legacy full storage "
+                            + newlyRequestsLegacy);
+
+            revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(),
+                    false, callingUid, userId, null, mDefaultPermissionCallback);
+        }
+
+    }
+
+    /**
      * We might auto-grant permissions if any permission of the group is already granted. Hence if
      * the group of a granted permission changes we need to revoke it to avoid having permissions of
      * the new group auto-granted.
@@ -3775,13 +3821,6 @@
     }
 
     private void grantRequestedRuntimePermissionsInternal(@NonNull AndroidPackage pkg,
-            @Nullable List<String> permissions, @NonNull int[] userIds) {
-        for (int userId : userIds) {
-            grantRequestedRuntimePermissionsForUser(pkg, permissions, userId);
-        }
-    }
-
-    private void grantRequestedRuntimePermissionsForUser(@NonNull AndroidPackage pkg,
             @Nullable List<String> permissions, int userId) {
         final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                 | PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -3828,25 +3867,136 @@
 
     private void setAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg,
             @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags,
-            @UserIdInt int[] userIds) {
-        SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
+            @UserIdInt int userId) {
+        ArraySet<String> oldGrantedRestrictedPermissions = null;
         boolean updatePermissions = false;
         final int permissionCount = pkg.getRequestedPermissions().size();
         final int myUid = Process.myUid();
 
-        for (int i = 0; i < userIds.length; i++) {
-            int userId = userIds[i];
+        for (int j = 0; j < permissionCount; j++) {
+            final String permissionName = pkg.getRequestedPermissions().get(j);
 
-            for (int j = 0; j < permissionCount; j++) {
-                final String permissionName = pkg.getRequestedPermissions().get(j);
+            final boolean isGranted;
+            synchronized (mLock) {
+                final Permission bp = mRegistry.getPermission(permissionName);
+                if (bp == null || !bp.isHardOrSoftRestricted()) {
+                    continue;
+                }
 
+                final UidPermissionState uidState = getUidStateLocked(pkg, userId);
+                if (uidState == null) {
+                    Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
+                            + " and user " + userId);
+                    continue;
+                }
+                isGranted = uidState.isPermissionGranted(permissionName);
+            }
+
+            if (isGranted) {
+                if (oldGrantedRestrictedPermissions == null) {
+                    oldGrantedRestrictedPermissions = new ArraySet<>();
+                }
+                oldGrantedRestrictedPermissions.add(permissionName);
+            }
+
+            final int oldFlags = getPermissionFlagsInternal(permissionName,
+                    pkg.getPackageName(), myUid, userId);
+
+            int newFlags = oldFlags;
+            int mask = 0;
+            int whitelistFlagsCopy = allowlistFlags;
+            while (whitelistFlagsCopy != 0) {
+                final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
+                whitelistFlagsCopy &= ~flag;
+                switch (flag) {
+                    case FLAG_PERMISSION_WHITELIST_SYSTEM: {
+                        mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+                        if (permissions != null && permissions.contains(permissionName)) {
+                            newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+                        } else {
+                            newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+                        }
+                    }
+                    break;
+                    case FLAG_PERMISSION_WHITELIST_UPGRADE: {
+                        mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+                        if (permissions != null && permissions.contains(permissionName)) {
+                            newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+                        } else {
+                            newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+                        }
+                    }
+                    break;
+                    case FLAG_PERMISSION_WHITELIST_INSTALLER: {
+                        mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+                        if (permissions != null && permissions.contains(permissionName)) {
+                            newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+                        } else {
+                            newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+                        }
+                    }
+                    break;
+                    case FLAG_PERMISSION_ALLOWLIST_ROLE: {
+                        mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                        if (permissions != null && permissions.contains(permissionName)) {
+                            newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                        } else {
+                            newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
+                        }
+                    }
+                    break;
+                }
+            }
+
+            if (oldFlags == newFlags) {
+                continue;
+            }
+
+            updatePermissions = true;
+
+            final boolean wasWhitelisted = (oldFlags
+                    & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+            final boolean isWhitelisted = (newFlags
+                    & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+
+            // If the permission is policy fixed as granted but it is no longer
+            // on any of the whitelists we need to clear the policy fixed flag
+            // as whitelisting trumps policy i.e. policy cannot grant a non
+            // grantable permission.
+            if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+                if (!isWhitelisted && isGranted) {
+                    mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+                    newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+                }
+            }
+
+            // If we are whitelisting an app that does not support runtime permissions
+            // we need to make sure it goes through the permission review UI at launch.
+            if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
+                    && !wasWhitelisted && isWhitelisted) {
+                mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+                newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+            }
+
+            updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
+                    myUid, userId, false, null /*callback*/);
+        }
+
+        if (updatePermissions) {
+            // Update permission of this app to take into account the new whitelist state.
+            restorePermissionState(pkg, false, pkg.getPackageName(), mDefaultPermissionCallback);
+
+            // If this resulted in losing a permission we need to kill the app.
+            if (oldGrantedRestrictedPermissions == null) {
+                return;
+            }
+
+            final int oldGrantedCount = oldGrantedRestrictedPermissions.size();
+            for (int j = 0; j < oldGrantedCount; j++) {
+                final String permissionName = oldGrantedRestrictedPermissions.valueAt(j);
+                // Sometimes we create a new permission state instance during update.
                 final boolean isGranted;
                 synchronized (mLock) {
-                    final Permission bp = mRegistry.getPermission(permissionName);
-                    if (bp == null || !bp.isHardOrSoftRestricted()) {
-                        continue;
-                    }
-
                     final UidPermissionState uidState = getUidStateLocked(pkg, userId);
                     if (uidState == null) {
                         Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
@@ -3855,128 +4005,9 @@
                     }
                     isGranted = uidState.isPermissionGranted(permissionName);
                 }
-
-                if (isGranted) {
-                    if (oldGrantedRestrictedPermissions.get(userId) == null) {
-                        oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
-                    }
-                    oldGrantedRestrictedPermissions.get(userId).add(permissionName);
-                }
-
-                final int oldFlags = getPermissionFlagsInternal(permissionName,
-                        pkg.getPackageName(), myUid, userId);
-
-                int newFlags = oldFlags;
-                int mask = 0;
-                int whitelistFlagsCopy = allowlistFlags;
-                while (whitelistFlagsCopy != 0) {
-                    final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
-                    whitelistFlagsCopy &= ~flag;
-                    switch (flag) {
-                        case FLAG_PERMISSION_WHITELIST_SYSTEM: {
-                            mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-                            if (permissions != null && permissions.contains(permissionName)) {
-                                newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-                            } else {
-                                newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
-                            }
-                        }
-                        break;
-                        case FLAG_PERMISSION_WHITELIST_UPGRADE: {
-                            mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-                            if (permissions != null && permissions.contains(permissionName)) {
-                                newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-                            } else {
-                                newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
-                            }
-                        }
-                        break;
-                        case FLAG_PERMISSION_WHITELIST_INSTALLER: {
-                            mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-                            if (permissions != null && permissions.contains(permissionName)) {
-                                newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-                            } else {
-                                newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
-                            }
-                        }
-                        break;
-                        case FLAG_PERMISSION_ALLOWLIST_ROLE: {
-                            mask |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                            if (permissions != null && permissions.contains(permissionName)) {
-                                newFlags |= FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                            } else {
-                                newFlags &= ~FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT;
-                            }
-                        }
-                        break;
-                    }
-                }
-
-                if (oldFlags == newFlags) {
-                    continue;
-                }
-
-                updatePermissions = true;
-
-                final boolean wasWhitelisted = (oldFlags
-                        & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
-                final boolean isWhitelisted = (newFlags
-                        & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
-
-                // If the permission is policy fixed as granted but it is no longer
-                // on any of the whitelists we need to clear the policy fixed flag
-                // as whitelisting trumps policy i.e. policy cannot grant a non
-                // grantable permission.
-                if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
-                    if (!isWhitelisted && isGranted) {
-                        mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-                        newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-                    }
-                }
-
-                // If we are whitelisting an app that does not support runtime permissions
-                // we need to make sure it goes through the permission review UI at launch.
-                if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
-                        && !wasWhitelisted && isWhitelisted) {
-                    mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-                    newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-                }
-
-                updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
-                        myUid, userId, false, null /*callback*/);
-            }
-        }
-
-        if (updatePermissions) {
-            // Update permission of this app to take into account the new whitelist state.
-            restorePermissionState(pkg, false, pkg.getPackageName(), mDefaultPermissionCallback);
-
-            // If this resulted in losing a permission we need to kill the app.
-            for (int i = 0; i < userIds.length; i++) {
-                int userId = userIds[i];
-                ArraySet<String> oldPermsForUser = oldGrantedRestrictedPermissions.get(userId);
-                if (oldPermsForUser == null) {
-                    continue;
-                }
-
-                final int oldGrantedCount = oldPermsForUser.size();
-                for (int j = 0; j < oldGrantedCount; j++) {
-                    final String permissionName = oldPermsForUser.valueAt(j);
-                    // Sometimes we create a new permission state instance during update.
-                    final boolean isGranted;
-                    synchronized (mLock) {
-                        final UidPermissionState uidState = getUidStateLocked(pkg, userId);
-                        if (uidState == null) {
-                            Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()
-                                    + " and user " + userId);
-                            continue;
-                        }
-                        isGranted = uidState.isPermissionGranted(permissionName);
-                    }
-                    if (!isGranted) {
-                        mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null);
-                        break;
-                    }
+                if (!isGranted) {
+                    mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null);
+                    break;
                 }
             }
         }
@@ -4884,6 +4915,7 @@
             AsyncTask.execute(() -> {
                 if (hasOldPkg) {
                     revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
+                    revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg);
                 }
                 if (hasPermissionDefinitionChanges) {
                     revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
@@ -4914,6 +4946,34 @@
         return true;
     }
 
+    private void onPackageInstalledInternal(@NonNull AndroidPackage pkg,
+            @NonNull List<String> grantedPermissions,
+            @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode,
+            @UserIdInt int userId) {
+        addAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions,
+                FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
+        if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED
+                || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) {
+            setAutoRevokeExemptedInternal(pkg,
+                    autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId);
+        }
+        grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userId);
+    }
+
+    private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg,
+            @NonNull List<String> allowlistedRestrictedPermissions,
+            @PermissionWhitelistFlags int flags, @UserIdInt int userId) {
+        List<String> permissions = getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId);
+        if (permissions != null) {
+            ArraySet<String> permissionSet = new ArraySet<>(permissions);
+            permissionSet.addAll(allowlistedRestrictedPermissions);
+            permissions = new ArrayList<>(permissionSet);
+        } else {
+            permissions = allowlistedRestrictedPermissions;
+        }
+        setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId);
+    }
+
     private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) {
         removeAllPermissionsInternal(pkg);
     }
@@ -5080,28 +5140,6 @@
             return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName);
         }
         @Override
-        public void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg,
-                @Nullable List<String> permissions, @NonNull int[] userIds) {
-            Objects.requireNonNull(pkg, "pkg");
-            Objects.requireNonNull(userIds, "userIds");
-            grantRequestedRuntimePermissionsInternal(pkg, permissions, userIds);
-        }
-        @Override
-        public void setAllowlistedRestrictedPermissions(@NonNull AndroidPackage pkg,
-                @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags,
-                @NonNull int[] userIds) {
-            Objects.requireNonNull(pkg, "pkg");
-            Objects.requireNonNull(userIds, "userIds");
-            setAllowlistedRestrictedPermissionsInternal(pkg, permissions, allowlistFlags, userIds);
-        }
-        @Override
-        public void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted,
-                @NonNull int[] userIds) {
-            Objects.requireNonNull(pkg, "pkg");
-            Objects.requireNonNull(userIds, "userIds");
-            setAutoRevokeExemptedInternal(pkg, exempted, userIds);
-        }
-        @Override
         public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
             PermissionManagerService.this
                     .updatePermissions(packageName, pkg, mDefaultPermissionCallback);
@@ -5372,6 +5410,20 @@
         }
 
         @Override
+        public void onPackageInstalled(@NonNull AndroidPackage pkg,
+                @NonNull List<String> grantedPermissions,
+                @NonNull List<String> allowlistedRestrictedPermissions,
+                int autoRevokePermissionsMode, @UserIdInt int userId) {
+            Objects.requireNonNull(pkg, "pkg");
+            Objects.requireNonNull(grantedPermissions, "grantedPermissions");
+            Objects.requireNonNull(allowlistedRestrictedPermissions,
+                    "allowlistedRestrictedPermissions");
+            Preconditions.checkArgumentNonNegative(userId, "userId");
+            onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions,
+                    autoRevokePermissionsMode, userId);
+        }
+
+        @Override
         public void onPackageRemoved(@NonNull AndroidPackage pkg) {
             Objects.requireNonNull(pkg);
             onPackageRemovedInternal(pkg);
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 1becbed..457fe36 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -20,7 +20,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
-import android.content.pm.PackageManager;
 import android.content.pm.PermissionInfo;
 import android.permission.PermissionManagerInternal;
 
@@ -190,42 +189,6 @@
             @UserIdInt int userId);
 
     /**
-     * Grant the requested runtime permissions for a package, or an explicit subset of them.
-     *
-     * @param pkg the package
-     * @param permissions the names of the subset of permissions to be granted, or {@code null} for
-     *                    granting all the requested permissions
-     * @param userIds the user IDs
-     */
-    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-    public abstract void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg,
-            @Nullable List<String> permissions, @NonNull int[] userIds);
-
-    /**
-     * Set the allowlisted restricted permissions for a package, or an explicit subset of them.
-     *
-     * @param pkg the package
-     * @param permissions the names of the subset of permissions to be allowlisted, or {@code null}
-     *                    for allowlisting all the requested restricted permissions
-     * @param userIds the user IDs
-     */
-    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-    public abstract void setAllowlistedRestrictedPermissions(
-            @NonNull AndroidPackage pkg, @Nullable List<String> permissions,
-            @PackageManager.PermissionWhitelistFlags int allowlistFlags, @NonNull int[] userIds);
-
-    /**
-     * Set whether a package is exempted from auto revoke.
-     *
-     * @param pkg the package
-     * @param exempted whether the package is exempted from auto revoke
-     * @param userIds the user IDs
-     */
-    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-    public abstract void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted,
-            @NonNull int[] userIds);
-
-    /**
      * Update permissions when a package changed.
      *
      * <p><ol>
@@ -526,6 +489,21 @@
             @Nullable AndroidPackage oldPkg);
 
     /**
+     * Callback when a package has been installed for certain users.
+     *
+     * @param pkg the installed package
+     * @param grantedPermissions the permissions to be granted
+     * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted
+     * @param autoRevokePermissionsMode the auto revoke permissions mode for this package
+     * @param userId the user ID this package is installed for
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    public abstract void onPackageInstalled(@NonNull AndroidPackage pkg,
+            @NonNull List<String> grantedPermissions,
+            @NonNull List<String> allowlistedRestrictedPermissions,
+            int autoRevokePermissionsMode, @UserIdInt int userId);
+
+    /**
      * Callback when a package has been removed.
      *
      * @param pkg the removed package
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index dd287ca..1e4e0a6 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -224,10 +224,9 @@
         mFileUpdater = new FileUpdater(context);
         mBatterySavingStats = batterySavingStats;
 
+        // TODO(79580230): remove plugin code and maybe screen on/off listeners?
         // Initialize plugins.
-        mPlugins = new Plugin[] {
-                new BatterySaverLocationPlugin(mContext)
-        };
+        mPlugins = new Plugin[0];
         PowerManager.invalidatePowerSaveModeCaches();
     }
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
deleted file mode 100644
index a77d133..0000000
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverLocationPlugin.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.power.batterysaver;
-
-import android.content.Context;
-import android.os.PowerManager;
-import android.provider.Settings;
-import android.provider.Settings.Global;
-import android.util.Slog;
-
-import com.android.server.power.batterysaver.BatterySaverController.Plugin;
-
-public class BatterySaverLocationPlugin implements Plugin {
-    private static final String TAG = "BatterySaverLocationPlugin";
-
-    private static final boolean DEBUG = BatterySaverController.DEBUG;
-
-    private final Context mContext;
-
-    public BatterySaverLocationPlugin(Context context) {
-        mContext = context;
-    }
-
-    @Override
-    public void onBatterySaverChanged(BatterySaverController caller) {
-        if (DEBUG) {
-            Slog.d(TAG, "onBatterySaverChanged");
-        }
-        updateLocationState(caller);
-    }
-
-    @Override
-    public void onSystemReady(BatterySaverController caller) {
-        if (DEBUG) {
-            Slog.d(TAG, "onSystemReady");
-        }
-        updateLocationState(caller);
-    }
-
-    private void updateLocationState(BatterySaverController caller) {
-        final boolean kill =
-                (caller.getBatterySaverPolicy().getGpsMode()
-                        == PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF)
-                        && !caller.isInteractive();
-
-        if (DEBUG) {
-            Slog.d(TAG, "Battery saver " + (kill ? "stopping" : "restoring") + " location.");
-        }
-        Settings.Global.putInt(mContext.getContentResolver(),
-                Global.LOCATION_GLOBAL_KILL_SWITCH, kill ? 1 : 0);
-    }
-}
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index b33dc8f..7751397 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -28,6 +28,7 @@
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
@@ -391,8 +392,7 @@
     private void readLegacyFileLocked() {
         File file = getFile(mUserId);
         try (FileInputStream in = new AtomicFile(file).openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parseXmlLocked(parser);
             Slog.i(LOG_TAG, "Read roles.xml successfully");
         } catch (FileNotFoundException e) {
@@ -402,7 +402,7 @@
         }
     }
 
-    private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
+    private void parseXmlLocked(@NonNull TypedXmlPullParser parser) throws IOException,
             XmlPullParserException {
         int type;
         int depth;
@@ -421,9 +421,9 @@
         Slog.w(LOG_TAG, "Missing <" + TAG_ROLES + "> in roles.xml");
     }
 
-    private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException,
+    private void parseRolesLocked(@NonNull TypedXmlPullParser parser) throws IOException,
             XmlPullParserException {
-        mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
+        mVersion = parser.getAttributeInt(null, ATTRIBUTE_VERSION);
         mPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
         mRoles.clear();
 
@@ -445,7 +445,7 @@
     }
 
     @NonNull
-    private ArraySet<String> parseRoleHoldersLocked(@NonNull XmlPullParser parser)
+    private ArraySet<String> parseRoleHoldersLocked(@NonNull TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         ArraySet<String> roleHolders = new ArraySet<>();
 
diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
index d2614e4..4f3101d 100644
--- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
+++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java
@@ -51,13 +51,10 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.Preconditions;
 import com.android.server.pm.Installer;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -65,7 +62,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -319,12 +315,12 @@
     }
 
     @VisibleForTesting
-    static void saveToXml(XmlSerializer out,
+    static void saveToXml(TypedXmlSerializer out,
             List<CacheQuotaHint> requests, long bytesWhenCalculated) throws IOException {
         out.startDocument(null, true);
         out.startTag(null, CACHE_INFO_TAG);
         int requestSize = requests.size();
-        out.attribute(null, ATTR_PREVIOUS_BYTES, Long.toString(bytesWhenCalculated));
+        out.attributeLong(null, ATTR_PREVIOUS_BYTES, bytesWhenCalculated);
 
         for (int i = 0; i < requestSize; i++) {
             CacheQuotaHint request = requests.get(i);
@@ -333,8 +329,8 @@
             if (uuid != null) {
                 out.attribute(null, ATTR_UUID, request.getVolumeUuid());
             }
-            out.attribute(null, ATTR_UID, Integer.toString(request.getUid()));
-            out.attribute(null, ATTR_QUOTA_IN_BYTES, Long.toString(request.getQuota()));
+            out.attributeInt(null, ATTR_UID, request.getUid());
+            out.attributeLong(null, ATTR_QUOTA_IN_BYTES, request.getQuota());
             out.endTag(null, TAG_QUOTA);
         }
         out.endTag(null, CACHE_INFO_TAG);
@@ -364,8 +360,7 @@
         final List<CacheQuotaHint> quotas = new ArrayList<>();
         long previousBytes;
         try {
-            previousBytes = Long.parseLong(parser.getAttributeValue(
-                    null, ATTR_PREVIOUS_BYTES));
+            previousBytes = parser.getAttributeLong(null, ATTR_PREVIOUS_BYTES);
         } catch (NumberFormatException e) {
             throw new IllegalStateException(
                     "Previous bytes formatted incorrectly; aborting quota read.");
@@ -389,14 +384,14 @@
     }
 
     @VisibleForTesting
-    static CacheQuotaHint getRequestFromXml(XmlPullParser parser) {
+    static CacheQuotaHint getRequestFromXml(TypedXmlPullParser parser) {
         try {
             String uuid = parser.getAttributeValue(null, ATTR_UUID);
-            int uid = Integer.parseInt(parser.getAttributeValue(null, ATTR_UID));
-            long bytes = Long.parseLong(parser.getAttributeValue(null, ATTR_QUOTA_IN_BYTES));
+            int uid = parser.getAttributeInt(null, ATTR_UID);
+            long bytes = parser.getAttributeLong(null, ATTR_QUOTA_IN_BYTES);
             return new CacheQuotaHint.Builder()
                     .setVolumeUuid(uuid).setUid(uid).setQuota(bytes).build();
-        } catch (NumberFormatException e) {
+        } catch (XmlPullParserException e) {
             Slog.e(TAG, "Invalid cache quota request, skipping.");
             return null;
         }
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index 7652c43..fd0df8d 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -129,7 +129,7 @@
     @GuardedBy("this")
     private PackageStatus getPackageStatusLocked() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
-            XmlPullParser parser = parseToPackageStatusTag(fis);
+            TypedXmlPullParser parser = parseToPackageStatusTag(fis);
             Integer checkStatus = getNullableIntAttribute(parser, ATTRIBUTE_CHECK_STATUS);
             if (checkStatus == null) {
                 return null;
@@ -254,7 +254,7 @@
     @GuardedBy("this")
     private int getCurrentOptimisticLockId() throws ParseException {
         try (FileInputStream fis = mPackageStatusFile.openRead()) {
-            XmlPullParser parser = parseToPackageStatusTag(fis);
+            TypedXmlPullParser parser = parseToPackageStatusTag(fis);
             return getIntAttribute(parser, ATTRIBUTE_OPTIMISTIC_LOCK_ID);
         } catch (IOException e) {
             ParseException e2 = new ParseException("Unable to read file", 0);
@@ -264,7 +264,7 @@
     }
 
     /** Returns a parser or throws ParseException, never returns null. */
-    private static XmlPullParser parseToPackageStatusTag(FileInputStream fis)
+    private static TypedXmlPullParser parseToPackageStatusTag(FileInputStream fis)
             throws ParseException {
         try {
             TypedXmlPullParser parser = Xml.resolvePullParser(fis);
@@ -358,7 +358,7 @@
         }
     }
 
-    private static Integer getNullableIntAttribute(XmlPullParser parser, String attributeName)
+    private static Integer getNullableIntAttribute(TypedXmlPullParser parser, String attributeName)
             throws ParseException {
         String attributeValue = parser.getAttributeValue(null, attributeName);
         try {
@@ -374,7 +374,7 @@
         }
     }
 
-    private static int getIntAttribute(XmlPullParser parser, String attributeName)
+    private static int getIntAttribute(TypedXmlPullParser parser, String attributeName)
             throws ParseException {
         Integer value = getNullableIntAttribute(parser, attributeName);
         if (value == null) {
diff --git a/services/core/java/com/android/server/tv/PersistentDataStore.java b/services/core/java/com/android/server/tv/PersistentDataStore.java
index 355c385..d3c9b3b 100644
--- a/services/core/java/com/android/server/tv/PersistentDataStore.java
+++ b/services/core/java/com/android/server/tv/PersistentDataStore.java
@@ -26,6 +26,7 @@
 import android.text.TextUtils;
 import android.util.AtomicFile;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
@@ -166,7 +167,7 @@
             return;
         }
 
-        XmlPullParser parser;
+        TypedXmlPullParser parser;
         try {
             parser = Xml.resolvePullParser(is);
             loadFromXml(parser);
@@ -237,7 +238,7 @@
     private static final String ATTR_STRING = "string";
     private static final String ATTR_ENABLED = "enabled";
 
-    private void loadFromXml(XmlPullParser parser)
+    private void loadFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         XmlUtils.beginDocument(parser, TAG_TV_INPUT_MANAGER_STATE);
         final int outerDepth = parser.getDepth();
@@ -255,7 +256,7 @@
         }
     }
 
-    private void loadBlockedRatingsFromXml(XmlPullParser parser)
+    private void loadBlockedRatingsFromXml(TypedXmlPullParser parser)
             throws IOException, XmlPullParserException {
         final int outerDepth = parser.getDepth();
         while (XmlUtils.nextElementWithin(parser, outerDepth)) {
@@ -270,7 +271,7 @@
         }
     }
 
-    private void saveToXml(XmlSerializer serializer) throws IOException {
+    private void saveToXml(TypedXmlSerializer serializer) throws IOException {
         serializer.startDocument(null, true);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, TAG_TV_INPUT_MANAGER_STATE);
@@ -284,7 +285,7 @@
         }
         serializer.endTag(null, TAG_BLOCKED_RATINGS);
         serializer.startTag(null, TAG_PARENTAL_CONTROLS);
-        serializer.attribute(null, ATTR_ENABLED, Boolean.toString(mParentalControlsEnabled));
+        serializer.attributeBoolean(null, ATTR_ENABLED, mParentalControlsEnabled);
         serializer.endTag(null, TAG_PARENTAL_CONTROLS);
         serializer.endTag(null, TAG_TV_INPUT_MANAGER_STATE);
         serializer.endDocument();
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
index 367b966..beb11ed 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/UseCasePriorityHints.java
@@ -20,6 +20,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -114,9 +115,7 @@
     protected void parseInternal(InputStream in)
             throws IOException, XmlPullParserException {
         try {
-            XmlPullParser parser = Xml.newPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-            parser.setInput(in, null);
+            TypedXmlPullParser parser = Xml.resolvePullParser(in);
             parser.nextTag();
             readUseCase(parser);
             in.close();
@@ -137,7 +136,7 @@
         }
     }
 
-    private void readUseCase(XmlPullParser parser)
+    private void readUseCase(TypedXmlPullParser parser)
             throws XmlPullParserException, IOException {
         parser.require(XmlPullParser.START_TAG, NS, "config");
         while (parser.next() != XmlPullParser.END_TAG) {
@@ -176,7 +175,7 @@
         }
     }
 
-    private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
+    private void skip(TypedXmlPullParser parser) throws XmlPullParserException, IOException {
         if (parser.getEventType() != XmlPullParser.START_TAG) {
             throw new IllegalStateException();
         }
@@ -193,7 +192,7 @@
         }
     }
 
-    private int readAttributeToInt(String attributeName, XmlPullParser parser) {
+    private int readAttributeToInt(String attributeName, TypedXmlPullParser parser) {
         return Integer.valueOf(parser.getAttributeValue(null, attributeName));
     }
 
@@ -203,7 +202,7 @@
     }
 
     @PriorityHintUseCaseType
-    private static int formatTypeToNum(String attributeName, XmlPullParser parser) {
+    private static int formatTypeToNum(String attributeName, TypedXmlPullParser parser) {
         String useCaseName = parser.getAttributeValue(null, attributeName);
         switch (useCaseName) {
             case "USE_CASE_BACKGROUND":
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index bbb5374..dcc1599 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -668,22 +668,22 @@
                     if (TAG_URI_GRANT.equals(tag)) {
                         final int sourceUserId;
                         final int targetUserId;
-                        final int userHandle = readIntAttribute(in,
-                                ATTR_USER_HANDLE, UserHandle.USER_NULL);
+                        final int userHandle = in.getAttributeInt(null, ATTR_USER_HANDLE,
+                                UserHandle.USER_NULL);
                         if (userHandle != UserHandle.USER_NULL) {
                             // For backwards compatibility.
                             sourceUserId = userHandle;
                             targetUserId = userHandle;
                         } else {
-                            sourceUserId = readIntAttribute(in, ATTR_SOURCE_USER_ID);
-                            targetUserId = readIntAttribute(in, ATTR_TARGET_USER_ID);
+                            sourceUserId = in.getAttributeInt(null, ATTR_SOURCE_USER_ID);
+                            targetUserId = in.getAttributeInt(null, ATTR_TARGET_USER_ID);
                         }
                         final String sourcePkg = in.getAttributeValue(null, ATTR_SOURCE_PKG);
                         final String targetPkg = in.getAttributeValue(null, ATTR_TARGET_PKG);
                         final Uri uri = Uri.parse(in.getAttributeValue(null, ATTR_URI));
-                        final boolean prefix = readBooleanAttribute(in, ATTR_PREFIX);
-                        final int modeFlags = readIntAttribute(in, ATTR_MODE_FLAGS);
-                        final long createdTime = readLongAttribute(in, ATTR_CREATED_TIME, now);
+                        final boolean prefix = in.getAttributeBoolean(null, ATTR_PREFIX, false);
+                        final int modeFlags = in.getAttributeInt(null, ATTR_MODE_FLAGS);
+                        final long createdTime = in.getAttributeLong(null, ATTR_CREATED_TIME, now);
 
                         // Validity check that provider still belongs to source package
                         // Both direct boot aware and unaware packages are fine as we
@@ -1319,14 +1319,14 @@
             out.startTag(null, TAG_URI_GRANTS);
             for (UriPermission.Snapshot perm : persist) {
                 out.startTag(null, TAG_URI_GRANT);
-                writeIntAttribute(out, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
-                writeIntAttribute(out, ATTR_TARGET_USER_ID, perm.targetUserId);
+                out.attributeInt(null, ATTR_SOURCE_USER_ID, perm.uri.sourceUserId);
+                out.attributeInt(null, ATTR_TARGET_USER_ID, perm.targetUserId);
                 out.attribute(null, ATTR_SOURCE_PKG, perm.sourcePkg);
                 out.attribute(null, ATTR_TARGET_PKG, perm.targetPkg);
                 out.attribute(null, ATTR_URI, String.valueOf(perm.uri.uri));
                 writeBooleanAttribute(out, ATTR_PREFIX, perm.uri.prefix);
-                writeIntAttribute(out, ATTR_MODE_FLAGS, perm.persistedModeFlags);
-                writeLongAttribute(out, ATTR_CREATED_TIME, perm.persistedCreateTime);
+                out.attributeInt(null, ATTR_MODE_FLAGS, perm.persistedModeFlags);
+                out.attributeLong(null, ATTR_CREATED_TIME, perm.persistedCreateTime);
                 out.endTag(null, TAG_URI_GRANT);
             }
             out.endTag(null, TAG_URI_GRANTS);
diff --git a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
similarity index 90%
rename from services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
rename to services/core/java/com/android/server/utils/DeviceConfigInterface.java
index ab7e7f6..ff60903 100644
--- a/services/core/java/com/android/server/wm/utils/DeviceConfigInterface.java
+++ b/services/core/java/com/android/server/utils/DeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.utils;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -54,6 +54,11 @@
     boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue);
 
     /**
+     * @see DeviceConfig#getFloat
+     */
+    float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue);
+
+    /**
      * @see DeviceConfig#addOnPropertiesChangedListener
      */
     void addOnPropertiesChangedListener(@NonNull String namespace, @NonNull Executor executor,
@@ -96,6 +101,12 @@
         }
 
         @Override
+        public float getFloat(@NonNull String namespace, @NonNull String name,
+                float defaultValue) {
+            return DeviceConfig.getFloat(namespace, name, defaultValue);
+        }
+
+        @Override
         public void addOnPropertiesChangedListener(String namespace, Executor executor,
                 DeviceConfig.OnPropertiesChangedListener listener) {
             DeviceConfig.addOnPropertiesChangedListener(namespace, executor, listener);
diff --git a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
index 12c6a7a..fe51d74 100644
--- a/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
+++ b/services/core/java/com/android/server/utils/eventlog/LocalEventLog.java
@@ -99,6 +99,7 @@
     private long mLastLogRealtimeMs;
 
     public LocalEventLog(int size) {
+        Preconditions.checkArgument(size > 0);
         mLog = new Log[size];
         mLogSize = 0;
         mLogEndIndex = 0;
@@ -163,7 +164,7 @@
 
         if (mLogSize == mLog.length) {
             // if log is full, size will remain the same, but update the start time
-            mStartRealtimeMs += event.getTimeDeltaMs();
+            mStartRealtimeMs += mLog[startIndex()].getTimeDeltaMs();
         } else {
             // otherwise add an item
             mLogSize++;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e07540a..bbe86be 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -100,12 +100,12 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.WindowManagerInternal;
@@ -114,7 +114,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -125,7 +124,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -2909,11 +2907,9 @@
     private void saveSettingsLocked(int userId) {
         JournaledFile journal = makeJournaledFile(userId);
         FileOutputStream fstream = null;
-        BufferedOutputStream stream = null;
         try {
             fstream = new FileOutputStream(journal.chooseForWrite(), false);
-            stream = new BufferedOutputStream(fstream);
-            TypedXmlSerializer out = Xml.resolveSerializer(stream);
+            TypedXmlSerializer out = Xml.resolveSerializer(fstream);
             out.startDocument(null, true);
 
             WallpaperData wallpaper;
@@ -2929,56 +2925,56 @@
 
             out.endDocument();
 
-            stream.flush(); // also flushes fstream
+            fstream.flush();
             FileUtils.sync(fstream);
-            stream.close(); // also closes fstream
+            fstream.close();
             journal.commit();
         } catch (IOException e) {
-            IoUtils.closeQuietly(stream);
+            IoUtils.closeQuietly(fstream);
             journal.rollback();
         }
     }
 
-    private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
+    private void writeWallpaperAttributes(TypedXmlSerializer out, String tag,
+            WallpaperData wallpaper)
             throws IllegalArgumentException, IllegalStateException, IOException {
         if (DEBUG) {
             Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
         final DisplayData wpdData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
         out.startTag(null, tag);
-        out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
-        out.attribute(null, "width", Integer.toString(wpdData.mWidth));
-        out.attribute(null, "height", Integer.toString(wpdData.mHeight));
+        out.attributeInt(null, "id", wallpaper.wallpaperId);
+        out.attributeInt(null, "width", wpdData.mWidth);
+        out.attributeInt(null, "height", wpdData.mHeight);
 
-        out.attribute(null, "cropLeft", Integer.toString(wallpaper.cropHint.left));
-        out.attribute(null, "cropTop", Integer.toString(wallpaper.cropHint.top));
-        out.attribute(null, "cropRight", Integer.toString(wallpaper.cropHint.right));
-        out.attribute(null, "cropBottom", Integer.toString(wallpaper.cropHint.bottom));
+        out.attributeInt(null, "cropLeft", wallpaper.cropHint.left);
+        out.attributeInt(null, "cropTop", wallpaper.cropHint.top);
+        out.attributeInt(null, "cropRight", wallpaper.cropHint.right);
+        out.attributeInt(null, "cropBottom", wallpaper.cropHint.bottom);
 
         if (wpdData.mPadding.left != 0) {
-            out.attribute(null, "paddingLeft", Integer.toString(wpdData.mPadding.left));
+            out.attributeInt(null, "paddingLeft", wpdData.mPadding.left);
         }
         if (wpdData.mPadding.top != 0) {
-            out.attribute(null, "paddingTop", Integer.toString(wpdData.mPadding.top));
+            out.attributeInt(null, "paddingTop", wpdData.mPadding.top);
         }
         if (wpdData.mPadding.right != 0) {
-            out.attribute(null, "paddingRight", Integer.toString(wpdData.mPadding.right));
+            out.attributeInt(null, "paddingRight", wpdData.mPadding.right);
         }
         if (wpdData.mPadding.bottom != 0) {
-            out.attribute(null, "paddingBottom", Integer.toString(wpdData.mPadding.bottom));
+            out.attributeInt(null, "paddingBottom", wpdData.mPadding.bottom);
         }
 
         if (wallpaper.primaryColors != null) {
             int colorsCount = wallpaper.primaryColors.getMainColors().size();
-            out.attribute(null, "colorsCount", Integer.toString(colorsCount));
+            out.attributeInt(null, "colorsCount", colorsCount);
             if (colorsCount > 0) {
                 for (int i = 0; i < colorsCount; i++) {
                     final Color wc = wallpaper.primaryColors.getMainColors().get(i);
-                    out.attribute(null, "colorValue"+i, Integer.toString(wc.toArgb()));
+                    out.attributeInt(null, "colorValue" + i, wc.toArgb());
                 }
             }
-            out.attribute(null, "colorHints",
-                    Integer.toString(wallpaper.primaryColors.getColorHints()));
+            out.attributeInt(null, "colorHints", wallpaper.primaryColors.getColorHints());
         }
 
         out.attribute(null, "name", wallpaper.name);
@@ -3028,12 +3024,8 @@
         }
     }
 
-    private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
-        String value = parser.getAttributeValue(null, name);
-        if (value == null) {
-            return defValue;
-        }
-        return Integer.parseInt(value);
+    private int getAttributeInt(TypedXmlPullParser parser, String name, int defValue) {
+        return parser.getAttributeInt(null, name, defValue);
     }
 
     /**
@@ -3213,11 +3205,11 @@
         }
     }
 
-    private void parseWallpaperAttributes(XmlPullParser parser, WallpaperData wallpaper,
-            boolean keepDimensionHints) {
-        final String idString = parser.getAttributeValue(null, "id");
-        if (idString != null) {
-            final int id = wallpaper.wallpaperId = Integer.parseInt(idString);
+    private void parseWallpaperAttributes(TypedXmlPullParser parser, WallpaperData wallpaper,
+            boolean keepDimensionHints) throws XmlPullParserException {
+        final int id = parser.getAttributeInt(null, "id", -1);
+        if (id != -1) {
+            wallpaper.wallpaperId = id;
             if (id > mWallpaperId) {
                 mWallpaperId = id;
             }
@@ -3228,8 +3220,8 @@
         final DisplayData wpData = getDisplayDataOrCreate(DEFAULT_DISPLAY);
 
         if (!keepDimensionHints) {
-            wpData.mWidth = Integer.parseInt(parser.getAttributeValue(null, "width"));
-            wpData.mHeight = Integer.parseInt(parser.getAttributeValue(null, "height"));
+            wpData.mWidth = parser.getAttributeInt(null, "width");
+            wpData.mHeight = parser.getAttributeInt(null, "height");
         }
         wallpaper.cropHint.left = getAttributeInt(parser, "cropLeft", 0);
         wallpaper.cropHint.top = getAttributeInt(parser, "cropTop", 0);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 913c3e5..75273ec 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -18,7 +18,6 @@
 
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
-import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
 import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
 import static android.app.ActivityOptions.ANIM_CUSTOM;
 import static android.app.ActivityOptions.ANIM_NONE;
@@ -96,18 +95,18 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
-import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
-import static android.view.WindowManager.TRANSIT_OLD_UNSET;
-import static android.view.WindowManager.TransitionOldType;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
@@ -183,6 +182,7 @@
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
+import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.Task.ActivityState.DESTROYED;
 import static com.android.server.wm.Task.ActivityState.DESTROYING;
 import static com.android.server.wm.Task.ActivityState.FINISHING;
@@ -194,7 +194,6 @@
 import static com.android.server.wm.Task.ActivityState.STARTED;
 import static com.android.server.wm.Task.ActivityState.STOPPED;
 import static com.android.server.wm.Task.ActivityState.STOPPING;
-import static com.android.server.wm.Task.TASK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
@@ -277,6 +276,8 @@
 import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.AppTransitionAnimationSpec;
 import android.view.DisplayCutout;
@@ -291,6 +292,7 @@
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.TransitionOldType;
 import android.view.animation.Animation;
 import android.window.WindowContainerToken;
 
@@ -320,9 +322,7 @@
 
 import com.google.android.collect.Sets;
 
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.IOException;
@@ -6372,14 +6372,7 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        setOrientation(requestedOrientation, mayFreezeScreenLocked());
-        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
-                task.mTaskId, requestedOrientation);
-    }
-
-    private void setOrientation(int requestedOrientation, boolean freezeScreenIfNeeded) {
-        final IBinder binder = freezeScreenIfNeeded ? appToken.asBinder() : null;
-        setOrientation(requestedOrientation, binder, this);
+        setOrientation(requestedOrientation, this);
 
         // Push the new configuration to the requested app in case where it's not pushed, e.g. when
         // the request is handled at task level with letterbox.
@@ -6387,6 +6380,9 @@
                 mLastReportedConfiguration.getMergedConfiguration())) {
             ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */);
         }
+
+        mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
+                task.mTaskId, requestedOrientation);
     }
 
     /*
@@ -6403,8 +6399,7 @@
             return;
         }
 
-        final IBinder freezeToken = mayFreezeScreenLocked() ? appToken : null;
-        if (onDescendantOrientationChanged(freezeToken, this)) {
+        if (onDescendantOrientationChanged(this)) {
             // The app is just becoming visible, and the parent Task has updated with the
             // orientation request. Update the size compat mode.
             updateSizeCompatMode();
@@ -7081,6 +7076,12 @@
             return true;
         }
 
+        if (isState(DESTROYED)) {
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "in destroyed state %s", this);
+            return true;
+        }
+
         if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
             ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
                     + "invisible: %s", this);
@@ -7464,9 +7465,9 @@
                 || (info.flags & FLAG_NO_HISTORY) != 0;
     }
 
-    void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        out.attribute(null, ATTR_ID, String.valueOf(createTime));
-        out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
+    void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException {
+        out.attributeLong(null, ATTR_ID, createTime);
+        out.attributeInt(null, ATTR_LAUNCHEDFROMUID, launchedFromUid);
         if (launchedFromPackage != null) {
             out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
         }
@@ -7476,8 +7477,8 @@
         if (resolvedType != null) {
             out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
         }
-        out.attribute(null, ATTR_COMPONENTSPECIFIED, String.valueOf(componentSpecified));
-        out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
+        out.attributeBoolean(null, ATTR_COMPONENTSPECIFIED, componentSpecified);
+        out.attributeInt(null, ATTR_USERID, mUserId);
 
         if (taskDescription != null) {
             taskDescription.saveToXml(out);
@@ -7494,43 +7495,20 @@
         }
     }
 
-    static ActivityRecord restoreFromXml(XmlPullParser in,
+    static ActivityRecord restoreFromXml(TypedXmlPullParser in,
             ActivityTaskSupervisor taskSupervisor) throws IOException, XmlPullParserException {
         Intent intent = null;
         PersistableBundle persistentState = null;
-        int launchedFromUid = 0;
-        String launchedFromPackage = null;
-        String launchedFromFeature = null;
-        String resolvedType = null;
-        boolean componentSpecified = false;
-        int userId = 0;
-        long createTime = -1;
+        int launchedFromUid = in.getAttributeInt(null, ATTR_LAUNCHEDFROMUID, 0);
+        String launchedFromPackage = in.getAttributeValue(null, ATTR_LAUNCHEDFROMPACKAGE);
+        String launchedFromFeature = in.getAttributeValue(null, ATTR_LAUNCHEDFROMFEATURE);
+        String resolvedType = in.getAttributeValue(null, ATTR_RESOLVEDTYPE);
+        boolean componentSpecified = in.getAttributeBoolean(null, ATTR_COMPONENTSPECIFIED, false);
+        int userId = in.getAttributeInt(null, ATTR_USERID, 0);
+        long createTime = in.getAttributeLong(null, ATTR_ID, -1);
         final int outerDepth = in.getDepth();
-        TaskDescription taskDescription = new TaskDescription();
 
-        for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) {
-            final String attrName = in.getAttributeName(attrNdx);
-            final String attrValue = in.getAttributeValue(attrNdx);
-            if (DEBUG) Slog.d(TaskPersister.TAG,
-                        "ActivityRecord: attribute name=" + attrName + " value=" + attrValue);
-            if (ATTR_ID.equals(attrName)) {
-                createTime = Long.parseLong(attrValue);
-            } else if (ATTR_LAUNCHEDFROMUID.equals(attrName)) {
-                launchedFromUid = Integer.parseInt(attrValue);
-            } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
-                launchedFromPackage = attrValue;
-            } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) {
-                launchedFromFeature = attrValue;
-            } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
-                resolvedType = attrValue;
-            } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
-                componentSpecified = Boolean.parseBoolean(attrValue);
-            } else if (ATTR_USERID.equals(attrName)) {
-                userId = Integer.parseInt(attrValue);
-            } else if (!attrName.startsWith(ATTR_TASKDESCRIPTION_PREFIX)) {
-                Log.d(TAG, "Unknown ActivityRecord attribute=" + attrName);
-            }
-        }
+        TaskDescription taskDescription = new TaskDescription();
         taskDescription.restoreFromXml(in);
 
         int event;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e12dc2b..8ba76be 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2738,9 +2738,8 @@
         }
     }
 
-    @Override
-    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
-        return getFilteredTasks(maxNum, false /* filterForVisibleRecents */);
+    List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
+        return getTasks(maxNum, false /* filterForVisibleRecents */);
     }
 
     /**
@@ -2748,7 +2747,7 @@
      *                                 be visible in the recent task list in systemui
      */
     @Override
-    public List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum,
+    public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
             boolean filterOnlyVisibleRecents) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
diff --git a/services/core/java/com/android/server/wm/AppWarnings.java b/services/core/java/com/android/server/wm/AppWarnings.java
index d40dea2..55200b5 100644
--- a/services/core/java/com/android/server/wm/AppWarnings.java
+++ b/services/core/java/com/android/server/wm/AppWarnings.java
@@ -492,7 +492,7 @@
                 }
                 out.startTag(null, "package");
                 out.attribute(null, "name", pkg);
-                out.attribute(null, "flags", Integer.toString(mode));
+                out.attributeInt(null, "flags", mode);
                 out.endTag(null, "package");
             }
 
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 89c5f62..4b34954 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -43,7 +43,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -113,14 +112,7 @@
                             if ("pkg".equals(tagName)) {
                                 String pkg = parser.getAttributeValue(null, "name");
                                 if (pkg != null) {
-                                    String mode = parser.getAttributeValue(null, "mode");
-                                    int modeInt = 0;
-                                    if (mode != null) {
-                                        try {
-                                            modeInt = Integer.parseInt(mode);
-                                        } catch (NumberFormatException e) {
-                                        }
-                                    }
+                                    int modeInt = parser.getAttributeInt(null, "mode", 0);
                                     mPackages.put(pkg, modeInt);
                                 }
                             }
@@ -396,7 +388,7 @@
                 }
                 out.startTag(null, "pkg");
                 out.attribute(null, "name", pkg);
-                out.attribute(null, "mode", Integer.toString(mode));
+                out.attributeInt(null, "mode", mode);
                 out.endTag(null, "pkg");
             }
 
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 60a62dc..36a1ef9 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -182,6 +182,11 @@
         // writing to proto (which has significant cost if we write a lot of empty configurations).
         mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
         mRequestedOverrideConfiguration.setTo(overrideConfiguration);
+        final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
+        if (mHasOverrideConfiguration && providesMaxBounds()
+                && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
+            mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
+        }
         // Update full configuration of this container and all its children.
         final ConfigurationContainer parent = getParent();
         onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
@@ -341,9 +346,6 @@
 
         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
         mRequestsTmpConfig.windowConfiguration.setBounds(bounds);
-        if (overrideMaxBounds) {
-            mRequestsTmpConfig.windowConfiguration.setMaxBounds(bounds);
-        }
         onRequestedOverrideConfigurationChanged(mRequestsTmpConfig);
 
         return boundsChange;
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 48e0300..a4ac16f 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -33,7 +33,6 @@
 import android.annotation.Nullable;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.util.proto.ProtoOutputStream;
 import android.window.DisplayAreaInfo;
 import android.window.IDisplayAreaOrganizer;
@@ -151,12 +150,11 @@
     }
 
     @Override
-    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
         // If this is set to ignore the orientation request, we don't propagate descendant
         // orientation request.
         return !mIgnoreOrientationRequest
-                && super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+                && super.onDescendantOrientationChanged(requestingContainer);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index ccc85f8..5df5050 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -55,6 +55,8 @@
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -73,8 +75,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
@@ -93,7 +93,6 @@
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.IME_POLICY;
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -105,6 +104,7 @@
 import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.IME_INSETS_SOURCE_PROVIDER;
+import static com.android.server.wm.DisplayContentProto.IME_POLICY;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_CONTROL_TARGET;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_INPUT_TARGET;
 import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET;
@@ -559,6 +559,9 @@
      */
     InsetsControlTarget mInputMethodControlTarget;
 
+    /** The surface parent of the IME container. */
+    private SurfaceControl mInputMethodSurfaceParent;
+
     /** If true hold off on modifying the animation layer of mInputMethodTarget */
     boolean mInputMethodTargetWaitingAnim;
 
@@ -1300,10 +1303,9 @@
     }
 
     @Override
-    boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
         final Configuration config = updateOrientation(
-                getRequestedOverrideConfiguration(), freezeDisplayToken, false /* forceUpdate */);
+                getRequestedOverrideConfiguration(), requestingContainer, false /* forceUpdate */);
         // If display rotation class tells us that it doesn't consider app requested orientation,
         // this display won't rotate just because of an app changes its requested orientation. Thus
         // it indicates that this display chooses not to handle this request.
@@ -1355,11 +1357,11 @@
      * @param currentConfig The current requested override configuration (it is usually set from
      *                      the last {@link #sendNewConfiguration}) of the display. It is used to
      *                      check if the configuration container has the latest state.
-     * @param freezeDisplayToken Freeze the app window token if the orientation is changed.
+     * @param freezeDisplayWindow Freeze the app window if the orientation is changed.
      * @param forceUpdate See {@link DisplayRotation#updateRotationUnchecked(boolean)}
      */
-    Configuration updateOrientation(Configuration currentConfig, IBinder freezeDisplayToken,
-            boolean forceUpdate) {
+    Configuration updateOrientation(Configuration currentConfig,
+            WindowContainer freezeDisplayWindow, boolean forceUpdate) {
         if (!mDisplayReady) {
             return null;
         }
@@ -1368,9 +1370,9 @@
         if (updateOrientation(forceUpdate)) {
             // If we changed the orientation but mOrientationChangeComplete is already true,
             // we used seamless rotation, and we don't need to freeze the screen.
-            if (freezeDisplayToken != null && !mWmService.mRoot.mOrientationChangeComplete) {
-                final ActivityRecord activity = getActivityRecord(freezeDisplayToken);
-                if (activity != null) {
+            if (freezeDisplayWindow != null && !mWmService.mRoot.mOrientationChangeComplete) {
+                final ActivityRecord activity = freezeDisplayWindow.asActivityRecord();
+                if (activity != null && activity.mayFreezeScreenLocked()) {
                     activity.startFreezingScreen();
                 }
             }
@@ -1677,6 +1679,28 @@
         }
     }
 
+    void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
+        if (mFixedRotationLaunchingApp != null) {
+            // The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
+            // of insets sources are consistent with the latest state.
+            final InsetsState rotatedState =
+                    mFixedRotationLaunchingApp.getFixedRotationTransformInsetsState();
+            if (rotatedState != null) {
+                final InsetsState state = mInsetsStateController.getRawInsetsState();
+                for (int i = 0; i < InsetsState.SIZE; i++) {
+                    final InsetsSource source = state.peekSource(i);
+                    if (source != null) {
+                        rotatedState.setSourceVisible(i, source.isVisible());
+                    }
+                }
+            }
+        }
+        forAllWindows(dispatchInsetsChanged, true /* traverseTopToBottom */);
+        if (mRemoteInsetsControlTarget != null) {
+            mRemoteInsetsControlTarget.notifyInsetsChanged();
+        }
+    }
+
     /**
      * Update rotation of the display.
      *
@@ -3610,7 +3634,9 @@
         ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target);
         mInputMethodTarget = target;
         mInputMethodTargetWaitingAnim = targetWaitingAnim;
-        assignWindowLayers(true /* setLayoutNeeded */);
+
+        // 1. Reparent the IME container window to the target root DA to get the correct bounds and
+        // config. (Only happens when the target window is in a different root DA)
         if (target != null) {
             RootDisplayArea targetRoot = target.getRootDisplayArea();
             if (targetRoot != null) {
@@ -3619,7 +3645,13 @@
                 targetRoot.placeImeContainer(mImeWindowsContainers);
             }
         }
+        // 2. Reparent the IME container surface to either the input target app, or the IME window
+        // parent.
         updateImeParent();
+        // 3. Assign window layers based on the IME surface parent to make sure it is on top of the
+        // app.
+        assignWindowLayers(true /* setLayoutNeeded */);
+        // 4. Update the IME control target to apply any inset change and animation.
         updateImeControlTarget();
     }
 
@@ -3649,7 +3681,8 @@
 
     void updateImeParent() {
         final SurfaceControl newParent = computeImeParent();
-        if (newParent != null) {
+        if (newParent != null && newParent != mInputMethodSurfaceParent) {
+            mInputMethodSurfaceParent = newParent;
             getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
             scheduleAnimation();
         }
@@ -4426,18 +4459,15 @@
         //
         // In the case of split-screen windowing mode, we need to elevate the IME above the
         // docked divider while keeping the app itself below the docked divider, so instead
-        // we use relative layering of the IME targets child windows, and place the IME in
-        // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
+        // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild
         //
         // In the case the IME target is animating, the animation Z order may be different
         // than the WindowContainer Z order, so it's difficult to be sure we have the correct
-        // IME target. In this case we just layer the IME over all transitions by placing it
-        // in the above applications layer.
+        // IME target. In this case we just layer the IME over its parent surface.
         //
-        // In the case where we have no IME target we assign it where its base layer would
-        // place it in the AboveAppWindowContainers.
+        // In the case where we have no IME target we let its window parent to place it.
         //
-        // Keep IME window in mAboveAppWindowsContainers as long as app's starting window
+        // Keep IME window in surface parent as long as app's starting window
         // exists so it get's layered above the starting window.
         if (imeTarget != null && !(imeTarget.mActivityRecord != null
                 && imeTarget.mActivityRecord.hasStartingWindow()) && (
@@ -4448,6 +4478,11 @@
                     // TODO: We need to use an extra level on the app surface to ensure
                     // this is always above SurfaceView but always below attached window.
                     1);
+        } else if (mInputMethodSurfaceParent != null) {
+            // The IME surface parent may not be its window parent's surface
+            // (@see #computeImeParent), so set relative layer here instead of letting the window
+            // parent to assign layer.
+            mImeWindowsContainers.assignRelativeLayer(t, mInputMethodSurfaceParent, 1);
         }
         super.assignChildLayers(t);
     }
@@ -5197,7 +5232,7 @@
         mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
         super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
         mCurrentOverrideConfigurationChanges = 0;
-        mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
+        mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this);
         mAtmService.addWindowLayoutReasons(
                 ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
     }
@@ -5689,4 +5724,8 @@
         }
         return count;
     }
+
+    MagnificationSpec getMagnificationSpec() {
+        return mMagnificationSpec;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 042dd6d..826b725 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -66,7 +66,6 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
@@ -279,7 +278,6 @@
     private volatile boolean mKeyguardDrawComplete;
     private volatile boolean mWindowManagerDrawComplete;
 
-    private final ArraySet<WindowState> mScreenDecorWindows = new ArraySet<>();
     private WindowState mStatusBar = null;
     private WindowState mNotificationShade = null;
     private final int[] mStatusBarHeightForRotation = new int[4];
@@ -864,19 +862,7 @@
      * @param attrs The window layout parameters to be modified.  These values
      * are modified in-place.
      */
-    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs,
-            int callingPid, int callingUid) {
-
-        final boolean isScreenDecor = (attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
-        if (mScreenDecorWindows.contains(win)) {
-            if (!isScreenDecor) {
-                // No longer has the flag set, so remove from the set.
-                mScreenDecorWindows.remove(win);
-            }
-        } else if (isScreenDecor && hasStatusBarServicePermission(callingPid, callingUid)) {
-            mScreenDecorWindows.add(win);
-        }
-
+    public void adjustWindowParamsLw(WindowState win, WindowManager.LayoutParams attrs) {
         switch (attrs.type) {
             case TYPE_SYSTEM_OVERLAY:
             case TYPE_SECURE_SYSTEM_OVERLAY:
@@ -966,11 +952,6 @@
      * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
      */
     int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) {
-        if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
-            mContext.enforcePermission(
-                    android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
-                    "DisplayPolicy");
-        }
         if ((attrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0) {
             mContext.enforcePermission(
                     android.Manifest.permission.INTERNAL_SYSTEM_WINDOW, callingPid, callingUid,
@@ -1090,10 +1071,6 @@
      * @param attrs Information about the window to be added.
      */
     void addWindowLw(WindowState win, WindowManager.LayoutParams attrs) {
-        if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
-            mScreenDecorWindows.add(win);
-        }
-
         switch (attrs.type) {
             case TYPE_NOTIFICATION_SHADE:
                 mNotificationShade = win;
@@ -1275,7 +1252,6 @@
         if (mLastFocusedWindow == win) {
             mLastFocusedWindow = null;
         }
-        mScreenDecorWindows.remove(win);
     }
 
     private int getStatusBarHeight(DisplayFrames displayFrames) {
@@ -1457,14 +1433,12 @@
         }
 
         final int fl = attrs.flags;
-        final int pfl = attrs.privateFlags;
         final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0
                 && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
-        final boolean screenDecor = (pfl & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0;
         final DisplayFrames displayFrames = isFixedRotationTransforming
                 ? windowToken.getFixedRotationTransformDisplayFrames()
                 : mDisplayContent.mDisplayFrames;
-        if (layoutInScreenAndInsetDecor && !screenDecor) {
+        if (layoutInScreenAndInsetDecor) {
             outDisplayCutout.set(
                     displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout());
         } else {
@@ -1564,7 +1538,6 @@
                     simulatedWindowFrames, barContentFrames,
                     contentFrame -> layoutStatusBar(displayFrames, contentFrame));
         }
-        layoutScreenDecorWindows(displayFrames, simulatedWindowFrames);
     }
 
     /**
@@ -1585,7 +1558,6 @@
 
         layoutNavigationBar(displayFrames, uiMode, null /* simulatedContentFrame */);
         layoutStatusBar(displayFrames, null /* simulatedContentFrame */);
-        layoutScreenDecorWindows(displayFrames, null /* simulatedFrames */);
     }
 
     void updateHideNavInputEventReceiver() {
@@ -1640,47 +1612,6 @@
         state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom);
     }
 
-    /**
-     * Layout the decor windows with {@link #PRIVATE_FLAG_IS_SCREEN_DECOR}.
-     *
-     * @param displayFrames The display frames to be layouted.
-     * @param simulatedFrames Non-null if the caller only needs the result of display frames (see
-     *                        {@link WindowState#mSimulatedWindowFrames}).
-     */
-    private void layoutScreenDecorWindows(DisplayFrames displayFrames,
-            WindowFrames simulatedFrames) {
-        if (mScreenDecorWindows.isEmpty()) {
-            return;
-        }
-
-        sTmpRect.setEmpty();
-        final int displayId = displayFrames.mDisplayId;
-
-        for (int i = mScreenDecorWindows.size() - 1; i >= 0; --i) {
-            final WindowState w = mScreenDecorWindows.valueAt(i);
-            if (w.getDisplayId() != displayId || !w.isVisible()) {
-                // Skip if not on the same display or not visible.
-                continue;
-            }
-
-            final boolean isSimulatedLayout = simulatedFrames != null;
-            if (isSimulatedLayout) {
-                w.setSimulatedWindowFrames(simulatedFrames);
-            }
-            getRotatedWindowBounds(displayFrames, w, sTmpScreenDecorFrame);
-            final WindowFrames windowFrames = w.getLayoutingWindowFrames();
-            windowFrames.setFrames(sTmpScreenDecorFrame /* parentFrame */,
-                    sTmpScreenDecorFrame /* displayFrame */);
-            try {
-                w.computeFrame(displayFrames);
-            } finally {
-                if (isSimulatedLayout) {
-                    w.setSimulatedWindowFrames(null);
-                }
-            }
-        }
-    }
-
     private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
         // decide where the status bar goes ahead of time
         if (mStatusBar == null) {
@@ -1819,8 +1750,7 @@
         // We've already done the navigation bar, status bar, and all screen decor windows. If the
         // status bar can receive input, we need to layout it again to accommodate for the IME
         // window.
-        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar
-                || mScreenDecorWindows.contains(win)) {
+        if ((win == mStatusBar && !canReceiveInput(win)) || win == mNavigationBar) {
             return;
         }
         final WindowManager.LayoutParams attrs = win.getAttrs();
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 78f1426..aa603ab 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -335,36 +335,31 @@
         return fileData;
     }
 
-    private static int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
-        try {
-            final String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.parseInt(str) : defaultValue;
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e);
-            return defaultValue;
-        }
+    private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) {
+        return parser.getAttributeInt(null, name, defaultValue);
     }
 
     @Nullable
-    private static Integer getIntegerAttribute(XmlPullParser parser, String name,
+    private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name,
             @Nullable Integer defaultValue) {
         try {
-            final String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.valueOf(str) : defaultValue;
-        } catch (NumberFormatException e) {
-            Slog.w(TAG, "Failed to parse display window settings attribute: " + name, e);
+            return parser.getAttributeInt(null, name);
+        } catch (Exception ignored) {
             return defaultValue;
         }
     }
 
     @Nullable
-    private static Boolean getBooleanAttribute(XmlPullParser parser, String name,
+    private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name,
             @Nullable Boolean defaultValue) {
-        final String str = parser.getAttributeValue(null, name);
-        return str != null ? Boolean.valueOf(str) : defaultValue;
+        try {
+            return parser.getAttributeBoolean(null, name);
+        } catch (Exception ignored) {
+            return defaultValue;
+        }
     }
 
-    private static void readDisplay(XmlPullParser parser, FileData fileData)
+    private static void readDisplay(TypedXmlPullParser parser, FileData fileData)
             throws NumberFormatException, XmlPullParserException, IOException {
         String name = parser.getAttributeValue(null, "name");
         if (name != null) {
@@ -407,7 +402,7 @@
         XmlUtils.skipCurrentTag(parser);
     }
 
-    private static void readConfig(XmlPullParser parser, FileData fileData)
+    private static void readConfig(TypedXmlPullParser parser, FileData fileData)
             throws NumberFormatException,
             XmlPullParserException, IOException {
         fileData.mIdentifierType = getIntAttribute(parser, "identifier",
@@ -432,8 +427,7 @@
             out.startTag(null, "display-settings");
 
             out.startTag(null, "config");
-            out.attribute(null, "identifier",
-                    Integer.toString(data.mIdentifierType));
+            out.attributeInt(null, "identifier", data.mIdentifierType);
             out.endTag(null, "config");
 
             for (Map.Entry<String, SettingsEntry> entry
@@ -447,8 +441,7 @@
                 out.startTag(null, "display");
                 out.attribute(null, "name", displayIdentifier);
                 if (settingsEntry.mWindowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
-                    out.attribute(null, "windowingMode",
-                            Integer.toString(settingsEntry.mWindowingMode));
+                    out.attributeInt(null, "windowingMode", settingsEntry.mWindowingMode);
                 }
                 if (settingsEntry.mUserRotationMode != null) {
                     out.attribute(null, "userRotationMode",
@@ -459,22 +452,18 @@
                             settingsEntry.mUserRotation.toString());
                 }
                 if (settingsEntry.mForcedWidth != 0 && settingsEntry.mForcedHeight != 0) {
-                    out.attribute(null, "forcedWidth",
-                            Integer.toString(settingsEntry.mForcedWidth));
-                    out.attribute(null, "forcedHeight",
-                            Integer.toString(settingsEntry.mForcedHeight));
+                    out.attributeInt(null, "forcedWidth", settingsEntry.mForcedWidth);
+                    out.attributeInt(null, "forcedHeight", settingsEntry.mForcedHeight);
                 }
                 if (settingsEntry.mForcedDensity != 0) {
-                    out.attribute(null, "forcedDensity",
-                            Integer.toString(settingsEntry.mForcedDensity));
+                    out.attributeInt(null, "forcedDensity", settingsEntry.mForcedDensity);
                 }
                 if (settingsEntry.mForcedScalingMode != null) {
                     out.attribute(null, "forcedScalingMode",
                             settingsEntry.mForcedScalingMode.toString());
                 }
                 if (settingsEntry.mRemoveContentMode != REMOVE_CONTENT_MODE_UNDEFINED) {
-                    out.attribute(null, "removeContentMode",
-                            Integer.toString(settingsEntry.mRemoveContentMode));
+                    out.attributeInt(null, "removeContentMode", settingsEntry.mRemoveContentMode);
                 }
                 if (settingsEntry.mShouldShowWithInsecureKeyguard != null) {
                     out.attribute(null, "shouldShowWithInsecureKeyguard",
diff --git a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
index cdc14cd..92baadf 100644
--- a/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
+++ b/services/core/java/com/android/server/wm/HighRefreshRateDenylist.java
@@ -27,7 +27,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
index 4793e1b..b0afc57 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ImpressionAttestationController.java
@@ -18,6 +18,9 @@
 
 import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
 
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -29,7 +32,9 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.hardware.HardwareBuffer;
 import android.os.Binder;
 import android.os.Bundle;
@@ -43,6 +48,7 @@
 import android.service.attestation.ImpressionAttestationService;
 import android.service.attestation.ImpressionToken;
 import android.util.Slog;
+import android.view.MagnificationSpec;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -59,7 +65,8 @@
  * blocking calls into another service.
  */
 public class ImpressionAttestationController {
-    private static final String TAG = "ImpressionAttestationController";
+    private static final String TAG =
+            TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
     private static final boolean DEBUG = false;
 
     private final Object mServiceConnectionLock = new Object();
@@ -81,6 +88,10 @@
 
     private final String mSalt;
 
+    private final float[] mTmpFloat9 = new float[9];
+    private final Matrix mTmpMatrix = new Matrix();
+    private final RectF mTmpRectF = new RectF();
+
     private interface Command {
         void run(IImpressionAttestationService service) throws RemoteException;
     }
@@ -151,6 +162,79 @@
     }
 
     /**
+     * Calculate the bounds to take the screenshot when generating the impression token. This takes
+     * into account window transform, magnification, and display bounds.
+     *
+     * Call while holding {@link WindowManagerService#mGlobalLock}
+     *
+     * @param win Window that the impression token is generated for.
+     * @param boundsInWindow The bounds passed in about where in the window to take the screenshot.
+     * @param outBounds The result of the calculated bounds
+     */
+    void calculateImpressionTokenBoundsLocked(WindowState win, Rect boundsInWindow,
+            Rect outBounds) {
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+        }
+        outBounds.set(boundsInWindow);
+
+        DisplayContent displayContent = win.getDisplayContent();
+        if (displayContent == null) {
+            return;
+        }
+
+        // Intersect boundsInWindow with the window to make sure it's not outside the window
+        // requesting the token. Offset the window bounds to 0,0 since the boundsInWindow are
+        // offset from the window location, not display.
+        final Rect windowBounds = new Rect();
+        win.getBounds(windowBounds);
+        windowBounds.offsetTo(0, 0);
+        outBounds.intersectUnchecked(windowBounds);
+
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+        }
+
+        if (outBounds.isEmpty()) {
+            return;
+        }
+
+        // Transform the bounds using the window transform in case there's a scale or offset.
+        // This allows the bounds to be in display space.
+        win.getTransformationMatrix(mTmpFloat9, mTmpMatrix);
+        mTmpRectF.set(outBounds);
+        mTmpMatrix.mapRect(mTmpRectF, mTmpRectF);
+        outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
+                (int) mTmpRectF.bottom);
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+        }
+
+        // Apply the magnification spec values to the bounds since the content could be magnified
+        final MagnificationSpec magSpec = displayContent.getMagnificationSpec();
+        if (magSpec != null) {
+            outBounds.scale(magSpec.scale);
+            outBounds.offset((int) magSpec.offsetX, (int) magSpec.offsetY);
+        }
+
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+        }
+
+        if (outBounds.isEmpty()) {
+            return;
+        }
+
+        // Intersect with the display bounds since it shouldn't take a screenshot of content
+        // outside the display since it's not visible to the user.
+        final Rect displayBounds = displayContent.getBounds();
+        outBounds.intersectUnchecked(displayBounds);
+        if (DEBUG) {
+            Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+        }
+    }
+
+    /**
      * Run a command, starting the service connection if necessary.
      */
     private void connectAndRun(@NonNull Command command) {
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b49d83d..25d779f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -40,6 +40,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -655,6 +656,7 @@
                 || type == TYPE_SECURE_SYSTEM_OVERLAY
                 || type == TYPE_DOCK_DIVIDER
                 || type == TYPE_ACCESSIBILITY_OVERLAY
-                || type == TYPE_INPUT_CONSUMER;
+                || type == TYPE_INPUT_CONSUMER
+                || type == TYPE_VOICE_INTERACTION;
     }
 }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 9d70fd7..752d6b4 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -496,10 +496,7 @@
     }
 
     void notifyInsetsChanged() {
-        mDisplayContent.forAllWindows(mDispatchInsetsChanged, true /* traverseTopToBottom */);
-        if (mDisplayContent.mRemoteInsetsControlTarget != null) {
-            mDisplayContent.mRemoteInsetsControlTarget.notifyInsetsChanged();
-        }
+        mDisplayContent.notifyInsetsChanged(mDispatchInsetsChanged);
     }
 
     void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/LaunchParamsPersister.java b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
index 2f8cfdd..391e659 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsPersister.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsPersister.java
@@ -27,26 +27,24 @@
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.DisplayInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.server.LocalServices;
 import com.android.server.pm.PackageList;
 import com.android.server.wm.LaunchParamsController.LaunchParams;
 
-import libcore.io.IoUtils;
-
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
 
-import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -195,12 +193,9 @@
                 continue;
             }
 
-            BufferedReader reader = null;
-            try {
-                reader = new BufferedReader(new FileReader(paramsFile));
+            try (InputStream in = new FileInputStream(paramsFile)) {
                 final PersistableLaunchParams params = new PersistableLaunchParams();
-                final XmlPullParser parser = Xml.newPullParser();
-                parser.setInput(reader);
+                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
                 int event;
                 while ((event = parser.next()) != XmlPullParser.END_DOCUMENT
                         && event != XmlPullParser.END_TAG) {
@@ -223,8 +218,6 @@
             } catch (Exception e) {
                 Slog.w(TAG, "Failed to restore launch params for " + name, e);
                 filesToDelete.add(paramsFile);
-            } finally {
-                IoUtils.closeQuietly(reader);
             }
         }
 
@@ -410,12 +403,11 @@
             mLaunchParams = launchParams;
         }
 
-        private StringWriter saveParamsToXml() {
-            final StringWriter writer = new StringWriter();
-            final XmlSerializer serializer = new FastXmlSerializer();
-
+        private byte[] saveParamsToXml() {
             try {
-                serializer.setOutput(writer);
+                final ByteArrayOutputStream os = new ByteArrayOutputStream();
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(os);
+
                 serializer.startDocument(/* encoding */ null, /* standalone */ true);
                 serializer.startTag(null, TAG_LAUNCH_PARAMS);
 
@@ -425,7 +417,7 @@
                 serializer.endDocument();
                 serializer.flush();
 
-                return writer;
+                return os.toByteArray();
             } catch (IOException e) {
                 return null;
             }
@@ -433,7 +425,7 @@
 
         @Override
         public void process() {
-            final StringWriter writer = saveParamsToXml();
+            final byte[] data = saveParamsToXml();
 
             final File launchParamFolder = getLaunchParamFolder(mUserId);
             if (!launchParamFolder.isDirectory() && !launchParamFolder.mkdirs()) {
@@ -447,7 +439,7 @@
             FileOutputStream stream = null;
             try {
                 stream = atomicFile.startWrite();
-                stream.write(writer.toString().getBytes());
+                stream.write(data);
             } catch (Exception e) {
                 Slog.e(TAG, "Failed to write param file for " + mComponentName, e);
                 if (stream != null) {
@@ -513,17 +505,16 @@
          */
         long mTimestamp;
 
-        void saveToXml(XmlSerializer serializer) throws IOException {
+        void saveToXml(TypedXmlSerializer serializer) throws IOException {
             serializer.attribute(null, ATTR_DISPLAY_UNIQUE_ID, mDisplayUniqueId);
-            serializer.attribute(null, ATTR_WINDOWING_MODE,
-                    Integer.toString(mWindowingMode));
+            serializer.attributeInt(null, ATTR_WINDOWING_MODE, mWindowingMode);
             serializer.attribute(null, ATTR_BOUNDS, mBounds.flattenToString());
             if (mWindowLayoutAffinity != null) {
                 serializer.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
             }
         }
 
-        void restore(File xmlFile, XmlPullParser parser) {
+        void restore(File xmlFile, TypedXmlPullParser parser) {
             for (int i = 0; i < parser.getAttributeCount(); ++i) {
                 final String attrValue = parser.getAttributeValue(i);
                 switch (parser.getAttributeName(i)) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 7073548..cbeaecf 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1804,10 +1804,7 @@
         Configuration config = null;
         if (displayContent != null) {
             config = displayContent.updateOrientation(
-                    getDisplayOverrideConfiguration(displayId),
-                    starting != null && starting.mayFreezeScreenLocked()
-                            ? starting.appToken : null,
-                    true /* forceUpdate */);
+                    getDisplayOverrideConfiguration(displayId), starting, true /* forceUpdate */);
         }
         // Visibilities may change so let the starting activity have a chance to report. Can't do it
         // when visibility is changed in each AppWindowToken because it may trigger wrong
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b46e796..c414c64 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -38,7 +38,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.annotation.Nullable;
-import android.app.ActivityManagerInternal;
 import android.app.PendingIntent;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -56,6 +55,7 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.service.attestation.ImpressionToken;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
@@ -848,4 +848,15 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
+
+    @Override
+    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+            String hashAlgorithm) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ebf5989..fb441fa 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -196,6 +196,8 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayInfo;
 import android.view.RemoteAnimationAdapter;
@@ -2249,7 +2251,7 @@
             // the rotation animation needs to capture snapshot earlier to avoid animating from
             // an intermediate state.
             if (oldOrientation != getOrientation()) {
-                onDescendantOrientationChanged(null, this);
+                onDescendantOrientationChanged(this);
             }
         } finally {
             if (pipChanging) {
@@ -3305,9 +3307,8 @@
     }
 
     @Override
-    public boolean onDescendantOrientationChanged(IBinder freezeDisplayToken,
-            WindowContainer requestingContainer) {
-        if (super.onDescendantOrientationChanged(freezeDisplayToken, requestingContainer)) {
+    public boolean onDescendantOrientationChanged(WindowContainer requestingContainer) {
+        if (super.onDescendantOrientationChanged(requestingContainer)) {
             return true;
         }
 
@@ -4099,6 +4100,7 @@
                 ? rootTask.mTaskId
                 : INVALID_TASK_ID;
         info.isFocused = isFocused();
+        info.isVisible = hasVisibleChildren();
     }
 
     @Nullable PictureInPictureParams getPictureInPictureParams() {
@@ -4483,14 +4485,14 @@
     /**
      * Saves this {@link Task} to XML using given serializer.
      */
-    void saveToXml(XmlSerializer out) throws Exception {
+    void saveToXml(TypedXmlSerializer out) throws Exception {
         if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
 
-        out.attribute(null, ATTR_TASKID, String.valueOf(mTaskId));
+        out.attributeInt(null, ATTR_TASKID, mTaskId);
         if (realActivity != null) {
             out.attribute(null, ATTR_REALACTIVITY, realActivity.flattenToShortString());
         }
-        out.attribute(null, ATTR_REALACTIVITY_SUSPENDED, String.valueOf(realActivitySuspended));
+        out.attributeBoolean(null, ATTR_REALACTIVITY_SUSPENDED, realActivitySuspended);
         if (origActivity != null) {
             out.attribute(null, ATTR_ORIGACTIVITY, origActivity.flattenToShortString());
         }
@@ -4509,37 +4511,36 @@
         if (mWindowLayoutAffinity != null) {
             out.attribute(null, ATTR_WINDOW_LAYOUT_AFFINITY, mWindowLayoutAffinity);
         }
-        out.attribute(null, ATTR_ROOTHASRESET, String.valueOf(rootWasReset));
-        out.attribute(null, ATTR_AUTOREMOVERECENTS, String.valueOf(autoRemoveRecents));
-        out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
-        out.attribute(null, ATTR_USERID, String.valueOf(mUserId));
-        out.attribute(null, ATTR_USER_SETUP_COMPLETE, String.valueOf(mUserSetupComplete));
-        out.attribute(null, ATTR_EFFECTIVE_UID, String.valueOf(effectiveUid));
-        out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
-        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
+        out.attributeBoolean(null, ATTR_ROOTHASRESET, rootWasReset);
+        out.attributeBoolean(null, ATTR_AUTOREMOVERECENTS, autoRemoveRecents);
+        out.attributeBoolean(null, ATTR_ASKEDCOMPATMODE, askedCompatMode);
+        out.attributeInt(null, ATTR_USERID, mUserId);
+        out.attributeBoolean(null, ATTR_USER_SETUP_COMPLETE, mUserSetupComplete);
+        out.attributeInt(null, ATTR_EFFECTIVE_UID, effectiveUid);
+        out.attributeLong(null, ATTR_LASTTIMEMOVED, mLastTimeMoved);
+        out.attributeBoolean(null, ATTR_NEVERRELINQUISH, mNeverRelinquishIdentity);
         if (lastDescription != null) {
             out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
         }
         if (getTaskDescription() != null) {
             getTaskDescription().saveToXml(out);
         }
-        out.attribute(null, ATTR_TASK_AFFILIATION, String.valueOf(mAffiliatedTaskId));
-        out.attribute(null, ATTR_PREV_AFFILIATION, String.valueOf(mPrevAffiliateTaskId));
-        out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
-        out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
+        out.attributeInt(null, ATTR_TASK_AFFILIATION, mAffiliatedTaskId);
+        out.attributeInt(null, ATTR_PREV_AFFILIATION, mPrevAffiliateTaskId);
+        out.attributeInt(null, ATTR_NEXT_AFFILIATION, mNextAffiliateTaskId);
+        out.attributeInt(null, ATTR_CALLING_UID, mCallingUid);
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
         out.attribute(null, ATTR_CALLING_FEATURE_ID,
                 mCallingFeatureId == null ? "" : mCallingFeatureId);
-        out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
-        out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
-                String.valueOf(mSupportsPictureInPicture));
+        out.attributeInt(null, ATTR_RESIZE_MODE, mResizeMode);
+        out.attributeBoolean(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE, mSupportsPictureInPicture);
         if (mLastNonFullscreenBounds != null) {
             out.attribute(
                     null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString());
         }
-        out.attribute(null, ATTR_MIN_WIDTH, String.valueOf(mMinWidth));
-        out.attribute(null, ATTR_MIN_HEIGHT, String.valueOf(mMinHeight));
-        out.attribute(null, ATTR_PERSIST_TASK_VERSION, String.valueOf(PERSIST_TASK_VERSION));
+        out.attributeInt(null, ATTR_MIN_WIDTH, mMinWidth);
+        out.attributeInt(null, ATTR_MIN_HEIGHT, mMinHeight);
+        out.attributeInt(null, ATTR_PERSIST_TASK_VERSION, PERSIST_TASK_VERSION);
 
         if (affinityIntent != null) {
             out.startTag(null, TAG_AFFINITYINTENT);
@@ -4564,7 +4565,7 @@
     }
 
     private static boolean saveActivityToXml(
-            ActivityRecord r, ActivityRecord first, XmlSerializer out) {
+            ActivityRecord r, ActivityRecord first, TypedXmlSerializer out) {
         if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable()
                 || ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
                 | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT)
@@ -4583,7 +4584,7 @@
         }
     }
 
-    static Task restoreFromXml(XmlPullParser in, ActivityTaskSupervisor taskSupervisor)
+    static Task restoreFromXml(TypedXmlPullParser in, ActivityTaskSupervisor taskSupervisor)
             throws IOException, XmlPullParserException {
         Intent intent = null;
         Intent affinityIntent = null;
@@ -5764,6 +5765,10 @@
                     starting, configChanges, preserveWindows, notifyClients, userLeaving),
                     true /* traverseTopToBottom */);
 
+            // Notify WM shell that task visibilities may have changed
+            forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
+                    true /* traverseTopToBottom */);
+
             if (mTranslucentActivityWaiting != null &&
                     mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
                 // Nothing is getting drawn or everything was already visible, don't wait for
diff --git a/services/core/java/com/android/server/wm/TaskPersister.java b/services/core/java/com/android/server/wm/TaskPersister.java
index c976bc2..855dd7e 100644
--- a/services/core/java/com/android/server/wm/TaskPersister.java
+++ b/services/core/java/com/android/server/wm/TaskPersister.java
@@ -30,26 +30,28 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -305,12 +307,9 @@
                 continue;
             }
 
-            BufferedReader reader = null;
             boolean deleteFile = false;
-            try {
-                reader = new BufferedReader(new FileReader(taskFile));
-                final XmlPullParser in = Xml.newPullParser();
-                in.setInput(reader);
+            try (InputStream is = new FileInputStream(taskFile)) {
+                final TypedXmlPullParser in = Xml.resolvePullParser(is);
 
                 int event;
                 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
@@ -360,7 +359,6 @@
                 Slog.e(TAG, "Failing file: " + fileToString(taskFile));
                 deleteFile = true;
             } finally {
-                IoUtils.closeQuietly(reader);
                 if (deleteFile) {
                     if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
@@ -513,11 +511,10 @@
             mService = service;
         }
 
-        private StringWriter saveToXml(Task task) throws Exception {
+        private byte[] saveToXml(Task task) throws Exception {
             if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
-            final XmlSerializer xmlSerializer = new FastXmlSerializer();
-            StringWriter stringWriter = new StringWriter();
-            xmlSerializer.setOutput(stringWriter);
+            final ByteArrayOutputStream os = new ByteArrayOutputStream();
+            final TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(os);
 
             if (DEBUG) {
                 xmlSerializer.setFeature(
@@ -534,13 +531,13 @@
             xmlSerializer.endDocument();
             xmlSerializer.flush();
 
-            return stringWriter;
+            return os.toByteArray();
         }
 
         @Override
         public void process() {
             // Write out one task.
-            StringWriter stringWriter = null;
+            byte[] data = null;
             Task task = mTask;
             if (DEBUG) Slog.d(TAG, "Writing task=" + task);
             synchronized (mService.mGlobalLock) {
@@ -548,12 +545,12 @@
                     // Still there.
                     try {
                         if (DEBUG) Slog.d(TAG, "Saving task=" + task);
-                        stringWriter = saveToXml(task);
+                        data = saveToXml(task);
                     } catch (Exception e) {
                     }
                 }
             }
-            if (stringWriter != null) {
+            if (data != null) {
                 // Write out xml file while not holding mService lock.
                 FileOutputStream file = null;
                 AtomicFile atomicFile = null;
@@ -567,8 +564,7 @@
                     atomicFile = new AtomicFile(new File(userTasksDir,
                             String.valueOf(task.mTaskId) + TASK_FILENAME_SUFFIX));
                     file = atomicFile.startWrite();
-                    file.write(stringWriter.toString().getBytes());
-                    file.write('\n');
+                    file.write(data);
                     atomicFile.finishWrite(file);
                 } catch (IOException e) {
                     if (file != null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a1bb89d..cf6468d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1139,18 +1139,15 @@
      * Called when this container or one of its descendants changed its requested orientation, and
      * wants this container to handle it or pass it to its parent.
      *
-     * @param freezeDisplayToken freeze this app window token if display needs to freeze
      * @param requestingContainer the container which orientation request has changed
      * @return {@code true} if handled; {@code false} otherwise.
      */
-    boolean onDescendantOrientationChanged(@Nullable IBinder freezeDisplayToken,
-            @Nullable WindowContainer requestingContainer) {
+    boolean onDescendantOrientationChanged(@Nullable WindowContainer requestingContainer) {
         final WindowContainer parent = getParent();
         if (parent == null) {
             return false;
         }
-        return parent.onDescendantOrientationChanged(freezeDisplayToken,
-                requestingContainer);
+        return parent.onDescendantOrientationChanged(requestingContainer);
     }
 
     /**
@@ -1224,14 +1221,13 @@
     }
 
     /**
-     * Calls {@link #setOrientation(int, IBinder, ActivityRecord)} with {@code null} to the last 2
+     * Calls {@link #setOrientation(int, IBinder, WindowContainer)} with {@code null} to the last 2
      * parameters.
      *
      * @param orientation the specified orientation.
      */
     void setOrientation(int orientation) {
-        setOrientation(orientation, null /* freezeDisplayToken */,
-                null /* ActivityRecord */);
+        setOrientation(orientation, null /* requestingContainer */);
     }
 
     /**
@@ -1240,14 +1236,10 @@
      *
      * @param orientation the specified orientation. Needs to be one of {@link
      *      android.content.pm.ActivityInfo.ScreenOrientation}.
-     * @param freezeDisplayToken uses this token to freeze display if orientation change is not
-     *                           done. Display will not be frozen if this is {@code null}, which
-     *                           should only happen in tests.
      * @param requestingContainer the container which orientation request has changed. Mostly used
      *                            to ensure it gets correct configuration.
      */
-    void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
-            @Nullable WindowContainer requestingContainer) {
+    void setOrientation(int orientation, @Nullable WindowContainer requestingContainer) {
         if (mOrientation == orientation) {
             return;
         }
@@ -1259,7 +1251,7 @@
                 // Resolve the requested orientation.
                 onConfigurationChanged(parent.getConfiguration());
             }
-            onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
+            onDescendantOrientationChanged(requestingContainer);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index b0c5dbc..a5ebf9a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -23,7 +23,7 @@
 import android.provider.DeviceConfig;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.utils.DeviceConfigInterface;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.io.PrintWriter;
 import java.util.Objects;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 80b0216..25049ae 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -48,6 +48,7 @@
 import static android.provider.Settings.Global.DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -78,7 +79,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED;
-import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManagerGlobal.ADD_OKAY;
@@ -199,6 +199,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
+import android.service.attestation.ImpressionToken;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.sysprop.SurfaceFlingerProperties;
@@ -256,9 +257,9 @@
 import android.view.WindowContentFrameStats;
 import android.view.WindowInsets;
 import android.view.WindowManager;
+import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.RemoveContentMode;
-import android.view.WindowManager.DisplayImePolicy;
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 import android.window.ClientWindowFrames;
@@ -288,8 +289,8 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.power.ShutdownThread;
+import com.android.server.utils.DeviceConfigInterface;
 import com.android.server.utils.PriorityDump;
-import com.android.server.wm.utils.DeviceConfigInterface;
 
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
@@ -767,6 +768,8 @@
     final EmbeddedWindowController mEmbeddedWindowController;
     final AnrController mAnrController;
 
+    private final ImpressionAttestationController mImpressionAttestationController;
+
     @VisibleForTesting
     final class SettingsObserver extends ContentObserver {
         private final Uri mDisplayInversionEnabledUri =
@@ -1367,6 +1370,7 @@
         mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                 mContext.getResources());
 
+        mImpressionAttestationController = new ImpressionAttestationController(mContext);
         setGlobalShadowSettings();
         mAnrController = new AnrController(this);
         mStartingSurfaceController = new StartingSurfaceController(this);
@@ -1627,7 +1631,7 @@
             }
 
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
-            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
+            displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
             win.updateRequestedVisibility(requestedVisibility);
 
             res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
@@ -2197,7 +2201,7 @@
             int flagChanges = 0;
             int privateFlagChanges = 0;
             if (attrs != null) {
-                displayPolicy.adjustWindowParamsLw(win, attrs, pid, uid);
+                displayPolicy.adjustWindowParamsLw(win, attrs);
                 win.mToken.adjustWindowParams(win, attrs);
                 int disableFlags =
                         (attrs.systemUiVisibility | attrs.subtreeSystemUiVisibility) & DISABLE_MASK;
@@ -8428,4 +8432,64 @@
             SystemClock.sleep(durationMs);
         }
     }
+
+    @Override
+    public String[] getSupportedImpressionAlgorithms() {
+        return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+    }
+
+    @Override
+    public boolean verifyImpressionToken(ImpressionToken impressionToken) {
+        return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+    }
+
+    ImpressionToken generateImpressionToken(Session session, IWindow window,
+            Rect boundsInWindow, String hashAlgorithm) {
+        final SurfaceControl displaySurfaceControl;
+        final Rect boundsInDisplay = new Rect(boundsInWindow);
+        synchronized (mGlobalLock) {
+            final WindowState win = windowForClientLocked(session, window, false);
+            if (win == null) {
+                Slog.w(TAG, "Failed to generate impression token. Invalid window");
+                return null;
+            }
+
+            DisplayContent displayContent = win.getDisplayContent();
+            if (displayContent == null) {
+                Slog.w(TAG, "Failed to generate impression token. Window is not on a display");
+                return null;
+            }
+
+            displaySurfaceControl = displayContent.getSurfaceControl();
+            mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+                    boundsInWindow, boundsInDisplay);
+
+            if (boundsInDisplay.isEmpty()) {
+                Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+                return null;
+            }
+        }
+
+        // A screenshot of the entire display is taken rather than just the window. This is
+        // because if we take a screenshot of the window, it will not include content that might
+        // be covering it with the same uid. We want to make sure we include content that's
+        // covering to ensure we get as close as possible to what the user sees
+        final int uid = session.mUid;
+        SurfaceControl.LayerCaptureArgs args =
+                new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
+                        .setUid(uid)
+                        .setSourceCrop(boundsInDisplay)
+                        .build();
+
+        SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+                SurfaceControl.captureLayers(args);
+        if (screenshotHardwareBuffer == null
+                || screenshotHardwareBuffer.getHardwareBuffer() == null) {
+            Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+            return null;
+        }
+
+        return mImpressionAttestationController.generateImpressionToken(
+                screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index d29534e..1a147b9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -98,6 +98,8 @@
     private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
     private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
     private static final String TAG_PASSWORD_QUALITY = "password-quality";
+    private static final String TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT =
+            "password-quality-applies-parent";
     private static final String TAG_POLICIES = "policies";
     private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
             "cross-profile-widget-providers";
@@ -143,6 +145,7 @@
 
     @NonNull
     PasswordPolicy mPasswordPolicy = new PasswordPolicy();
+    boolean mPasswordPolicyAppliesToParent = true;
 
     @DevicePolicyManager.PasswordComplexity
     int mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
@@ -333,6 +336,9 @@
                 writeAttributeValueToXml(
                         out, TAG_MIN_PASSWORD_NONLETTER, mPasswordPolicy.nonLetter);
             }
+
+            writeAttributeValueToXml(out, TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT,
+                    mPasswordPolicyAppliesToParent);
         }
         if (passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) {
             writeAttributeValueToXml(
@@ -626,6 +632,9 @@
             } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
                 mPasswordPolicy.nonLetter = Integer.parseInt(
                         parser.getAttributeValue(null, ATTR_VALUE));
+            } else if (TAG_PASSWORD_QUALITY_APPLIES_TO_PARENT.equals(tag)) {
+                mPasswordPolicyAppliesToParent = Boolean.parseBoolean(
+                        parser.getAttributeValue(null, ATTR_VALUE));
             } else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
                 maximumTimeToUnlock = Long.parseLong(
                         parser.getAttributeValue(null, ATTR_VALUE));
@@ -995,6 +1004,9 @@
         pw.print("minimumPasswordNonLetter=");
         pw.println(mPasswordPolicy.nonLetter);
 
+        pw.print("passwordPolicyAppliesToParent=");
+        pw.println(mPasswordPolicyAppliesToParent);
+
         pw.print("maximumTimeToUnlock=");
         pw.println(maximumTimeToUnlock);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index ce61d50..05b1e425 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -101,4 +101,9 @@
     public boolean canProfileOwnerResetPasswordWhenLocked(int userId) {
         return false;
     }
+
+    public boolean hasKeyPair(String callerPackage, String alias) {
+        // STOPSHIP: implement delegation code in ArcDevicePolicyManagerWrapperService & nuke this.
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6ca27b5..83b4c82 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -155,10 +155,12 @@
 import android.app.admin.SystemUpdatePolicy;
 import android.app.admin.UnsafeStateException;
 import android.app.backup.IBackupManager;
+import android.app.compat.CompatChanges;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -527,6 +529,19 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long USE_SET_LOCATION_ENABLED = 117835097L;
 
+    /**
+     * Admin apps targeting Android S+ may not use
+     * {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
+     * on the {@code DevicePolicyManager} instance obtained by calling
+     * {@link android.app.admin.DevicePolicyManager#getParentProfileInstance}.
+     * Instead, they should use
+     * {@link android.app.admin.DevicePolicyManager#setRequiredPasswordComplexity} to set
+     * coarse-grained password requirements device-wide.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
+    private static final long PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT = 165573442L;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -1396,6 +1411,10 @@
         public long systemCurrentTimeMillis() {
             return System.currentTimeMillis();
         }
+
+        public boolean isChangeEnabled(long changeId, String packageName, int userId) {
+            return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId));
+        }
     }
 
     /**
@@ -3315,6 +3334,11 @@
                 getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
     }
 
+    private boolean canSetPasswordQualityOnParent(String packageName, int userId) {
+        return !mInjector.isChangeEnabled(
+                PREVENT_SETTING_PASSWORD_QUALITY_ON_PARENT, packageName, userId);
+    }
+
     @Override
     public void setPasswordQuality(ComponentName who, int quality, boolean parent) {
         if (!mHasFeature) {
@@ -3323,7 +3347,18 @@
         Objects.requireNonNull(who, "ComponentName is null");
         validateQualityConstant(quality);
 
-        final int userId = mInjector.userHandleGetCallingUserId();
+        final CallerIdentity caller = getCallerIdentity(who);
+        Preconditions.checkCallAuthorization(
+                isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller));
+
+        final boolean qualityMayApplyToParent =
+                canSetPasswordQualityOnParent(who.getPackageName(), caller.getUserId());
+        if (!qualityMayApplyToParent) {
+            Preconditions.checkArgument(!parent,
+                    "Profile Owner may not apply password quality requirements device-wide");
+        }
+
+        final int userId = caller.getUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(
                     who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, parent);
@@ -3332,6 +3367,7 @@
                 if (passwordPolicy.quality != quality) {
                     passwordPolicy.quality = quality;
                     ap.mPasswordComplexity = PASSWORD_COMPLEXITY_NONE;
+                    ap.mPasswordPolicyAppliesToParent = qualityMayApplyToParent;
                     resetInactivePasswordRequirementsIfRPlus(userId, ap);
                     updatePasswordValidityCheckpointLocked(userId, parent);
                     updatePasswordQualityCacheForUserGroup(userId);
@@ -4063,7 +4099,16 @@
         synchronized (getLockObject()) {
             List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userId);
             for (ActiveAdmin admin : admins) {
-                adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+                final boolean isAdminOfUser = userId == admin.getUserHandle().getIdentifier();
+                // Use the password metrics from the admin in one of three cases:
+                // (1) The admin is of the user we're getting the minimum metrics for. The admin
+                //     always affects the user it's managing. This applies also to the parent
+                //     ActiveAdmin instance: It'd have the same user handle.
+                // (2) The mPasswordPolicyAppliesToParent field is true: That indicates the
+                //     call to setPasswordQuality was made by an admin that may affect the parent.
+                if (isAdminOfUser || admin.mPasswordPolicyAppliesToParent) {
+                    adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+                }
             }
         }
         return PasswordMetrics.merge(adminMetrics);
@@ -4155,13 +4200,13 @@
                     /* shouldIncludeProfileAdmins */ (user) -> user.id == profileUser
                     || !mLockPatternUtils.isSeparateProfileChallengeEnabled(user.id));
             ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>(admins.size());
+            int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
             for (ActiveAdmin admin : admins) {
                 adminMetrics.add(admin.mPasswordPolicy.getMinMetrics());
+                maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
             }
-            //TODO: Take complexity into account, would need to take complexity from all admins
-            //in the admins list.
             return PasswordMetrics.validatePasswordMetrics(PasswordMetrics.merge(adminMetrics),
-                    PASSWORD_COMPLEXITY_NONE, false, metrics).isEmpty();
+                    maxRequiredComplexity, false, metrics).isEmpty();
         }
     }
 
@@ -4255,28 +4300,33 @@
                     admin.mPasswordComplexity = passwordComplexity;
                     // Reset the password policy.
                     admin.mPasswordPolicy = new PasswordPolicy();
+                    admin.mPasswordPolicyAppliesToParent = true;
                     updatePasswordValidityCheckpointLocked(caller.getUserId(), calledOnParent);
                     updatePasswordQualityCacheForUserGroup(caller.getUserId());
                     saveSettingsLocked(caller.getUserId());
-                    //TODO: Log password complexity change if security logging is enabled.
                 });
             }
+            logPasswordComplexityRequiredIfSecurityLogEnabled(admin.info.getComponent(),
+                    caller.getUserId(), calledOnParent, passwordComplexity);
         }
         //TODO: Log metrics.
     }
 
+    private void logPasswordComplexityRequiredIfSecurityLogEnabled(ComponentName who, int userId,
+            boolean parent, int complexity) {
+        if (SecurityLog.isLoggingEnabled()) {
+            final int affectedUserId = parent ? getProfileParentId(userId) : userId;
+            SecurityLog.writeEvent(SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED,
+                    who.getPackageName(), userId, affectedUserId, complexity);
+        }
+    }
+
     private int getEffectivePasswordComplexityRequirementLocked(@UserIdInt int userHandle) {
         ensureLocked();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(userHandle);
         int maxRequiredComplexity = PASSWORD_COMPLEXITY_NONE;
         for (ActiveAdmin admin : admins) {
-            final ComponentName adminComponent = admin.info.getComponent();
-            final int adminUser = admin.getUserHandle().getIdentifier();
-            // Password complexity is only taken into account from DO/PO
-            if (isDeviceOwner(adminComponent, adminUser)
-                    || isProfileOwnerUncheckedLocked(adminComponent, adminUser)) {
-                maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
-            }
+            maxRequiredComplexity = Math.max(maxRequiredComplexity, admin.mPasswordComplexity);
         }
         return maxRequiredComplexity;
     }
@@ -4301,6 +4351,21 @@
     }
 
     @Override
+    public int getAggregatedPasswordComplexityForUser(int userId) {
+        if (!mHasFeature) {
+            return PASSWORD_COMPLEXITY_NONE;
+        }
+
+        final CallerIdentity caller = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userId));
+
+        synchronized (getLockObject()) {
+            return getEffectivePasswordComplexityRequirementLocked(userId);
+        }
+    }
+
+
+    @Override
     public int getCurrentFailedPasswordAttempts(int userHandle, boolean parent) {
         if (!mLockPatternUtils.hasSecureLockScreen()) {
             return 0;
@@ -5046,6 +5111,30 @@
     }
 
     @Override
+    public boolean hasKeyPair(String callerPackage, String alias) {
+        final CallerIdentity caller = getCallerIdentity(callerPackage);
+        Preconditions.checkCallAuthorization(canManageCertificates(caller));
+
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            try (KeyChainConnection keyChainConnection =
+                         KeyChain.bindAsUser(mContext, caller.getUserHandle())) {
+                return keyChainConnection.getService().containsKeyPair(alias);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Querying keypair", e);
+            } catch (InterruptedException e) {
+                Log.w(LOG_TAG, "Interrupted while querying keypair", e);
+                Thread.currentThread().interrupt();
+            }
+            return false;
+        });
+    }
+
+    private boolean canManageCertificates(CallerIdentity caller) {
+        return isProfileOwner(caller) || isDeviceOwner(caller)
+                || isCallerDelegate(caller, DELEGATION_CERT_INSTALL);
+    }
+
+    @Override
     public boolean setKeyGrantForApp(ComponentName who, String callerPackage, String alias,
             String packageName, boolean hasGrant) {
         Preconditions.checkStringNotEmpty(alias, "Alias to grant cannot be empty");
@@ -5123,9 +5212,7 @@
          */
         if (hasProfileOwner(caller.getUserId())) {
             // Make sure that the caller is the profile owner or delegate.
-            Preconditions.checkCallAuthorization(
-                    isDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate(
-                            caller, DELEGATION_CERT_INSTALL));
+            Preconditions.checkCallAuthorization(canManageCertificates(caller));
             // Verify that the managed profile is on an organization-owned device and as such
             // the profile owner can access Device IDs.
             if (isProfileOwnerOfOrganizationOwnedDevice(caller.getUserId())) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
index e48b671..9f895c6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUpgradeTest.java
@@ -35,6 +35,8 @@
 import android.os.HandlerThread;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -181,7 +183,7 @@
 
         boolean parse() {
             try (FileInputStream stream = new FileInputStream(mFile)) {
-                XmlPullParser parser = Xml.newPullParser();
+                TypedXmlPullParser parser = Xml.newFastPullParser();
                 parser.setInput(stream, StandardCharsets.UTF_8.name());
                 int type;
                 while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 0c2fab8..343b156 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -51,7 +51,6 @@
         "testng",
         "junit",
         "platform-compat-test-rules",
-
     ],
 
     aidl: {
@@ -117,6 +116,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
@@ -133,6 +133,7 @@
         "utils/**/*.java",
         "utils/**/*.kt",
         "utils-mockito/**/*.kt",
+        ":services.core-sources-deviceconfig-interface",
     ],
     static_libs: [
         "junit",
diff --git a/services/tests/servicestests/res/xml/usertypes_test_full.xml b/services/tests/servicestests/res/xml/usertypes_test_full.xml
index a281dca..099ccbe 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_full.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_full.xml
@@ -22,4 +22,7 @@
             <item res='@*android:color/profile_badge_1' />
         </badge-colors>
     </full-type>
+
+    <change-user-type from="android.old.name" to="android.test.1" whenVersionLeq="1" />
+
 </user-types>
diff --git a/services/tests/servicestests/res/xml/usertypes_test_profile.xml b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
index b6c8fbd..daa7d7b 100644
--- a/services/tests/servicestests/res/xml/usertypes_test_profile.xml
+++ b/services/tests/servicestests/res/xml/usertypes_test_profile.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<user-types>
+<user-types version="1234">
     <profile-type
         name='android.test.2'
         max-allowed-per-parent='12'
@@ -32,4 +32,7 @@
         <default-restrictions no_remove_user='true' no_bluetooth='true' />
     </profile-type>
     <profile-type name='custom.test.1' max-allowed-per-parent='14' />
+
+    <change-user-type from="android.test.1" to="android.test.2" whenVersionLeq="1233" />
+
 </user-types>
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 904e93b..e2b48d4 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -127,6 +127,8 @@
         // Used as an override when set to nonzero.
         private long mCurrentTimeMillis = 0;
 
+        private final Map<Long, Pair<String, Integer>> mEnabledChanges = new ArrayMap<>();
+
         public MockInjector(MockSystemServices services, DpmMockContext context) {
             super(context);
             this.services = services;
@@ -487,5 +489,33 @@
         public long systemCurrentTimeMillis() {
             return mCurrentTimeMillis != 0 ? mCurrentTimeMillis : System.currentTimeMillis();
         }
+
+        public void setChangeEnabledForPackage(
+                long changeId, boolean enabled, String packageName, int userId) {
+            if (enabled) {
+                mEnabledChanges.put(changeId, Pair.create(packageName, userId));
+            } else {
+                mEnabledChanges.remove(changeId);
+            }
+        }
+
+        public void clearEnabledChanges() {
+            mEnabledChanges.clear();
+        }
+
+        @Override
+        public boolean isChangeEnabled(long changeId, String packageName, int userId) {
+            Pair<String, Integer> packageAndUser = mEnabledChanges.get(changeId);
+            if (packageAndUser == null) {
+                return false;
+            }
+
+            if (!packageAndUser.first.equals(packageName)
+                    || !packageAndUser.second.equals(userId)) {
+                return false;
+            }
+
+            return true;
+        }
     }
 }
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 c0c82d5..77090a8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5090,11 +5090,11 @@
         doReturn(true).when(getServices().lockPatternUtils)
                 .isSeparateProfileChallengeEnabled(managedProfileUserId);
 
-        dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
-        parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
+        dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH);
+        parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM);
 
         when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM))
-                .thenReturn(computeForPassword("1234".getBytes()));
+                .thenReturn(computeForPassword("184342".getBytes()));
 
         // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly
         // on the parent admin)
@@ -5107,6 +5107,68 @@
         managedProfileUserId)).isFalse();
     }
 
+    @Test
+    public void testCanSetPasswordRequirementOnParentPreS() throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+        dpms.mMockInjector.setChangeEnabledForPackage(165573442L, false,
+                admin1.getPackageName(), managedProfileUserId);
+
+        parentDpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+        assertThat(parentDpm.getPasswordQuality(admin1))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+    }
+
+    @Test
+    public void testCannotSetPasswordRequirementOnParent() throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1);
+        dpms.mMockInjector.setChangeEnabledForPackage(165573442L, true,
+                admin1.getPackageName(), managedProfileUserId);
+
+        try {
+            assertExpectException(IllegalArgumentException.class, null, () ->
+                    parentDpm.setPasswordQuality(
+                            admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX));
+        } finally {
+            dpms.mMockInjector.clearEnabledChanges();
+        }
+    }
+
+    @Test
+    public void testPasswordQualityAppliesToParentPreS() throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+        when(getServices().userManager.getProfileParent(CALLER_USER_HANDLE))
+                .thenReturn(new UserInfo(UserHandle.USER_SYSTEM, "user system", 0));
+
+        dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+        assertThat(parentDpm.getPasswordQuality(null))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+    }
+
+    @Test
+    public void testPasswordQualityDoesNotApplyToParentPostS() throws Exception {
+        final int managedProfileUserId = CALLER_USER_HANDLE;
+        final int managedProfileAdminUid =
+                UserHandle.getUid(managedProfileUserId, DpmMockContext.SYSTEM_UID);
+        mContext.binder.callingUid = managedProfileAdminUid;
+        addManagedProfile(admin1, managedProfileAdminUid, admin1, VERSION_CODES.R);
+
+        dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX);
+        assertThat(parentDpm.getPasswordQuality(admin1))
+                .isEqualTo(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+    }
+
     private void setActivePasswordState(PasswordMetrics passwordMetrics)
             throws Exception {
         final int userHandle = UserHandle.getUserId(mContext.binder.callingUid);
@@ -7014,20 +7076,32 @@
      * @param adminUid uid of the admin package.
      * @param copyFromAdmin package information for {@code admin} will be built based on this
      *     component's information.
+     * @param appTargetSdk admin's target SDK level
      */
     private void addManagedProfile(
-            ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception {
+            ComponentName admin, int adminUid, ComponentName copyFromAdmin, int appTargetSdk)
+            throws Exception {
         final int userId = UserHandle.getUserId(adminUid);
         getServices().addUser(userId, 0, UserManager.USER_TYPE_PROFILE_MANAGED,
                 UserHandle.USER_SYSTEM);
         mContext.callerPermissions.addAll(OWNER_SETUP_PERMISSIONS);
-        setUpPackageManagerForFakeAdmin(admin, adminUid, copyFromAdmin);
+        setUpPackageManagerForFakeAdmin(admin, adminUid, /* enabledSetting= */ null,
+                appTargetSdk, copyFromAdmin);
         dpm.setActiveAdmin(admin, false, userId);
         assertThat(dpm.setProfileOwner(admin, null, userId)).isTrue();
         mContext.callerPermissions.removeAll(OWNER_SETUP_PERMISSIONS);
     }
 
     /**
+     * Same as {@code addManagedProfile} above, except using development API level as the API
+     * level of the admin.
+     */
+    private void addManagedProfile(
+            ComponentName admin, int adminUid, ComponentName copyFromAdmin) throws Exception {
+        addManagedProfile(admin, adminUid, copyFromAdmin, VERSION_CODES.CUR_DEVELOPMENT);
+    }
+
+    /**
      * Convert String[] to StringParceledListSlice.
      */
     private static StringParceledListSlice asSlice(String[] s) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
index 3aa5a80..d58d71f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -23,6 +23,8 @@
 
 import android.app.admin.FactoryResetProtectionPolicy;
 import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -98,7 +100,7 @@
             throws Exception {
         ByteArrayOutputStream outStream = serialize(policy);
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG);
 
@@ -109,7 +111,7 @@
             throws Exception {
         ByteArrayOutputStream outStream = serialize(policy);
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = mock(XmlPullParser.class);
+        TypedXmlPullParser parser = mock(TypedXmlPullParser.class);
         when(parser.next()).thenThrow(XmlPullParserException.class);
         parser.setInput(new InputStreamReader(inStream));
 
@@ -120,7 +122,7 @@
     private ByteArrayOutputStream serialize(FactoryResetProtectionPolicy policy)
             throws IOException {
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         outXml.startTag(null, TAG_FACTORY_RESET_PROTECTION_POLICY);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
index 0a9aad7..1308a3e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
@@ -29,6 +29,8 @@
 import android.app.admin.FreezePeriod;
 import android.app.admin.SystemUpdatePolicy;
 import android.os.Parcel;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -471,7 +473,7 @@
 
         // Test XML serialization
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         outXml.startTag(null, "ota");
@@ -481,7 +483,7 @@
         outXml.flush();
 
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         assertThat(parser.next()).isEqualTo(XmlPullParser.START_TAG);
         checkFreezePeriods(SystemUpdatePolicy.restoreFromXml(parser), expectedPeriods);
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 325ba11..cb5ca04 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -16,49 +16,97 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
+
+import static com.android.server.display.DisplayModeDirector.Vote.PRIORITY_FLICKER;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
+import android.annotation.NonNull;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 
-import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.util.test.FakeSettingsProviderRule;
 import com.android.server.display.DisplayModeDirector.BrightnessObserver;
 import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs;
 import com.android.server.display.DisplayModeDirector.Vote;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class DisplayModeDirectorTest {
     // The tolerance within which we consider something approximately equals.
+    private static final String TAG = "DisplayModeDirectorTest";
+    private static final boolean DEBUG = false;
     private static final float FLOAT_TOLERANCE = 0.01f;
 
     private Context mContext;
+    private FakesInjector mInjector;
+    private Handler mHandler;
+    @Rule
+    public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
 
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+        final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContext);
+        when(mContext.getContentResolver()).thenReturn(resolver);
+        mInjector = new FakesInjector();
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     private DisplayModeDirector createDirectorFromRefreshRateArray(
             float[] refreshRates, int baseModeId) {
         DisplayModeDirector director =
-                new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper()));
+                new DisplayModeDirector(mContext, mHandler, mInjector);
         int displayId = 0;
         Display.Mode[] modes = new Display.Mode[refreshRates.length];
         for (int i = 0; i < refreshRates.length; i++) {
@@ -159,9 +207,9 @@
     }
 
     @Test
-    public void testBrightnessHasLowerPriorityThanUser() {
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
-        assertTrue(Vote.PRIORITY_LOW_BRIGHTNESS < Vote.PRIORITY_APP_REQUEST_SIZE);
+    public void testFlickerHasLowerPriorityThanUser() {
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_REFRESH_RATE);
+        assertTrue(PRIORITY_FLICKER < Vote.PRIORITY_APP_REQUEST_SIZE);
 
         int displayId = 0;
         DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
@@ -169,7 +217,7 @@
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -177,7 +225,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -185,7 +233,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(90);
@@ -193,7 +241,7 @@
 
         votes.clear();
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(60, 60));
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(90, 90));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(90, 90));
         director.injectVotesByDisplay(votesByDisplay);
         desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -202,10 +250,10 @@
 
     @Test
     public void testAppRequestRefreshRateRange() {
-        // Confirm that the app request range doesn't include low brightness or min refresh rate
-        // settings, but does include everything else.
+        // Confirm that the app request range doesn't include flicker or min refresh rate settings,
+        // but does include everything else.
         assertTrue(
-                Vote.PRIORITY_LOW_BRIGHTNESS < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
+                PRIORITY_FLICKER < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE
                 < Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
         assertTrue(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE
@@ -216,7 +264,7 @@
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(60, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(60, 60));
         director.injectVotesByDisplay(votesByDisplay);
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.primaryRefreshRateRange.min).isWithin(FLOAT_TOLERANCE).of(60);
@@ -310,7 +358,7 @@
         SparseArray<Vote> votes = new SparseArray<>();
         SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
         votesByDisplay.put(displayId, votes);
-        votes.put(Vote.PRIORITY_LOW_BRIGHTNESS, Vote.forRefreshRates(0, 60));
+        votes.put(PRIORITY_FLICKER, Vote.forRefreshRates(0, 60));
         votes.put(Vote.PRIORITY_USER_SETTING_MIN_REFRESH_RATE, Vote.forRefreshRates(60, 90));
         votes.put(Vote.PRIORITY_APP_REQUEST_REFRESH_RATE, Vote.forRefreshRates(90, 90));
         votes.put(Vote.PRIORITY_USER_SETTING_PEAK_REFRESH_RATE, Vote.forRefreshRates(60, 60));
@@ -398,4 +446,343 @@
         DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(displayId);
         assertThat(desiredSpecs.allowGroupSwitching).isTrue();
     }
+
+    @Test
+    public void testBrightnessObserverGetsUpdatedRefreshRatesForZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int initialRefreshRate = 60;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(initialRefreshRate);
+        director.start(sensorManager);
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(initialRefreshRate);
+
+        final int updatedRefreshRate = 90;
+        mInjector.getDeviceConfig().setRefreshRateInLowZone(updatedRefreshRate);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getRefreshRateInLowZone())
+                .isEqualTo(updatedRefreshRate);
+    }
+
+    @Test
+    public void testBrightnessObserverThresholdsInZone() {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+        SensorManager sensorManager = createMockSensorManager(createLightSensor());
+
+        final int[] initialDisplayThresholds = { 10 };
+        final int[] initialAmbientThresholds = { 20 };
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setLowDisplayBrightnessThresholds(initialDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(initialAmbientThresholds);
+        director.start(sensorManager);
+
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(initialDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(initialAmbientThresholds);
+
+        final int[] updatedDisplayThresholds = { 9, 14 };
+        final int[] updatedAmbientThresholds = { -1, 19 };
+        config.setLowDisplayBrightnessThresholds(updatedDisplayThresholds);
+        config.setLowAmbientBrightnessThresholds(updatedAmbientThresholds);
+        // Need to wait for the property change to propagate to the main thread.
+        waitForIdleSync();
+        assertThat(director.getBrightnessObserver().getLowDisplayBrightnessThresholds())
+                .isEqualTo(updatedDisplayThresholds);
+        assertThat(director.getBrightnessObserver().getLowAmbientBrightnessThresholds())
+                .isEqualTo(updatedAmbientThresholds);
+    }
+
+    @Test
+    public void testLockFpsForLowZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInLowZone(90);
+        config.setLowDisplayBrightnessThresholds(new int[] { 10 });
+        config.setLowAmbientBrightnessThresholds(new int[] { 20 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(10);
+        // Sensor reads 20 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 20 /*lux*/));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 90 /*fps*/);
+
+        setBrightness(125);
+        // Sensor reads 1000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 1000 /*lux*/));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+    }
+
+    @Test
+    public void testLockFpsForHighZone() throws Exception {
+        DisplayModeDirector director =
+                createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
+        setPeakRefreshRate(90 /*fps*/);
+        director.getSettingsObserver().setDefaultRefreshRate(90);
+        director.getBrightnessObserver().setDefaultDisplayState(true);
+
+        final FakeDeviceConfig config = mInjector.getDeviceConfig();
+        config.setRefreshRateInHighZone(60);
+        config.setHighDisplayBrightnessThresholds(new int[] { 255 });
+        config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
+
+        Sensor lightSensor = createLightSensor();
+        SensorManager sensorManager = createMockSensorManager(lightSensor);
+
+        director.start(sensorManager);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        Mockito.verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
+                .registerListener(
+                        listenerCaptor.capture(),
+                        eq(lightSensor),
+                        anyInt(),
+                        any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        setBrightness(100);
+        // Sensor reads 2000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 2000));
+
+        Vote vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertThat(vote).isNull();
+
+        setBrightness(255);
+        // Sensor reads 9000 lux,
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 9000));
+
+        vote = director.getVote(Display.DEFAULT_DISPLAY, PRIORITY_FLICKER);
+        assertVoteForRefreshRateLocked(vote, 60 /*fps*/);
+    }
+
+    private void assertVoteForRefreshRateLocked(Vote vote, float refreshRate) {
+        assertThat(vote).isNotNull();
+        final DisplayModeDirector.RefreshRateRange expectedRange =
+                new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate);
+        assertThat(vote.refreshRateRange).isEqualTo(expectedRange);
+    }
+
+    private static class FakeDeviceConfig extends FakeDeviceConfigInterface {
+        @Override
+        public String getProperty(String namespace, String name) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            return super.getProperty(namespace, name);
+        }
+
+        @Override
+        public void addOnPropertiesChangedListener(
+                String namespace,
+                Executor executor,
+                DeviceConfig.OnPropertiesChangedListener listener) {
+            Preconditions.checkArgument(DeviceConfig.NAMESPACE_DISPLAY_MANAGER.equals(namespace));
+            super.addOnPropertiesChangedListener(namespace, executor, listener);
+        }
+
+        void setRefreshRateInLowZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_LOW_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setLowAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setRefreshRateInHighZone(int fps) {
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_REFRESH_RATE_IN_HIGH_ZONE,
+                    String.valueOf(fps));
+        }
+
+        void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
+            String thresholds = toPropertyValue(brightnessThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Brightness Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        void setHighAmbientBrightnessThresholds(int[] ambientThresholds) {
+            String thresholds = toPropertyValue(ambientThresholds);
+
+            if (DEBUG) {
+                Slog.e(TAG, "Ambient Thresholds = " + thresholds);
+            }
+
+            putPropertyAndNotify(
+                    DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                    KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS,
+                    thresholds);
+        }
+
+        @NonNull
+        private static String toPropertyValue(@NonNull int[] intArray) {
+            return Arrays.stream(intArray)
+                    .mapToObj(Integer::toString)
+                    .collect(Collectors.joining(","));
+        }
+    }
+
+    private void setBrightness(int brightness) {
+        Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS,
+                brightness);
+        mInjector.notifyBrightnessChanged();
+        waitForIdleSync();
+    }
+
+    private void setPeakRefreshRate(float fps) {
+        Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE,
+                 fps);
+        mInjector.notifyPeakRefreshRateChanged();
+        waitForIdleSync();
+    }
+
+    private static SensorManager createMockSensorManager(Sensor... sensors) {
+        SensorManager sensorManager = Mockito.mock(SensorManager.class);
+        when(sensorManager.getSensorList(anyInt())).then((invocation) -> {
+            List<Sensor> requestedSensors = new ArrayList<>();
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type || type == Sensor.TYPE_ALL) {
+                    requestedSensors.add(sensor);
+                }
+            }
+            return requestedSensors;
+        });
+
+        when(sensorManager.getDefaultSensor(anyInt())).then((invocation) -> {
+            int type = invocation.getArgument(0);
+            for (Sensor sensor : sensors) {
+                if (sensor.getType() == type) {
+                    return sensor;
+                }
+            }
+            return null;
+        });
+        return sensorManager;
+    }
+
+    private static Sensor createLightSensor() {
+        try {
+            return TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
+        } catch (Exception e) {
+            // There's nothing we can do if this fails, just throw a RuntimeException so that we
+            // don't have to mark every function that might call this as throwing Exception
+            throw new RuntimeException("Failed to create a light sensor", e);
+        }
+    }
+
+    private void waitForIdleSync() {
+        mHandler.runWithScissors(() -> { }, 500 /*timeout*/);
+    }
+
+    static class FakesInjector implements DisplayModeDirector.Injector {
+        private final FakeDeviceConfig mDeviceConfig;
+        private ContentObserver mBrightnessObserver;
+        private ContentObserver mPeakRefreshRateObserver;
+
+        FakesInjector() {
+            mDeviceConfig = new FakeDeviceConfig();
+        }
+
+        @NonNull
+        public FakeDeviceConfig getDeviceConfig() {
+            return mDeviceConfig;
+        }
+
+        @Override
+        public void registerBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            if (mBrightnessObserver != null) {
+                throw new IllegalStateException("Tried to register a second brightness observer");
+            }
+            mBrightnessObserver = observer;
+        }
+
+        @Override
+        public void unregisterBrightnessObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mBrightnessObserver = null;
+        }
+
+        void notifyBrightnessChanged() {
+            if (mBrightnessObserver != null) {
+                mBrightnessObserver.dispatchChange(false /*selfChange*/, DISPLAY_BRIGHTNESS_URI);
+            }
+        }
+
+        @Override
+        public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
+                @NonNull ContentObserver observer) {
+            mPeakRefreshRateObserver = observer;
+        }
+
+        void notifyPeakRefreshRateChanged() {
+            if (mPeakRefreshRateObserver != null) {
+                mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/,
+                        PEAK_REFRESH_RATE_URI);
+            }
+        }
+
+        @Override
+        public boolean isDeviceInteractive(@NonNull Context context) {
+            return true;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
index 9ef7557..20f9b70 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -27,6 +27,8 @@
 
 import android.content.om.OverlayInfo;
 import android.text.TextUtils;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -403,7 +405,7 @@
 
     private int countXmlTags(String xml, String tagToLookFor) throws Exception {
         int count = 0;
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new StringReader(xml));
         int event = parser.getEventType();
         while (event != XmlPullParser.END_DOCUMENT) {
@@ -418,7 +420,7 @@
     private int countXmlAttributesWhere(String xml, String tag, String attr, String value)
             throws Exception {
         int count = 0;
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new StringReader(xml));
         int event = parser.getEventType();
         while (event != XmlPullParser.END_DOCUMENT) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
index bbb83b6..11d00f0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageInstallerSessionTest.java
@@ -29,6 +29,8 @@
 import android.util.Slog;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 89c3d50..90658055 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -25,6 +25,8 @@
 import android.content.pm.PackageParser;
 import android.content.pm.Signature;
 import android.util.TypedXmlPullParser;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 194ae05..23fcf70 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -98,6 +98,8 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.frameworks.servicestests.R;
@@ -8695,7 +8697,7 @@
 
         // Write ShareTargets to Xml
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
-        final XmlSerializer outXml = new FastXmlSerializer();
+        final TypedXmlSerializer outXml = Xml.newFastSerializer();
         outXml.setOutput(outStream, StandardCharsets.UTF_8.name());
         outXml.startDocument(null, true);
         for (int i = 0; i < expectedValues.size(); i++) {
@@ -8706,7 +8708,7 @@
 
         // Read ShareTargets from Xml
         ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new InputStreamReader(inStream));
         List<ShareTargetInfo> shareTargets = new ArrayList<>();
         int type;
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index 4fac9dc..dfc25e0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -21,6 +21,7 @@
 import static android.content.pm.UserInfo.FLAG_EPHEMERAL;
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_INITIALIZED;
 import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
 import static android.content.pm.UserInfo.FLAG_PROFILE;
 import static android.content.pm.UserInfo.FLAG_RESTRICTED;
@@ -44,6 +45,7 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
@@ -206,6 +208,8 @@
     @Test
     public void testUpgradeIfNecessaryLP_9() {
         final int versionToTest = 9;
+        // do not trigger a user type upgrade
+        final int userTypeVersion = UserTypeFactory.getUserTypeVersion();
 
         mUserManagerService.putUserInfo(createUser(100, FLAG_MANAGED_PROFILE, null));
         mUserManagerService.putUserInfo(createUser(101,
@@ -216,7 +220,7 @@
         mUserManagerService.putUserInfo(createUser(105, FLAG_SYSTEM | FLAG_FULL, null));
         mUserManagerService.putUserInfo(createUser(106, FLAG_DEMO | FLAG_FULL, null));
 
-        mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1);
+        mUserManagerService.upgradeIfNecessaryLP(null, versionToTest - 1, userTypeVersion);
 
         assertTrue(mUserManagerService.isUserOfType(100, USER_TYPE_PROFILE_MANAGED));
         assertTrue((mUserManagerService.getUserInfo(100).flags & FLAG_PROFILE) != 0);
@@ -278,4 +282,86 @@
                     two.convertedFromPreCreated);
         }
     }
+
+    /** Tests upgrading profile types */
+    @Test
+    public void testUpgradeProfileType_updateTypeAndFlags() {
+        final int userId = 42;
+        final String newUserTypeName = "new.user.type";
+        final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED;
+
+        UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder()
+                .setName(oldUserTypeName)
+                .setBaseType(FLAG_PROFILE)
+                .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
+                .setMaxAllowedPerParent(32)
+                .setIconBadge(401)
+                .setBadgeColors(402, 403, 404)
+                .setBadgeLabels(23, 24, 25);
+        UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails();
+
+        UserInfo userInfo = createUser(userId,
+                oldUserType.getDefaultUserInfoFlags() | FLAG_INITIALIZED, oldUserTypeName);
+        mUserManagerService.putUserInfo(userInfo);
+
+        UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder()
+                .setName(newUserTypeName)
+                .setBaseType(FLAG_PROFILE)
+                .setMaxAllowedPerParent(32)
+                .setIconBadge(401)
+                .setBadgeColors(402, 403, 404)
+                .setBadgeLabels(23, 24, 25);
+        UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails();
+
+        mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType);
+
+        assertTrue(mUserManagerService.isUserOfType(userId, newUserTypeName));
+        assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_PROFILE) != 0);
+        assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_MANAGED_PROFILE) == 0);
+        assertTrue((mUserManagerService.getUserInfo(userId).flags & FLAG_INITIALIZED) != 0);
+    }
+
+    @Test
+    public void testUpgradeProfileType_updateRestrictions() {
+        final int userId = 42;
+        final String newUserTypeName = "new.user.type";
+        final String oldUserTypeName = USER_TYPE_PROFILE_MANAGED;
+
+        UserTypeDetails.Builder oldUserTypeBuilder = new UserTypeDetails.Builder()
+                .setName(oldUserTypeName)
+                .setBaseType(FLAG_PROFILE)
+                .setDefaultUserInfoPropertyFlags(FLAG_MANAGED_PROFILE)
+                .setMaxAllowedPerParent(32)
+                .setIconBadge(401)
+                .setBadgeColors(402, 403, 404)
+                .setBadgeLabels(23, 24, 25);
+        UserTypeDetails oldUserType = oldUserTypeBuilder.createUserTypeDetails();
+
+        UserInfo userInfo = createUser(userId, oldUserType.getDefaultUserInfoFlags(),
+                oldUserTypeName);
+        mUserManagerService.putUserInfo(userInfo);
+        mUserManagerService.setUserRestriction(UserManager.DISALLOW_CAMERA, true, userId);
+        mUserManagerService.setUserRestriction(UserManager.DISALLOW_PRINTING, true, userId);
+
+        UserTypeDetails.Builder newUserTypeBuilder = new UserTypeDetails.Builder()
+                .setName(newUserTypeName)
+                .setBaseType(FLAG_PROFILE)
+                .setMaxAllowedPerParent(32)
+                .setIconBadge(401)
+                .setBadgeColors(402, 403, 404)
+                .setBadgeLabels(23, 24, 25)
+                .setDefaultRestrictions(
+                        UserManagerServiceUserTypeTest.makeRestrictionsBundle(
+                                UserManager.DISALLOW_WALLPAPER));
+        UserTypeDetails newUserType = newUserTypeBuilder.createUserTypeDetails();
+
+        mUserManagerService.upgradeProfileToTypeLU(userInfo, newUserType);
+
+        assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean(
+                UserManager.DISALLOW_PRINTING));
+        assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean(
+                UserManager.DISALLOW_CAMERA));
+        assertTrue(mUserManagerService.getUserRestrictions(userId).getBoolean(
+                UserManager.DISALLOW_WALLPAPER));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
index 8e74c90..ee30f68 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserTypeTest.java
@@ -51,6 +51,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
+
 /**
  * Tests for {@link UserTypeDetails} and {@link UserTypeFactory}.
  *
@@ -358,13 +360,56 @@
                 () -> UserTypeFactory.customizeBuilders(builders, parser));
     }
 
+    @Test
+    public void testUserTypeFactoryVersion_versionMissing() {
+        final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_eraseArray);
+        assertEquals(0, UserTypeFactory.getUserTypeVersion(parser));
+    }
+
+    @Test
+    public void testUserTypeFactoryVersion_versionPresent() {
+        final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
+        assertEquals(1234, UserTypeFactory.getUserTypeVersion(parser));
+    }
+
+    @Test
+    public void testUserTypeFactoryUpgrades_validUpgrades() {
+        final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
+        builders.put("name", getMinimalBuilder());
+
+        final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_profile);
+        List<UserTypeFactory.UserTypeUpgrade> upgrades = UserTypeFactory.parseUserUpgrades(builders,
+                parser);
+
+        assertFalse(upgrades.isEmpty());
+        UserTypeFactory.UserTypeUpgrade upgrade = upgrades.get(0);
+        assertEquals("android.test.1", upgrade.getFromType());
+        assertEquals("android.test.2", upgrade.getToType());
+        assertEquals(1233, upgrade.getUpToVersion());
+    }
+
+    @Test
+    public void testUserTypeFactoryUpgrades_illegalBaseTypeUpgrade() {
+        final String userTypeFull = "android.test.1";
+        final ArrayMap<String, UserTypeDetails.Builder> builders = new ArrayMap<>();
+        builders.put(userTypeFull, new UserTypeDetails.Builder()
+                .setName(userTypeFull)
+                .setBaseType(FLAG_FULL));
+
+        final XmlResourceParser parser = mResources.getXml(R.xml.usertypes_test_full);
+
+        // parser is illegal because the "to" upgrade type is not a profile, but a full user
+        assertThrows(IllegalArgumentException.class,
+                () -> UserTypeFactory.parseUserUpgrades(builders, parser));
+    }
+
     /** Returns a minimal {@link UserTypeDetails.Builder} that can legitimately be created. */
     private UserTypeDetails.Builder getMinimalBuilder() {
         return new UserTypeDetails.Builder().setName("name").setBaseType(FLAG_FULL);
     }
 
     /** Creates a Bundle of the given String restrictions, each set to true. */
-    private Bundle makeRestrictionsBundle(String ... restrictions) {
+    public static Bundle makeRestrictionsBundle(String ... restrictions) {
         final Bundle bundle = new Bundle();
         for (String restriction : restrictions) {
             bundle.putBoolean(restriction, true);
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 0f9bf2f..8bccce1 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -367,7 +367,7 @@
          * for you if needed; if you already have this information around, it can
          * be much more efficient to supply it here.
          * 
-         * @return Returns an XmlPullParser allowing you to parse out the XML
+         * @return Returns an TypedXmlPullParser allowing you to parse out the XML
          * data.  Returns null if the xml resource could not be found for any
          * reason.
          */
diff --git a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
index 1d62e01..7ac4938 100644
--- a/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/CacheQuotaStrategyTest.java
@@ -21,6 +21,8 @@
 import android.app.usage.CacheQuotaHint;
 import android.test.AndroidTestCase;
 import android.util.Pair;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
 
@@ -37,12 +39,12 @@
 @RunWith(JUnit4.class)
 public class CacheQuotaStrategyTest extends AndroidTestCase {
     StringWriter mWriter;
-    FastXmlSerializer mOut;
+    TypedXmlSerializer mOut;
 
     @Before
     public void setUp() throws Exception {
         mWriter = new StringWriter();
-        mOut = new FastXmlSerializer();
+        mOut = Xml.newFastSerializer();
         mOut.setOutput(mWriter);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
similarity index 92%
rename from services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
rename to services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
index 2904a5b..a67f645 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/FakeDeviceConfigInterface.java
+++ b/services/tests/servicestests/utils/com/android/server/testutils/FakeDeviceConfigInterface.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.wm.utils;
+package com.android.server.testutils;
 
 import android.annotation.NonNull;
 import android.provider.DeviceConfig;
@@ -22,6 +22,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.utils.DeviceConfigInterface;
 
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
@@ -122,6 +123,19 @@
     }
 
     @Override
+    public float getFloat(String namespace, String name, float defaultValue) {
+        String value = getProperty(namespace, name);
+        if (value == null) {
+            return defaultValue;
+        }
+        try {
+            return Float.parseFloat(value);
+        } catch (NumberFormatException e) {
+            return defaultValue;
+        }
+    }
+
+    @Override
     public boolean getBoolean(String namespace, String name, boolean defaultValue) {
         String value = getProperty(namespace, name);
         return value != null ? Boolean.parseBoolean(value) : defaultValue;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index c507928..4a4d9bc 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -54,6 +54,8 @@
 import android.util.ArraySet;
 import android.util.IntArray;
 import android.util.SparseArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -267,7 +269,7 @@
                     mIpm, approvalLevel);
 
             // approved services aren't in xml
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(new BufferedInputStream(new ByteArrayInputStream(new byte[]{})),
                     null);
             writeExpectedValuesToSettings(approvalLevel);
@@ -335,7 +337,7 @@
             String testComponent = "user.test.component/C1";
             String resolvedValue =
                     (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage;
-            XmlPullParser parser =
+            TypedXmlPullParser parser =
                     getParserWithEntries(service, getXmlEntry(resolvedValue, 0, true));
 
             service.readXml(parser, null, true, 10);
@@ -357,7 +359,7 @@
             String resolvedValue =
                     (approvalLevel == APPROVAL_BY_COMPONENT) ? testComponent : testPackage;
             String xmlEntry = getXmlEntry(resolvedValue, 0, true, false);
-            XmlPullParser parser = getParserWithEntries(service, xmlEntry);
+            TypedXmlPullParser parser = getParserWithEntries(service, xmlEntry);
 
             service.readXml(parser, null, true, 0);
 
@@ -384,7 +386,7 @@
         ManagedServices service2 =
                 new TestManagedServices(
                         getContext(), mLock, mUserProfiles, mIpm, APPROVAL_BY_COMPONENT);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         BufferedOutputStream outStream = new BufferedOutputStream(baos);
         serializer.setOutput(outStream, "utf-8");
@@ -396,7 +398,7 @@
         serializer.endDocument();
         outStream.flush();
 
-        final XmlPullParser parser = Xml.newPullParser();
+        final TypedXmlPullParser parser = Xml.newFastPullParser();
         BufferedInputStream input = new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray()));
 
@@ -536,7 +538,7 @@
                     service,
                     Collections.singletonList(service.getPackageName(resolvedValue10)),
                     10);
-            XmlPullParser parser =
+            TypedXmlPullParser parser =
                     getParserWithEntries(
                             service,
                             getXmlEntry(resolvedValue0, 0, true),
@@ -544,7 +546,7 @@
             service.readXml(parser, null, false, UserHandle.USER_ALL);
 
             // Write backup.
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -557,7 +559,7 @@
             service.setPackageOrComponentEnabled(resolvedValue10, 10, true, false);
 
             // Parse backup via restore.
-            XmlPullParser restoreParser = Xml.newPullParser();
+            TypedXmlPullParser restoreParser = Xml.newFastPullParser();
             restoreParser.setInput(
                     new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
             restoreParser.nextTag();
@@ -608,7 +610,7 @@
                 addExpectedServices(service, entriesExpectedToHaveServices, userInfo.id);
             }
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -618,7 +620,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(new BufferedInputStream(
                     new ByteArrayInputStream(baos.toByteArray())), null);
             parser.nextTag();
@@ -640,7 +642,7 @@
                     mIpm, approvalLevel);
             loadXml(service);
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -666,7 +668,7 @@
                     mIpm, approvalLevel);
             loadXml(service);
 
-            XmlSerializer serializer = new FastXmlSerializer();
+            TypedXmlSerializer serializer = Xml.newFastSerializer();
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
             serializer.startDocument(null, true);
@@ -674,7 +676,7 @@
             serializer.endDocument();
             serializer.flush();
 
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             byte[] rawOutput = baos.toByteArray();
             parser.setInput(new BufferedInputStream(
                     new ByteArrayInputStream(rawOutput)), null);
@@ -1387,7 +1389,7 @@
 
     private void loadXml(ManagedServices service) throws Exception {
         String xmlString = createXml(service);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xmlString.getBytes())), null);
         parser.nextTag();
@@ -1423,7 +1425,7 @@
         return xml.toString();
     }
 
-    private XmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
+    private TypedXmlPullParser getParserWithEntries(ManagedServices service, String... xmlEntries)
             throws Exception {
         final StringBuffer xml = new StringBuffer();
         xml.append("<" + service.getConfig().xmlTag + ">\n");
@@ -1432,7 +1434,7 @@
         }
         xml.append("</" + service.getConfig().xmlTag + ">");
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.toString().getBytes())), null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index f649911..8c2038b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -36,6 +36,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.IntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.server.UiServiceTestCase;
@@ -126,7 +128,7 @@
                 + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
                 + "</enabled_assistants>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.toString().getBytes())), null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 09a4289..6083237 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -160,6 +160,8 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.widget.RemoteViews;
 
@@ -3446,7 +3448,7 @@
                 new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
                 false,
                 UserHandle.USER_ALL);
-        verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class), anyLong());
+        verify(mSnoozeHelper, times(1)).readXml(any(TypedXmlPullParser.class), anyLong());
     }
 
     @Test
@@ -3887,12 +3889,12 @@
         NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
         channel.setSound(Uri.EMPTY, null);
 
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXmlForBackup(serializer, getContext());
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
@@ -3915,7 +3917,7 @@
         NotificationChannel channel = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT);
         channel.setVibrationPattern(new long[0]);
 
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         channel.writeXml(serializer);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 7ec8689..98c4a2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -99,6 +99,8 @@
 import android.util.ArraySet;
 import android.util.Pair;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -280,7 +282,7 @@
     private ByteArrayOutputStream writeXmlAndPurge(
             String pkg, int uid, boolean forBackup, int userId, String... channelIds)
             throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -300,7 +302,7 @@
 
     private void loadByteArrayXml(byte[] byteArray, boolean forRestore, int userId)
             throws Exception {
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
         parser.nextTag();
         mHelper.readXml(parser, forRestore, userId);
@@ -717,7 +719,7 @@
         mHelper.onPackagesChanged(true, UserHandle.myUserId(), new String[]{PKG_N_MR1}, new int[]{
                 UID_N_MR1});
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
@@ -772,7 +774,7 @@
                 + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" visibility=\""
                 + Notification.VISIBILITY_PRIVATE + "\" />\n"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())),
                 null);
         parser.nextTag();
@@ -2559,7 +2561,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2580,7 +2582,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2612,7 +2614,7 @@
                 + " importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2753,7 +2755,7 @@
                 + "<channel id=\"b\" name=\"b\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -2779,7 +2781,7 @@
                 + "<channel id=\"c\" name=\"c\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3047,7 +3049,7 @@
         toAdd.add(new Pair(PKG_O, UID_O));
         mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, toAdd);
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())),
                 null);
         parser.nextTag();
@@ -3121,7 +3123,7 @@
                 + "<channel id=\"" + extraChannel1 + "\" name=\"hi\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3154,12 +3156,12 @@
                 + "</ranking>";
 
         // trigger a restore for both users
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser0.getBytes())),
                 null);
         parser.nextTag();
         mHelper.readXml(parser, true, 0);
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xmlUser10.getBytes())),
                 null);
         parser.nextTag();
@@ -3242,7 +3244,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"foo:placeholder_id\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3261,7 +3263,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\" conv_id=\"other\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
@@ -3280,7 +3282,7 @@
                 + "<channel id=\"id\" name=\"hi\" importance=\"3\"/>"
                 + "</package>"
                 + "</ranking>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())),
                 null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 3deeea2..35b224a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -43,6 +43,8 @@
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.IntArray;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -95,7 +97,7 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
@@ -114,12 +116,12 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
         mSnoozeHelper.writeXml(serializer);
@@ -137,7 +139,7 @@
                 + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 1);
@@ -151,7 +153,7 @@
             throws XmlPullParserException, IOException {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 999999999);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -159,7 +161,7 @@
         serializer.endDocument();
         serializer.flush();
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), "utf-8");
         mSnoozeHelper.readXml(parser, 1);
@@ -175,7 +177,7 @@
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 0);
        // Thread.sleep(100);
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -183,7 +185,7 @@
         serializer.endDocument();
         serializer.flush();
         Thread.sleep(10);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), "utf-8");
         mSnoozeHelper.readXml(parser, 2);
@@ -227,7 +229,7 @@
                 + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
                 + "pkg=\"pkg\" key=\"key2\" time=\"" + 15+ "\"/>"
                 + "</snoozed-notifications>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml_string.getBytes())), null);
         mSnoozeHelper.readXml(parser, 4);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 013a994..5262465 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -30,6 +30,8 @@
 import android.service.notification.ZenModeConfig.EventInfo;
 import android.service.notification.ZenPolicy;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -199,7 +201,7 @@
         rule.name = "name";
         rule.snoozing = true;
 
-        XmlSerializer out = new FastXmlSerializer();
+        TypedXmlSerializer out = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         out.setOutput(new BufferedOutputStream(baos), "utf-8");
         out.startDocument(null, true);
@@ -208,7 +210,7 @@
         out.endTag(null, tag);
         out.endDocument();
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3430dbd..cfdd246 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -95,6 +95,8 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.StatsEvent;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -189,14 +191,14 @@
                 + "&amp;end=7.0&amp;exitAtAlarm=true\"/>"
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
         return new XmlResourceParserImpl(parser);
     }
 
     private ByteArrayOutputStream writeXmlAndPurge(Integer version) throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -209,7 +211,7 @@
 
     private ByteArrayOutputStream writeXmlAndPurgeForUser(Integer version, int userId)
             throws Exception {
-        XmlSerializer serializer = new FastXmlSerializer();
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
         serializer.startDocument(null, true);
@@ -222,8 +224,8 @@
         return baos;
     }
 
-    private XmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception {
-        XmlPullParser parser = Xml.newPullParser();
+    private TypedXmlPullParser getParserForByteStream(ByteArrayOutputStream baos) throws Exception {
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(
                 new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -837,7 +839,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, false, UserHandle.USER_ALL);
 
         assertEquals("Config mismatch: current vs expected: "
@@ -962,7 +964,7 @@
         mZenModeHelperSpy.mConfigs.put(11, newConfig11);
 
         // Parse backup data.
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, 10);
         mZenModeHelperSpy.readXml(parser, true, 11);
 
@@ -980,7 +982,7 @@
         ZenModeConfig original = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM);
 
         assertEquals("Config mismatch: current vs original: "
@@ -1000,7 +1002,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
 
         // Restore data for user 10.
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, 10);
 
         ZenModeConfig actual = mZenModeHelperSpy.mConfigs.get(10);
@@ -1045,7 +1047,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurge(null);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1086,7 +1088,7 @@
         ZenModeConfig expected = mZenModeHelperSpy.mConfig.copy();
 
         ByteArrayOutputStream baos = writeXmlAndPurgeForUser(null, UserHandle.USER_SYSTEM);
-        XmlPullParser parser = getParserForByteStream(baos);
+        TypedXmlPullParser parser = getParserForByteStream(baos);
         mZenModeHelperSpy.readXml(parser, true, UserHandle.USER_SYSTEM);
 
         ZenModeConfig.ZenRule original = expected.automaticRules.get(ruleId);
@@ -1113,7 +1115,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1133,7 +1135,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1149,7 +1151,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1168,7 +1170,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1187,7 +1189,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1206,7 +1208,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1222,7 +1224,7 @@
                 + "<disallow visualEffects=\"511\" />"
                 + "</zen>";
 
-        parser = Xml.newPullParser();
+        parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(xml.getBytes())), null);
         parser.nextTag();
@@ -1242,7 +1244,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1278,7 +1280,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1330,7 +1332,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1399,7 +1401,7 @@
 
         // set previous version
         ByteArrayOutputStream baos = writeXmlAndPurge(5);
-        XmlPullParser parser = Xml.newPullParser();
+        TypedXmlPullParser parser = Xml.newFastPullParser();
         parser.setInput(new BufferedInputStream(
                 new ByteArrayInputStream(baos.toByteArray())), null);
         parser.nextTag();
@@ -1628,12 +1630,12 @@
     }
 
     /**
-     * Wrapper to use XmlPullParser as XmlResourceParser for Resources.getXml()
+     * Wrapper to use TypedXmlPullParser as XmlResourceParser for Resources.getXml()
      */
     final class XmlResourceParserImpl implements XmlResourceParser {
-        private XmlPullParser parser;
+        private TypedXmlPullParser parser;
 
-        public XmlResourceParserImpl(XmlPullParser parser) {
+        public XmlResourceParserImpl(TypedXmlPullParser parser) {
             this.parser = parser;
         }
 
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 7f4f3dd..0ed037c 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -65,8 +65,6 @@
                   android:turnScreenOn="true" />
         <activity android:name="com.android.server.wm.TaskStackChangedListenerTest$ResumeWhilePausingActivity"
                   android:resumeWhilePausing="true"/>
-        <activity android:name="com.android.server.wm.ScreenDecorWindowTests$TestActivity"
-                  android:showWhenLocked="true" android:allowEmbedded="true"/>
         <activity android:name="com.android.server.wm.ActivityLeakTests$DetectLeakActivity" />
     </application>
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 53ade0e..2304efc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -71,6 +71,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.never;
 
@@ -376,6 +377,27 @@
     }
 
     @Test
+    public void testDestroyedActivityNotScheduleConfigChanged() throws RemoteException {
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setCreateTask(true)
+                .setConfigChanges(CONFIG_ORIENTATION)
+                .build();
+        final Task task = activity.getTask();
+        activity.setState(DESTROYED, "Testing");
+
+        final Configuration newConfig = new Configuration(task.getConfiguration());
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
+        task.onRequestedOverrideConfigurationChanged(newConfig);
+
+        ensureActivityConfiguration(activity);
+
+        verify(mAtm.getLifecycleManager(), never())
+                .scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class));
+    }
+
+    @Test
     public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
         final ActivityRecord activity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
@@ -401,7 +423,7 @@
 
         // Mimic the behavior that display doesn't handle app's requested orientation.
         final DisplayContent dc = activity.getTask().getDisplayContent();
-        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).onDescendantOrientationChanged(any());
         doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
 
         final int requestedOrientation;
@@ -1658,7 +1680,7 @@
         final int rotatedOrentation = r.getConfiguration().orientation == ORIENTATION_PORTRAIT
                 ? SCREEN_ORIENTATION_LANDSCAPE
                 : SCREEN_ORIENTATION_PORTRAIT;
-        doReturn(false).when(r).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(r).onDescendantOrientationChanged(any());
         r.setOrientation(rotatedOrentation);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 6ca69bf..8cc515e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -196,7 +196,7 @@
     @Test
     public void testCancelRemoteAnimationWhenFreeze() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
-        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).onDescendantOrientationChanged(any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
         final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 5828d02..59b12e4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -366,6 +366,30 @@
         assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty());
     }
 
+    @Test
+    public void testOnRequestedOverrideConfigurationChangedOverrideMaxBounds() {
+        final TestConfigurationContainer root =
+                new TestConfigurationContainer(true /* providesMaxBounds */);
+        final Rect bounds = new Rect(0, 0, 10, 10);
+        final TestConfigurationContainer child = new TestConfigurationContainer();
+        root.addChild(child);
+        final Configuration configuration = new Configuration();
+        configuration.windowConfiguration.setBounds(bounds);
+
+        root.onRequestedOverrideConfigurationChanged(configuration);
+
+        assertEquals(bounds, root.getBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds());
+        assertEquals(bounds, child.getBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds());
+
+        assertEquals(bounds, root.getMaxBounds());
+        assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds());
+        assertEquals(bounds, child.getMaxBounds());
+        assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds());
+    }
+
+
     /**
      * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed
      * for testing.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
index 06a6882..bc91c70 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaGroupTest.java
@@ -87,7 +87,7 @@
         final Task task = new TaskBuilder(mSupervisor)
                 .setTaskDisplayArea(mTaskDisplayArea).setCreateActivity(true).build();
         final ActivityRecord activity = task.getTopNonFinishingActivity();
-        doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any(), any());
+        doReturn(true).when(mDisplayContent).onDescendantOrientationChanged(any());
         activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
         // Display is portrait, DisplayAreaGroup inherits that
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 59b2d4f..025c5a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -468,14 +468,14 @@
 
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        verify(tda).onDescendantOrientationChanged(any(), any());
-        verify(mDisplayContent, never()).onDescendantOrientationChanged(any(), any());
+        verify(tda).onDescendantOrientationChanged(any());
+        verify(mDisplayContent, never()).onDescendantOrientationChanged(any());
 
         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
         activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
 
-        verify(tda, times(2)).onDescendantOrientationChanged(any(), any());
-        verify(mDisplayContent).onDescendantOrientationChanged(any(), any());
+        verify(tda, times(2)).onDescendantOrientationChanged(any());
+        verify(mDisplayContent).onDescendantOrientationChanged(any());
     }
 
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d921718..64065e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1202,6 +1202,17 @@
         assertTrue(mNavBarWindow.getParent().isAnimating(WindowContainer.AnimationFlags.PARENTS,
                 ANIMATION_TYPE_FIXED_TRANSFORM));
 
+        // If the visibility of insets state is changed, the rotated state should be updated too.
+        final InsetsState rotatedState = app.getFixedRotationTransformInsetsState();
+        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        state.getSource(ITYPE_STATUS_BAR).setVisible(
+                !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
+                rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
+
         final Rect outFrame = new Rect();
         final Rect outInsets = new Rect();
         final Rect outStableInsets = new Rect();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 21bdc9e..79b2da1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -236,8 +236,7 @@
         final WindowState activity = createBaseApplicationWindow();
         activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 
-        policy.adjustWindowParamsLw(activity, activity.mAttrs, 0 /* callingPid */,
-                0 /* callingUid */);
+        policy.adjustWindowParamsLw(activity, activity.mAttrs);
     }
 
     private WindowState createApplicationWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 94ffcda..20775e8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -107,11 +107,9 @@
     }
 
     void addWindow(WindowState win) {
-        final int callingPid = Binder.getCallingPid();
-        final int callingUid = Binder.getCallingUid();
-        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
-        assertEquals(WindowManagerGlobal.ADD_OKAY,
-                mDisplayPolicy.validateAddingWindowLw(win.mAttrs, callingPid, callingUid));
+        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs);
+        assertEquals(WindowManagerGlobal.ADD_OKAY, mDisplayPolicy.validateAddingWindowLw(
+                win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid()));
         mDisplayPolicy.addWindowLw(win, win.mAttrs);
         win.mHasSurface = true;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
index eaedd58..554160c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java
@@ -27,6 +27,8 @@
 
 import android.annotation.Nullable;
 import android.platform.test.annotations.Presubmit;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.Display;
 import android.view.DisplayAddress;
@@ -245,7 +247,7 @@
     private String getStoredDisplayAttributeValue(TestStorage storage, String attr)
             throws Exception {
         try (InputStream stream = storage.openRead()) {
-            XmlPullParser parser = Xml.newPullParser();
+            TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(stream, StandardCharsets.UTF_8.name());
             int type;
             while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
index c3e1922..dfc2e35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/HighRefreshRateDenylistTest.java
@@ -31,7 +31,7 @@
 
 import com.android.internal.R;
 import com.android.internal.util.Preconditions;
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Test;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 40f73b1..5c39bd0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -322,7 +322,7 @@
         assertEquals(landActivity.findMainWindow(), win1);
 
         // Ensure that the display is in Landscape
-        landActivity.onDescendantOrientationChanged(landActivity.token, landActivity);
+        landActivity.onDescendantOrientationChanged(landActivity);
         assertEquals(Configuration.ORIENTATION_LANDSCAPE,
                 mDefaultDisplay.getConfiguration().orientation);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
deleted file mode 100644
index 25ba6db3..0000000
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.graphics.Color.RED;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Gravity.BOTTOM;
-import static android.view.Gravity.LEFT;
-import static android.view.Gravity.RIGHT;
-import static android.view.Gravity.TOP;
-import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
-import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.VirtualDisplay;
-import android.media.ImageReader;
-import android.os.Handler;
-import android.os.SystemClock;
-import android.platform.test.annotations.Presubmit;
-import android.util.Pair;
-import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.function.BooleanSupplier;
-
-/**
- * Tests for the {@link android.view.WindowManager.LayoutParams#PRIVATE_FLAG_IS_SCREEN_DECOR} flag.
- *
- * Build/Install/Run:
- *  atest WmTests:ScreenDecorWindowTests
- */
-// TODO: Add test for FLAG_FULLSCREEN which hides the status bar and also other flags.
-// TODO: Test non-Activity windows.
-@SmallTest
-@Presubmit
-public class ScreenDecorWindowTests {
-
-    private final Context mContext = getInstrumentation().getTargetContext();
-    private final Instrumentation mInstrumentation = getInstrumentation();
-
-    private WindowManager mWm;
-    private ArrayList<View> mWindows = new ArrayList<>();
-
-    private Activity mTestActivity;
-    private VirtualDisplay mDisplay;
-    private ImageReader mImageReader;
-
-    private int mDecorThickness;
-    private int mHalfDecorThickness;
-
-    @Before
-    public void setUp() {
-        final Pair<VirtualDisplay, ImageReader> result = createDisplay();
-        mDisplay = result.first;
-        mImageReader = result.second;
-        final Display display = mDisplay.getDisplay();
-        final Context dContext = mContext.createDisplayContext(display);
-        mWm = dContext.getSystemService(WindowManager.class);
-        mTestActivity = startActivityOnDisplay(TestActivity.class, display.getDisplayId());
-        final Point size = new Point();
-        mDisplay.getDisplay().getRealSize(size);
-        mDecorThickness = Math.min(size.x, size.y) / 3;
-        mHalfDecorThickness = mDecorThickness / 2;
-    }
-
-    @After
-    public void tearDown() {
-        while (!mWindows.isEmpty()) {
-            removeWindow(mWindows.get(0));
-        }
-        finishActivity(mTestActivity);
-        mDisplay.release();
-        mImageReader.close();
-    }
-
-    @Test
-    public void testScreenSides() {
-        // Decor on top
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        // Decor at the bottom
-        updateWindow(decorWindow, BOTTOM, MATCH_PARENT, mDecorThickness, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mDecorThickness);
-
-        // Decor to the left
-        updateWindow(decorWindow, LEFT, mDecorThickness, MATCH_PARENT, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, LEFT, mDecorThickness);
-
-        // Decor to the right
-        updateWindow(decorWindow, RIGHT, mDecorThickness, MATCH_PARENT, 0, 0);
-        assertInsetGreaterOrEqual(mTestActivity, RIGHT, mDecorThickness);
-    }
-
-    // Decor windows (i.e windows using PRIVATE_FLAG_IS_SCREEN_DECOR) are no longer supported.
-    // PRIVATE_FLAG_IS_SCREEN_DECOR and related code will be deprecated/removed soon.
-    @Ignore
-    @Test
-    public void testMultipleDecors() {
-        // Test 2 decor windows on-top.
-        createDecorWindow(TOP, MATCH_PARENT, mHalfDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mHalfDecorThickness);
-        createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        // And one at the bottom.
-        createDecorWindow(BOTTOM, MATCH_PARENT, mHalfDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, BOTTOM, mHalfDecorThickness);
-    }
-
-    @Test
-    public void testFlagChange() {
-        WindowInsets initialInsets = getInsets(mTestActivity);
-
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertTopInsetEquals(mTestActivity, mDecorThickness);
-
-        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
-                0, PRIVATE_FLAG_IS_SCREEN_DECOR);
-
-        // TODO: fix test and re-enable assertion.
-        // initialInsets was not actually immutable and just updated to the current insets,
-        // meaning this assertion never actually tested anything. Now that WindowInsets actually is
-        // immutable, it turns out the test was broken.
-        // assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
-
-        updateWindow(decorWindow, TOP, MATCH_PARENT, mDecorThickness,
-                PRIVATE_FLAG_IS_SCREEN_DECOR, PRIVATE_FLAG_IS_SCREEN_DECOR);
-        assertTopInsetEquals(mTestActivity, mDecorThickness);
-    }
-
-    @Test
-    public void testRemoval() {
-        WindowInsets initialInsets = getInsets(mTestActivity);
-
-        final View decorWindow = createDecorWindow(TOP, MATCH_PARENT, mDecorThickness);
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-
-        removeWindow(decorWindow);
-        assertTopInsetEquals(mTestActivity, initialInsets.getSystemWindowInsetTop());
-    }
-
-    @Test
-    public void testProvidesInsetsTypes() {
-        int[] providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR};
-        final View win = createWindow("StatusBarSubPanel", TOP, MATCH_PARENT, mDecorThickness, RED,
-                FLAG_LAYOUT_IN_SCREEN, 0, providesInsetsTypes);
-
-        assertInsetGreaterOrEqual(mTestActivity, TOP, mDecorThickness);
-    }
-
-    private View createDecorWindow(int gravity, int width, int height) {
-        int[] providesInsetsTypes =
-                new int[]{gravity == TOP ? ITYPE_CLIMATE_BAR : ITYPE_EXTRA_NAVIGATION_BAR};
-        return createWindow("decorWindow", gravity, width, height, RED,
-                FLAG_LAYOUT_IN_SCREEN, PRIVATE_FLAG_IS_SCREEN_DECOR, providesInsetsTypes);
-    }
-
-    private View createWindow(String name, int gravity, int width, int height, int color, int flags,
-            int privateFlags, int[] providesInsetsTypes) {
-
-        final View[] viewHolder = new View[1];
-        final int finalFlag = flags
-                | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCHABLE;
-
-        // Needs to run on the UI thread.
-        Handler.getMain().runWithScissors(() -> {
-            final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                    width, height, TYPE_APPLICATION_OVERLAY, finalFlag, PixelFormat.OPAQUE);
-            lp.gravity = gravity;
-            lp.privateFlags |= privateFlags;
-            lp.providesInsetsTypes = providesInsetsTypes;
-
-            final TextView view = new TextView(mContext);
-            view.setText("ScreenDecorWindowTests - " + name);
-            view.setBackgroundColor(color);
-            mWm.addView(view, lp);
-            mWindows.add(view);
-            viewHolder[0] = view;
-        }, 0);
-
-        waitForIdle();
-        return viewHolder[0];
-    }
-
-    private void updateWindow(View v, int gravity, int width, int height,
-            int privateFlags, int privateFlagsMask) {
-        // Needs to run on the UI thread.
-        Handler.getMain().runWithScissors(() -> {
-            final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) v.getLayoutParams();
-            lp.gravity = gravity;
-            lp.width = width;
-            lp.height = height;
-            setPrivateFlags(lp, privateFlags, privateFlagsMask);
-
-            mWm.updateViewLayout(v, lp);
-        }, 0);
-
-        waitForIdle();
-    }
-
-    private void removeWindow(View v) {
-        Handler.getMain().runWithScissors(() -> mWm.removeView(v), 0);
-        mWindows.remove(v);
-        waitForIdle();
-    }
-
-    private WindowInsets getInsets(Activity a) {
-        return new WindowInsets(a.getWindow().getDecorView().getRootWindowInsets());
-    }
-
-    /**
-     * Set the flags of the window, as per the
-     * {@link WindowManager.LayoutParams WindowManager.LayoutParams}
-     * flags.
-     *
-     * @param flags The new window flags (see WindowManager.LayoutParams).
-     * @param mask Which of the window flag bits to modify.
-     */
-    public void setPrivateFlags(WindowManager.LayoutParams lp, int flags, int mask) {
-        lp.flags = (lp.flags & ~mask) | (flags & mask);
-    }
-
-    /**
-     * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
-     */
-    private void assertTopInsetEquals(Activity activity, int expected) {
-        waitForTopInsetEqual(activity, expected);
-        assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
-    }
-
-    private void waitForTopInsetEqual(Activity activity, int expected) {
-        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
-    }
-
-    /**
-     * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
-     * waiting as needed.
-     */
-    private void assertInsetGreaterOrEqual(Activity activity, int side, int expected) {
-        waitForInsetGreaterOrEqual(activity, side, expected);
-
-        final WindowInsets insets = getInsets(activity);
-        switch (side) {
-            case TOP:
-                assertThat(insets.getSystemWindowInsetTop()).isAtLeast(expected);
-                break;
-            case BOTTOM:
-                assertThat(insets.getSystemWindowInsetBottom()).isAtLeast(expected);
-                break;
-            case LEFT:
-                assertThat(insets.getSystemWindowInsetLeft()).isAtLeast(expected);
-                break;
-            case RIGHT:
-                assertThat(insets.getSystemWindowInsetRight()).isAtLeast(expected);
-                break;
-        }
-    }
-
-    private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
-        waitFor(() -> {
-            final WindowInsets insets = getInsets(activity);
-            switch (side) {
-                case TOP: return insets.getSystemWindowInsetTop() >= expected;
-                case BOTTOM: return insets.getSystemWindowInsetBottom() >= expected;
-                case LEFT: return insets.getSystemWindowInsetLeft() >= expected;
-                case RIGHT: return insets.getSystemWindowInsetRight() >= expected;
-                default: return true;
-            }
-        });
-    }
-
-    private void waitFor(BooleanSupplier waitCondition) {
-        int retriesLeft = 5;
-        do {
-            if (waitCondition.getAsBoolean()) {
-                break;
-            }
-            SystemClock.sleep(500);
-        } while (retriesLeft-- > 0);
-    }
-
-    private void finishActivity(Activity a) {
-        if (a == null) {
-            return;
-        }
-        a.finish();
-        waitForIdle();
-    }
-
-    private void waitForIdle() {
-        mInstrumentation.waitForIdleSync();
-    }
-
-    private Activity startActivityOnDisplay(Class<?> cls, int displayId) {
-        final Intent intent = new Intent(mContext, cls);
-        intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchDisplayId(displayId);
-
-        final Activity activity = SystemUtil.runWithShellPermissionIdentity(
-                () -> mInstrumentation.startActivitySync(intent, options.toBundle()),
-                "android.permission.ACTIVITY_EMBEDDING");
-        waitForIdle();
-
-        assertEquals(displayId, activity.getDisplayId());
-        return activity;
-    }
-
-    private Pair<VirtualDisplay, ImageReader> createDisplay() {
-        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
-        final DisplayInfo displayInfo = new DisplayInfo();
-        final Display defaultDisplay = dm.getDisplay(DEFAULT_DISPLAY);
-        defaultDisplay.getDisplayInfo(displayInfo);
-        final String name = "ScreenDecorWindowTests";
-        int flags = VIRTUAL_DISPLAY_FLAG_PRESENTATION | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
-
-        final ImageReader imageReader = ImageReader.newInstance(
-                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
-
-        final VirtualDisplay display = dm.createVirtualDisplay(name, displayInfo.logicalWidth,
-                displayInfo.logicalHeight, displayInfo.logicalDensityDpi, imageReader.getSurface(),
-                flags);
-
-        return Pair.create(display, imageReader);
-    }
-
-    public static class TestActivity extends Activity {
-    }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index a4bf594..da00198 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -698,7 +698,7 @@
 
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
-        verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+        verify(mTask).onDescendantOrientationChanged(same(newActivity));
         verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
 
         final Rect displayBounds = display.getBounds();
@@ -739,7 +739,7 @@
 
         // Update with new activity requested orientation and recompute bounds with no previous
         // size compat cache.
-        verify(mTask).onDescendantOrientationChanged(any(), same(newActivity));
+        verify(mTask).onDescendantOrientationChanged(same(newActivity));
         verify(mTask).computeFullscreenBounds(any(), any(), any(), anyInt());
 
         final Rect displayBounds = display.getBounds();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index d080fa0..61d4a47 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -70,6 +70,8 @@
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
 import android.util.Xml;
 import android.view.DisplayInfo;
 
@@ -1064,12 +1066,12 @@
 
         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(any(), same(task));
+        verify(display).onDescendantOrientationChanged(same(task));
         reset(display);
 
         display.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation());
-        verify(display).onDescendantOrientationChanged(any(), same(task));
+        verify(display).onDescendantOrientationChanged(same(task));
     }
 
     private Task getTestTask() {
@@ -1097,7 +1099,7 @@
 
     private byte[] serializeToBytes(Task r) throws Exception {
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
-            final XmlSerializer serializer = Xml.newSerializer();
+            final TypedXmlSerializer serializer = Xml.newFastSerializer();
             serializer.setOutput(os, "UTF-8");
             serializer.startDocument(null, true);
             serializer.startTag(null, TASK_TAG);
@@ -1112,7 +1114,7 @@
 
     private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException {
         try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) {
-            final XmlPullParser parser = Xml.newPullParser();
+            final TypedXmlPullParser parser = Xml.newFastPullParser();
             parser.setInput(reader);
             assertEquals(XmlPullParser.START_TAG, parser.next());
             assertEquals(TASK_TAG, parser.getName());
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 3d8adbd..573da89 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -56,7 +56,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.view.IRemoteAnimationFinishedCallback;
@@ -787,12 +786,11 @@
         final TestWindowContainerBuilder builder = new TestWindowContainerBuilder(mWm);
         final TestWindowContainer root = spy(builder.build());
 
-        final IBinder binder = mock(IBinder.class);
         final ActivityRecord activityRecord = mock(ActivityRecord.class);
         final TestWindowContainer child = root.addChildWindow();
 
-        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, binder, activityRecord);
-        verify(root).onDescendantOrientationChanged(binder, activityRecord);
+        child.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activityRecord);
+        verify(root).onDescendantOrientationChanged(activityRecord);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
index 5210011..7a0ef0d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerConstantsTest.java
@@ -32,7 +32,7 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.utils.FakeDeviceConfigInterface;
+import com.android.server.testutils.FakeDeviceConfigInterface;
 
 import org.junit.After;
 import org.junit.Before;
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 3af88e1..d585b23 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -1213,9 +1213,11 @@
                     Slog.d(TAG, "Clear notification");
                     mUsbNotificationId = 0;
                 }
-                // Not relevant for automotive.
-                if (mContext.getPackageManager().hasSystemFeature(
+                // Not relevant for automotive and watch.
+                if ((mContext.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_AUTOMOTIVE)
+                        || mContext.getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_WATCH))
                         && id == SystemMessage.NOTE_USB_CHARGING) {
                     mUsbNotificationId = 0;
                     return;
diff --git a/telephony/java/android/telephony/CallForwardingInfo.java b/telephony/java/android/telephony/CallForwardingInfo.java
index 6ae6d00..aeac36e 100644
--- a/telephony/java/android/telephony/CallForwardingInfo.java
+++ b/telephony/java/android/telephony/CallForwardingInfo.java
@@ -86,7 +86,7 @@
      * Call forwarding reason types
      * @hide
      */
-    @IntDef(flag = true, prefix = { "REASON_" }, value = {
+    @IntDef(prefix = { "REASON_" }, value = {
         REASON_UNCONDITIONAL,
         REASON_BUSY,
         REASON_NO_REPLY,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 643d8c8..9288f79 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9463,7 +9463,7 @@
     }
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = {
+    @IntDef(prefix = { "CDMA_SUBSCRIPTION_" }, value = {
             CDMA_SUBSCRIPTION_UNKNOWN,
             CDMA_SUBSCRIPTION_RUIM_SIM,
             CDMA_SUBSCRIPTION_NV
diff --git a/telephony/java/android/telephony/ims/DelegateMessageCallback.java b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
index beec4a6..0d82a54 100644
--- a/telephony/java/android/telephony/ims/DelegateMessageCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateMessageCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 
 /**
@@ -30,6 +31,7 @@
  * </ul>
  * @hide
  */
+@SystemApi
 public interface DelegateMessageCallback {
 
     /**
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index 4facfa7..3558a9b 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -18,14 +18,14 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.ArraySet;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 
@@ -34,6 +34,7 @@
  * ImsService.
  * @hide
  */
+@SystemApi
 public final class DelegateRegistrationState implements Parcelable {
 
     /**
@@ -114,14 +115,14 @@
     })
     public @interface DeregisteringReason {}
 
-    private final ArrayList<String> mRegisteredTags = new ArrayList<>();
-    private final ArrayList<FeatureTagState> mDeregisteringTags = new ArrayList<>();
-    private final ArrayList<FeatureTagState> mDeregisteredTags = new ArrayList<>();
+    private ArraySet<String> mRegisteredTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteringTags = new ArraySet<>();
+    private final ArraySet<FeatureTagState> mDeregisteredTags = new ArraySet<>();
 
     /**
      * Builder used to create new instances of {@link DelegateRegistrationState}.
      */
-    public static class Builder {
+    public static final class Builder {
 
         private final DelegateRegistrationState mState;
 
@@ -135,10 +136,8 @@
          * @param featureTag The IMS media feature tag included in the current IMS registration.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addRegisteredFeatureTag(@NonNull String featureTag) {
-            if (!mState.mRegisteredTags.contains(featureTag)) {
-                mState.mRegisteredTags.add(featureTag);
-            }
+        public @NonNull Builder addRegisteredFeatureTag(@NonNull String featureTag) {
+            mState.mRegisteredTags.add(featureTag);
             return this;
         }
 
@@ -148,7 +147,8 @@
          * @param featureTags The IMS media feature tags included in the current IMS registration.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addRegisteredFeatureTags(@NonNull Set<String> featureTags) {
             mState.mRegisteredTags.addAll(featureTags);
             return this;
         }
@@ -167,13 +167,9 @@
          *         The availability of the feature tag depends on the {@link DeregisteringReason}.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addDeregisteringFeatureTag(@NonNull String featureTag,
+        public @NonNull Builder addDeregisteringFeatureTag(@NonNull String featureTag,
                 @DeregisteringReason int reason) {
-            boolean ftExists = mState.mDeregisteringTags.stream().anyMatch(
-                    f -> f.getFeatureTag().equals(featureTag));
-            if (!ftExists) {
-                mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
-            }
+            mState.mDeregisteringTags.add(new FeatureTagState(featureTag, reason));
             return this;
         }
 
@@ -185,20 +181,16 @@
          * @param reason The reason why the media feature tag has been deregistered.
          * @return The in-progress Builder instance for RegistrationState.
          */
-        public Builder addDeregisteredFeatureTag(@NonNull String featureTag,
+        public @NonNull Builder addDeregisteredFeatureTag(@NonNull String featureTag,
                 @DeregisteredReason int reason) {
-            boolean ftExists = mState.mDeregisteredTags.stream().anyMatch(
-                    f -> f.getFeatureTag().equals(featureTag));
-            if (!ftExists) {
-                mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
-            }
+            mState.mDeregisteredTags.add(new FeatureTagState(featureTag, reason));
             return this;
         }
 
         /**
          * @return the finalized instance.
          */
-        public DelegateRegistrationState build() {
+        public @NonNull DelegateRegistrationState build() {
             return mState;
         }
     }
@@ -212,7 +204,7 @@
      * Used for unparcelling only.
      */
     private DelegateRegistrationState(Parcel source) {
-        source.readList(mRegisteredTags, null /*classloader*/);
+        mRegisteredTags = (ArraySet<String>) source.readArraySet(null);
         readStateFromParcel(source, mDeregisteringTags);
         readStateFromParcel(source, mDeregisteredTags);
     }
@@ -268,7 +260,8 @@
         return new ArraySet<>(mDeregisteredTags);
     }
 
-    public static final Creator<DelegateRegistrationState> CREATOR =
+
+    public static final @NonNull Creator<DelegateRegistrationState> CREATOR =
             new Creator<DelegateRegistrationState>() {
         @Override
         public DelegateRegistrationState createFromParcel(Parcel source) {
@@ -287,13 +280,13 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeList(mRegisteredTags);
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeArraySet(mRegisteredTags);
         writeStateToParcel(dest, mDeregisteringTags);
         writeStateToParcel(dest, mDeregisteredTags);
     }
 
-    private void writeStateToParcel(Parcel dest, List<FeatureTagState> state) {
+    private void writeStateToParcel(Parcel dest, Set<FeatureTagState> state) {
         dest.writeInt(state.size());
         for (FeatureTagState s : state) {
             dest.writeString(s.getFeatureTag());
@@ -301,11 +294,12 @@
         }
     }
 
-    private void readStateFromParcel(Parcel source, List<FeatureTagState> emptyState) {
+    private void readStateFromParcel(Parcel source, Set<FeatureTagState> emptyState) {
         int len = source.readInt();
         for (int i = 0; i < len; i++) {
             String ft = source.readString();
             int reason = source.readInt();
+
             emptyState.add(new FeatureTagState(ft, reason));
         }
     }
diff --git a/telephony/java/android/telephony/ims/DelegateRequest.java b/telephony/java/android/telephony/ims/DelegateRequest.java
index 73d0840..c322d92 100644
--- a/telephony/java/android/telephony/ims/DelegateRequest.java
+++ b/telephony/java/android/telephony/ims/DelegateRequest.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.SipDelegate;
@@ -31,6 +32,7 @@
  * SipDelegateConnection given back to the requesting application.
  * @hide
  */
+@SystemApi
 public final class DelegateRequest implements Parcelable {
 
     private final ArrayList<String> mFeatureTags;
@@ -52,7 +54,7 @@
      * @return the list of IMS feature tag associated with this DelegateRequest in the format
      * defined in RCC.07 section 2.6.1.3.
      */
-    public Set<String> getFeatureTags() {
+    public @NonNull Set<String> getFeatureTags() {
         return new ArraySet<>(mFeatureTags);
     }
 
@@ -70,7 +72,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeList(mFeatureTags);
     }
 
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index 0f1afc4..fb65949 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -18,10 +18,11 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 import android.telephony.ims.stub.SipTransportImplBase;
 
-import java.util.List;
+import java.util.Set;
 
 /**
  * Callback interface to notify a remote application of the following:
@@ -34,26 +35,24 @@
  * </ul>
  * @hide
  */
+@SystemApi
 public interface DelegateStateCallback {
 
     /**
      * This must be called by the ImsService after {@link SipTransportImplBase#createSipDelegate} is
      * called by the framework to notify the framework and remote application that the
      * {@link SipDelegate} has been successfully created.
-     *
-     * @param delegate The SipDelegate created to service the DelegateRequest.
-     * @param deniedTags A List of {@link FeatureTagState}, which contains the feature tags
+     *  @param delegate The SipDelegate created to service the DelegateRequest.
+     * @param deniedTags A Set of {@link FeatureTagState}s, which contain the feature tags
      *    associated with this {@link SipDelegate} that have no access to send/receive SIP messages
      *    as well as a reason for why the feature tag is denied. For more information on the reason
      *    why the feature tag was denied access, see the
      *    {@link SipDelegateManager.DeniedReason} reasons. This is considered a permanent denial due
      *    to this {@link SipDelegate} not supporting a feature or this ImsService already
      *    implementing this feature elsewhere. If all features of this {@link SipDelegate} are
-     *    denied, {@link #onCreated(SipDelegate, List)} should still be called as the framework will
-     *    later call {@link SipTransportImplBase#destroySipDelegate(SipDelegate, int)} to clean the
-     *    delegate up.
+     *    denied, this method should still be called.
      */
-    void onCreated(@NonNull SipDelegate delegate, @Nullable List<FeatureTagState> deniedTags);
+    void onCreated(@NonNull SipDelegate delegate, @Nullable Set<FeatureTagState> deniedTags);
 
     /**
      * This must be called by the ImsService after the framework calls
diff --git a/telephony/java/android/telephony/ims/FeatureTagState.java b/telephony/java/android/telephony/ims/FeatureTagState.java
index 060be6f..3622065 100644
--- a/telephony/java/android/telephony/ims/FeatureTagState.java
+++ b/telephony/java/android/telephony/ims/FeatureTagState.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
@@ -39,6 +40,7 @@
  * currently available.
  * @hide
  */
+@SystemApi
 public final class FeatureTagState implements Parcelable {
 
     private final String mFeatureTag;
@@ -48,8 +50,8 @@
      * Associate an IMS feature tag with its current state. See {@link DelegateRegistrationState}
      * and {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged(
      * DelegateRegistrationState, List)} and
-     * {@link DelegateStateCallback#onCreated(SipDelegate, List)} for examples on how and when this
-     * is used.
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} for examples on how and
+     * when this is used.
      *
      * @param featureTag The IMS feature tag that is deregistered, in the process of
      *                   deregistering, or denied.
@@ -93,12 +95,12 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mFeatureTag);
         dest.writeInt(mState);
     }
 
-    public static final Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
+    public static final @NonNull Creator<FeatureTagState> CREATOR = new Creator<FeatureTagState>() {
         @Override
         public FeatureTagState createFromParcel(Parcel source) {
             return new FeatureTagState(source);
diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java
index fdf636c..c663e39 100644
--- a/telephony/java/android/telephony/ims/ImsExternalCallState.java
+++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java
@@ -49,8 +49,7 @@
     public static final int CALL_STATE_TERMINATED = 2;
 
     /**@hide*/
-    @IntDef(flag = true,
-            value = {
+    @IntDef(value = {
                     CALL_STATE_CONFIRMED,
                     CALL_STATE_TERMINATED
             },
@@ -59,8 +58,7 @@
     public @interface ExternalCallState {}
 
     /**@hide*/
-    @IntDef(flag = true,
-            value = {
+    @IntDef(value = {
                     ImsCallProfile.CALL_TYPE_VOICE,
                     ImsCallProfile.CALL_TYPE_VT_TX,
                     ImsCallProfile.CALL_TYPE_VT_RX,
diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java
index fb8e5d3..868dea6a 100644
--- a/telephony/java/android/telephony/ims/ImsSsData.java
+++ b/telephony/java/android/telephony/ims/ImsSsData.java
@@ -72,7 +72,7 @@
 
 
     /**@hide*/
-    @IntDef(flag = true, prefix = {"SS_"}, value = {
+    @IntDef(prefix = {"SS_"}, value = {
             SS_ACTIVATION,
             SS_DEACTIVATION,
             SS_INTERROGATION,
@@ -89,7 +89,7 @@
     public static final int SS_ERASURE = 4;
 
     /**@hide*/
-    @IntDef(flag = true, prefix = {"SS_"}, value = {
+    @IntDef(prefix = {"SS_"}, value = {
             SS_ALL_TELE_AND_BEARER_SERVICES,
             SS_ALL_TELESEVICES,
             SS_TELEPHONY,
@@ -190,7 +190,7 @@
     public static final int RESULT_SUCCESS = 0;
 
     /** @hide */
-    @IntDef(flag = true, prefix = { "SS_" }, value = {
+    @IntDef(prefix = { "SS_" }, value = {
             SS_CFU,
             SS_CF_BUSY,
             SS_CF_NO_REPLY,
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index d12a6ae..5848be8 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -105,10 +105,17 @@
     public @interface RequestResult {}
 
     /**
+     * The base class of {@link OptionsBuilder} and {@link PresenceBuilder}
+     */
+    public static abstract class RcsUcsCapabilityBuilder {
+        public abstract @NonNull RcsContactUceCapability build();
+    }
+
+    /**
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through SIP OPTIONS.
      */
-    public static class OptionsBuilder {
+    public static class OptionsBuilder extends RcsUcsCapabilityBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -155,6 +162,7 @@
         /**
          * @return the constructed instance.
          */
+        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
@@ -164,7 +172,7 @@
      * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
      * queried through a presence server.
      */
-    public static class PresenceBuilder {
+    public static class PresenceBuilder extends RcsUcsCapabilityBuilder {
 
         private final RcsContactUceCapability mCapabilities;
 
@@ -205,6 +213,7 @@
         /**
          * @return the RcsContactUceCapability instance.
          */
+        @Override
         public @NonNull RcsContactUceCapability build() {
             return mCapabilities;
         }
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
index 6bfdc2c..c3cc1ed 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConnection.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -17,6 +17,7 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.stub.SipDelegate;
 
 /**
@@ -36,6 +37,7 @@
  * @see SipDelegateManager#createSipDelegate
  * @hide
  */
+@SystemApi
 public interface SipDelegateConnection {
 
     /**
@@ -47,9 +49,8 @@
      * @param sipMessage The SipMessage to be sent.
      * @param configVersion The SipDelegateImsConfiguration version used to construct the
      *                      SipMessage. See {@link SipDelegateImsConfiguration#getVersion} for more
-     *                      information on this parameter and why it is used.
      */
-    void sendMessage(@NonNull SipMessage sipMessage, int configVersion);
+    void sendMessage(@NonNull SipMessage sipMessage, long configVersion);
 
     /**
      * Notify the {@link SipDelegate} that a SIP message received from
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 8abd0ee..eddbb10 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -17,7 +17,10 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -47,7 +50,8 @@
  * update.
  * @hide
  */
-public class SipDelegateImsConfiguration implements Parcelable {
+@SystemApi
+public final class SipDelegateImsConfiguration implements Parcelable {
 
     /**
      * IPV4 Address type.
@@ -354,7 +358,7 @@
     /**
      * Builder class to be used when constructing a new SipDelegateImsConfiguration.
      */
-    public static class Builder {
+    public static final class Builder {
         private final long mVersion;
         private final PersistableBundle mBundle;
 
@@ -381,7 +385,10 @@
         /**
          * Put a string value into this configuration bundle for the given key.
          */
-        public Builder putString(@StringConfigKey String key, String value) {
+        // getString is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addString(@NonNull @StringConfigKey String key,
+                @NonNull String value) {
             mBundle.putString(key, value);
             return this;
         }
@@ -389,7 +396,9 @@
         /**
          * Replace the existing default value with a new value for a given key.
          */
-        public Builder putInt(@IntConfigKey String key, int value) {
+        // getInt is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addInt(@NonNull @IntConfigKey String key, int value) {
             mBundle.putInt(key, value);
             return this;
         }
@@ -397,7 +406,9 @@
         /**
          * Replace the existing default value with a new value for a given key.
          */
-        public Builder putBoolean(@BooleanConfigKey String key, boolean value) {
+        // getBoolean is available below.
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder addBoolean(@NonNull @BooleanConfigKey String key, boolean value) {
             mBundle.putBoolean(key, value);
             return this;
         }
@@ -405,7 +416,7 @@
         /**
          * @return a new SipDelegateImsConfiguration from this Builder.
          */
-        public SipDelegateImsConfiguration build() {
+        public @NonNull SipDelegateImsConfiguration build() {
             return new SipDelegateImsConfiguration(mVersion, mBundle);
         }
     }
@@ -424,30 +435,38 @@
     }
 
     /**
+     * @return {@code true} if this configuration object has a an entry for the key specified,
+     * {@code false} if it does not.
+     */
+    public boolean containsKey(@NonNull String key) {
+        return mBundle.containsKey(key);
+    }
+
+    /**
      * @return the string value associated with a given key or {@code null} if it doesn't exist.
      */
-    public @StringConfigKey String getString(String key) {
+    public @Nullable @StringConfigKey String getString(@NonNull String key) {
         return mBundle.getString(key);
     }
 
     /**
-     * @return the Integer value associated with a given key or {@code null} if the value doesn't
-     * exist.
+     * @return the integer value associated with a given key if it exists or the supplied default
+     * value if it does not.
      */
-    public @IntConfigKey Integer getInt(String key) {
+    public @IntConfigKey int getInt(@NonNull String key, int defaultValue) {
         if (!mBundle.containsKey(key)) {
-            return null;
+            return defaultValue;
         }
         return mBundle.getInt(key);
     }
 
     /**
-     * @return the Integer value associated with a given key or {@code null} if the value doesn't
-     * exist.
+     * @return the boolean value associated with a given key or the supplied default value if the
+     * value doesn't exist in the bundle.
      */
-    public @BooleanConfigKey Boolean getBoolen(String key) {
+    public @BooleanConfigKey boolean getBoolean(@NonNull String key, boolean defaultValue) {
         if (!mBundle.containsKey(key)) {
-            return null;
+            return defaultValue;
         }
         return mBundle.getBoolean(key);
     }
@@ -455,7 +474,7 @@
     /**
      * @return a shallow copy of the full configuration.
      */
-    public PersistableBundle copyBundle() {
+    public @NonNull PersistableBundle copyBundle() {
         return new PersistableBundle(mBundle);
     }
 
@@ -479,12 +498,12 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeLong(mVersion);
         dest.writePersistableBundle(mBundle);
     }
 
-    public static final Creator<SipDelegateImsConfiguration> CREATOR =
+    public static final @NonNull Creator<SipDelegateImsConfiguration> CREATOR =
             new Creator<SipDelegateImsConfiguration>() {
         @Override
         public SipDelegateImsConfiguration createFromParcel(Parcel source) {
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 190a792..2ec88ff 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -54,7 +54,6 @@
      * The SIP message has failed being sent or received for an unknown reason.
      * <p>
      * The caller should retry a message that failed with this response.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_UNKNOWN = 0;
 
@@ -64,47 +63,40 @@
      * <p>
      * This is considered a permanent error and the system will automatically begin the teardown and
      * destruction of the SipDelegate. No further messages should be sent on this transport.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_DELEGATE_DEAD = 1;
 
     /**
      * The message has not been sent/received because the delegate is in the process of closing and
      * has become unavailable. No further messages should be sent/received on this delegate.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_DELEGATE_CLOSED = 2;
 
     /**
      * The SIP message has an invalid start line and the message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_START_LINE = 3;
 
     /**
      * One or more of the header fields in the header section of the outgoing SIP message is invalid
      * and the SIP message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS = 4;
 
     /**
      * The body content of the SIP message is invalid and the message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT = 5;
 
     /**
      * The feature tag associated with the outgoing message does not match any known feature tags
      * and this message can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG = 6;
 
     /**
      * The feature tag associated with the outgoing message is not enabled for the associated
      * SipDelegateConnection and can not be sent.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE = 7;
 
@@ -113,7 +105,6 @@
      * <p>
      * This message should be retried when connectivity to the network is re-established. See
      * {@link android.net.ConnectivityManager.NetworkCallback} for how this can be determined.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE = 8;
 
@@ -124,7 +115,6 @@
      * This is considered a temporary failure, the message should not be retried until an IMS
      * registration change callback is received via
      * {@link DelegateConnectionStateCallback#onFeatureTagStatusChanged}
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_NOT_REGISTERED = 9;
 
@@ -135,7 +125,6 @@
      * <p>
      * The @link SipMessage} should be recreated using the newest
      * {@link SipDelegateImsConfiguration} and sent again.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION = 10;
 
@@ -146,7 +135,6 @@
      * This is considered a temporary error and the {@link SipDelegateConnection} should resend the
      * message once {@link DelegateRegistrationState#DEREGISTERING_REASON_FEATURE_TAGS_CHANGING} is
      * no longer reported.
-     * @hide
      */
     public static final int MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION = 11;
 
@@ -171,7 +159,6 @@
 
     /**
      * Access to use this feature tag has been denied for an unknown reason.
-     * @hide
      */
     public static final int DENIED_REASON_UNKNOWN = 0;
 
@@ -179,14 +166,12 @@
      * This feature tag is allowed to be used by this SipDelegateConnection, but it is in use by
      * another SipDelegateConnection and can not be associated with this delegate. The feature tag
      * will stay in this state until the feature tag is release by the other application.
-     * @hide
      */
     public static final int DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE = 1;
 
     /**
      * Access to use this feature tag has been denied because this application does not have the
      * permissions required to access this feature tag.
-     * @hide
      */
     public static final int DENIED_REASON_NOT_ALLOWED = 2;
 
@@ -194,14 +179,12 @@
      * Access to use this feature tag has been denied because single registration is not allowed by
      * the carrier at this time. The application should fall back to dual registration if
      * applicable.
-     * @hide
      */
     public static final int DENIED_REASON_SINGLE_REGISTRATION_NOT_ALLOWED = 3;
 
     /**
      * This feature tag is not recognized as a valid feature tag by the SipDelegate and has been
      * denied.
-     * @hide
      */
     public static final int DENIED_REASON_INVALID = 4;
 
@@ -218,33 +201,28 @@
 
     /**
      * The SipDelegate has closed due to an unknown reason.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_UNKNOWN = 0;
 
     /**
      * The SipDelegate has closed because the IMS service has died unexpectedly.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_SERVICE_DEAD = 1;
 
     /**
      * The SipDelegate has closed because the IMS application has requested that the connection be
      * destroyed.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_REQUESTED_BY_APP = 2;
 
     /**
      * The SipDelegate has been closed due to the user disabling RCS.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_USER_DISABLED_RCS = 3;
 
     /**
      * The SipDelegate has been closed due to the subscription associated with this delegate being
      * torn down.
-     * @hide
      */
     public static final int SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN = 4;
 
@@ -331,7 +309,6 @@
      *           SipDelegateConnection.
      * @throws ImsException Thrown if there was a problem communicating with the ImsService
      * associated with this SipDelegateManager. See {@link ImsException#getCode()}.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void createSipDelegate(@NonNull DelegateRequest request, @NonNull Executor executor,
@@ -366,7 +343,6 @@
      * This will also clean up all related callbacks in the associated ImsService.
      * @param delegateConnection The SipDelegateConnection to destroy.
      * @param reason The reason for why this SipDelegateConnection was destroyed.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void destroySipDelegate(@NonNull SipDelegateConnection delegateConnection,
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index c3b1be2..1539224 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -17,10 +17,14 @@
 package android.telephony.ims;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Represents a partially encoded SIP message. See RFC 3261 for more information on how SIP
  * messages are structured and used.
@@ -29,6 +33,7 @@
  * verification and should not be used as a generic SIP message container.
  * @hide
  */
+@SystemApi
 public final class SipMessage implements Parcelable {
     // Should not be set to true for production!
     private static final boolean IS_DEBUGGING = Build.IS_ENG;
@@ -95,14 +100,14 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mStartLine);
         dest.writeString(mHeaderSection);
         dest.writeInt(mContent.length);
         dest.writeByteArray(mContent);
     }
 
-    public static final Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
+    public static final @NonNull Creator<SipMessage> CREATOR = new Creator<SipMessage>() {
         @Override
         public SipMessage createFromParcel(Parcel source) {
             return new SipMessage(source);
@@ -152,4 +157,21 @@
         }
         return startLine;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SipMessage that = (SipMessage) o;
+        return mStartLine.equals(that.mStartLine)
+                && mHeaderSection.equals(that.mHeaderSection)
+                && Arrays.equals(mContent, that.mContent);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mStartLine, mHeaderSection);
+        result = 31 * result + Arrays.hashCode(mContent);
+        return result;
+    }
 }
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
index 477ee95..5d6766a6 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
@@ -23,7 +23,7 @@
  * {@hide}
  */
 oneway interface ISipDelegate {
-    void sendMessage(in SipMessage sipMessage, int configVersion);
+    void sendMessage(in SipMessage sipMessage, long configVersion);
     void notifyMessageReceived(in String viaTransactionId);
     void notifyMessageReceiveError(in String viaTransactionId, int reason);
 
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index a7f62cc..522ad81 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -29,12 +29,13 @@
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.SipDelegate;
 
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Set;
 import java.util.concurrent.Executor;
 
 /**
  * Implementation of callbacks by wrapping the internal AIDL from telephony. Also implements
- * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, List)} is called
+ * ISipDelegate internally when {@link DelegateStateCallback#onCreated(SipDelegate, Set)} is called
  * in order to trampoline events back to telephony.
  * @hide
  */
@@ -42,7 +43,7 @@
 
     private final ISipDelegate.Stub mDelegateBinder = new ISipDelegate.Stub() {
         @Override
-        public void sendMessage(SipMessage sipMessage, int configVersion) {
+        public void sendMessage(SipMessage sipMessage, long configVersion) {
             SipDelegate d = mDelegate;
             final long token = Binder.clearCallingIdentity();
             try {
@@ -136,10 +137,10 @@
 
     @Override
     public void onCreated(@NonNull SipDelegate delegate,
-            @Nullable List<FeatureTagState> deniedTags) {
+            @Nullable Set<FeatureTagState> deniedTags) {
         mDelegate = delegate;
         try {
-            mStateBinder.onCreated(mDelegateBinder, deniedTags);
+            mStateBinder.onCreated(mDelegateBinder, new ArrayList<>(deniedTags));
         } catch (RemoteException e) {
             // BinderDied will trigger destroySipDelegate, so just ignore this locally.
         }
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 3bd1a46..29ba8e2 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -158,7 +158,7 @@
     }
 
     @Override
-    public void sendMessage(SipMessage sipMessage, int configVersion) {
+    public void sendMessage(SipMessage sipMessage, long configVersion) {
         try {
             ISipDelegate conn = getSipDelegateBinder();
             if (conn == null) {
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
index 59f9601..eefe849 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionMessageCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.SipDelegateConnection;
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
@@ -26,6 +27,7 @@
  * messages as well as the result of sending a SIP message.
  * @hide
  */
+@SystemApi
 public interface DelegateConnectionMessageCallback {
 
     /**
@@ -49,6 +51,6 @@
      *                         previously sent {@link SipMessage}.
      * @param reason The reason for the failure.
      */
-    void onMessageSendFailure(String viaTransactionId,
+    void onMessageSendFailure(@NonNull String viaTransactionId,
             @SipDelegateManager.MessageFailureReason int reason);
 }
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
index 9761805..02218ea 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.DelegateRegistrationState;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
@@ -58,6 +59,7 @@
  *
  * @hide
  */
+@SystemApi
 public interface DelegateConnectionStateCallback {
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index a6f5c45..153d687 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -49,8 +49,7 @@
      * @hide
      */
     // Defines the underlying radio technology type that we have registered for IMS over.
-    @IntDef(flag = true,
-            value = {
+    @IntDef(value = {
                     REGISTRATION_TECH_NONE,
                     REGISTRATION_TECH_LTE,
                     REGISTRATION_TECH_IWLAN
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
index 3ec9709..d7e7b62 100644
--- a/telephony/java/android/telephony/ims/stub/SipDelegate.java
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.telephony.ims.DelegateMessageCallback;
 import android.telephony.ims.ImsService;
 import android.telephony.ims.SipDelegateImsConfiguration;
@@ -40,6 +41,7 @@
  * {@link android.telephony.ims.DelegateStateCallback} for more information.
  * @hide
  */
+@SystemApi
 public interface SipDelegate {
 
     /**
@@ -57,7 +59,7 @@
      *         {@link DelegateMessageCallback#onMessageSendFailure} should be called with code
      *         {@link SipDelegateManager#MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION}.
      */
-    void sendMessage(@NonNull SipMessage message, int configVersion);
+    void sendMessage(@NonNull SipMessage message, long configVersion);
 
     /**
      * The framework is requesting that routing resources associated with the SIP dialog using the
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 93d438c..1f74c09 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -17,6 +17,7 @@
 package android.telephony.ims.stub;
 
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.IBinder;
@@ -32,7 +33,6 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
@@ -99,7 +99,8 @@
     /**
      * Called by the Telephony framework to request the creation of a new {@link SipDelegate}.
      * <p>
-     * The implementation must call {@link DelegateStateCallback#onCreated(SipDelegate, List)} with
+     * The implementation must call
+     * {@link DelegateStateCallback#onCreated(SipDelegate, java.util.Set)} with
      * the {@link SipDelegate} that is associated with the {@link DelegateRequest}.
      * <p>
      * This method will be called on the Executor specified in
@@ -112,8 +113,9 @@
      *           for the SipDelegate.
      * @param mc A callback back to the remote application to be used to send SIP messages to the
      *           remote application and acknowledge the sending of outgoing SIP messages.
-     * @hide
      */
+    // executor used is defined in the constructor.
+    @SuppressLint("ExecutorRegistration")
     public void createSipDelegate(int subscriptionId, @NonNull DelegateRequest request,
             @NonNull DelegateStateCallback dc, @NonNull DelegateMessageCallback mc) {
         throw new UnsupportedOperationException("createSipDelegate not implemented!");
@@ -130,7 +132,6 @@
      * @param delegate The delegate to be destroyed.
      * @param reason The reason the remote connection to this {@link SipDelegate} is being
      *         destroyed.
-     * @hide
      */
     public void destroySipDelegate(@NonNull SipDelegate delegate,
             @SipDelegateManager.SipDelegateDestroyReason int reason) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b524549..5d4fdd0 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -857,6 +857,11 @@
             in int[] featureTypes, in String packageName);
 
     /**
+     *  @return true if the ImsService cleared any carrier ImsService overrides, false otherwise.
+     */
+    boolean clearCarrierImsServiceOverride(int slotIndex);
+
+    /**
     * @return the package name of the carrier/device ImsService associated with this slot.
     */
     String getBoundImsServicePackage(int slotIndex, boolean isCarrierImsService, int featureType);
diff --git a/tests/BootImageProfileTest/TEST_MAPPING b/tests/BootImageProfileTest/TEST_MAPPING
new file mode 100644
index 0000000..1b569f9
--- /dev/null
+++ b/tests/BootImageProfileTest/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "BootImageProfileTest"
+    }
+  ]
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index 1f03c4d..686ddcb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -105,8 +105,7 @@
                                 configuration.endRotation)
                             navBarLayerIsAlwaysVisible(enabled = false)
                             statusBarLayerIsAlwaysVisible(enabled = false)
-                            visibleLayersShownMoreThanOneConsecutiveEntry(
-                                    enabled = Surface.ROTATION_0 == configuration.endRotation)
+                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 174541970)
 
                             appLayerReplacesWallpaperLayer(testApp)
                         }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 52718be..3d8deb5 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -34,7 +34,6 @@
 
 import com.android.cts.install.lib.Install;
 import com.android.cts.install.lib.InstallUtils;
-import com.android.cts.install.lib.LocalIntentSender;
 import com.android.cts.install.lib.TestApp;
 import com.android.cts.install.lib.Uninstall;
 import com.android.cts.rollback.lib.Rollback;
@@ -258,10 +257,6 @@
                 .getPackageManager().getPackageInstaller();
         pi.abandonSession(sessionId);
 
-        // Remove the first intent sender result, so that the next staged install session does not
-        // erroneously think that it has itself been abandoned.
-        // TODO(b/136260017): Restructure LocalIntentSender to negate the need for this step.
-        LocalIntentSender.getIntentSenderResult();
         Install.single(TestApp.A2).setStaged().setEnableRollback().commit();
     }
 
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index b77ed6a..cade5ba 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -22,10 +22,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Build;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -34,7 +37,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(AndroidJUnit4.class)
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@RunWith(DevSdkIgnoreRunner.class)
 @SmallTest
 public class OemNetworkPreferencesTest {
 
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 3619937..ba87dc5 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1058,7 +1058,9 @@
 
         public void setUids(Set<UidRange> uids) {
             mNetworkCapabilities.setUids(uids);
-            updateCapabilitiesInternal(null /* defaultNetwork */, true);
+            if (mAgentRegistered) {
+                mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true);
+            }
         }
 
         public void setVpnType(int vpnType) {
@@ -1089,6 +1091,10 @@
             mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
                     mNetworkCapabilities);
             mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
+            verify(mNetworkManagementService, times(1))
+                    .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
+            verify(mNetworkManagementService, never())
+                    .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());
             mAgentRegistered = true;
             mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
             mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
@@ -1143,28 +1149,6 @@
             mMockNetworkAgent.sendLinkProperties(lp);
         }
 
-        private NetworkCapabilities updateCapabilitiesInternal(Network defaultNetwork,
-                boolean sendToConnectivityService) {
-            if (!mAgentRegistered) return null;
-            super.updateCapabilities(defaultNetwork);
-            // Because super.updateCapabilities will update the capabilities of the agent but
-            // not the mock agent, the mock agent needs to know about them.
-            copyCapabilitiesToNetworkAgent(sendToConnectivityService);
-            return new NetworkCapabilities(mNetworkCapabilities);
-        }
-
-        private void copyCapabilitiesToNetworkAgent(boolean sendToConnectivityService) {
-            if (null != mMockNetworkAgent) {
-                mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
-                        sendToConnectivityService);
-            }
-        }
-
-        @Override
-        public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
-            return updateCapabilitiesInternal(defaultNetwork, false);
-        }
-
         public void disconnect() {
             if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
             mAgentRegistered = false;
@@ -6922,8 +6906,8 @@
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
         mMockVpn.establish(lp, VPN_UID, vpnRange);
 
-        // Connected VPN should have interface rules set up. There are two expected invocations,
-        // one during VPN uid update, one during VPN LinkProperties update
+        // A connected VPN should have interface rules set up. There are two expected invocations,
+        // one during the VPN initial connection, one during the VPN LinkProperties update.
         ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
         verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture());
         assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID);
@@ -7438,20 +7422,14 @@
         setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
                 Manifest.permission.ACCESS_FINE_LOCATION);
 
-        // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
-        // active
-        final VpnInfo info = new VpnInfo();
-        info.ownerUid = Process.myUid();
-        info.vpnIface = VPN_IFNAME;
-        mMockVpn.setVpnInfo(info);
-
         mMockVpn.establishForMyUid();
-        waitForIdle();
 
+        // Wait for networks to connect and broadcasts to be sent before removing permissions.
+        waitForIdle();
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
-
         assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
+        waitForIdle();
         assertTrue(
                 "Active VPN permission not applied",
                 mService.checkConnectivityDiagnosticsPermissions(
@@ -7459,6 +7437,7 @@
                         mContext.getOpPackageName()));
 
         assertTrue(mService.setUnderlyingNetworksForVpn(null));
+        waitForIdle();
         assertFalse(
                 "VPN shouldn't receive callback on non-underlying network",
                 mService.checkConnectivityDiagnosticsPermissions(
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 1dcc07c..d0db55f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -41,6 +41,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
@@ -86,10 +87,10 @@
 import android.os.Bundle;
 import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
-import android.os.Looper;
 import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.test.TestLooper;
 import android.provider.Settings;
 import android.security.Credentials;
 import android.security.KeyStore;
@@ -100,6 +101,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
+import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.server.IpSecService;
@@ -223,6 +225,8 @@
                 .thenReturn(mNotificationManager);
         when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
                 .thenReturn(mConnectivityManager);
+        when(mContext.getSystemServiceName(eq(ConnectivityManager.class)))
+                .thenReturn(Context.CONNECTIVITY_SERVICE);
         when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
         when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
                 .thenReturn(Resources.getSystem().getString(
@@ -589,7 +593,7 @@
     }
 
     @Test
-    public void testNotificationShownForAlwaysOnApp() {
+    public void testNotificationShownForAlwaysOnApp() throws Exception {
         final UserHandle userHandle = UserHandle.of(primaryUser.id);
         final Vpn vpn = createVpn(primaryUser.id);
         setMockedUsers(primaryUser);
@@ -619,7 +623,6 @@
 
     @Test
     public void testCapabilities() {
-        final Vpn vpn = createVpn(primaryUser.id);
         setMockedUsers(primaryUser);
 
         final Network mobile = new Network(1);
@@ -1037,7 +1040,7 @@
         when(exception.getErrorType())
                 .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED);
 
-        final Vpn vpn = startLegacyVpn(mVpnProfile);
+        final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile));
         final NetworkCallback cb = triggerOnAvailableAndGetCallback();
 
         // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
@@ -1048,20 +1051,20 @@
         ikeCb.onClosedExceptionally(exception);
 
         verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
-        assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+        assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
     }
 
     @Test
     public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception {
         when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
                 .thenThrow(new IllegalArgumentException());
-        final Vpn vpn = startLegacyVpn(mVpnProfile);
+        final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile);
         final NetworkCallback cb = triggerOnAvailableAndGetCallback();
 
         // Wait for createIkeSession() to be called before proceeding in order to ensure consistent
         // state
         verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
-        assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+        assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
     }
 
     private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
@@ -1100,8 +1103,7 @@
         // a subsequent CL.
     }
 
-    public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception {
-        final Vpn vpn = createVpn(primaryUser.id);
+    private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
         setMockedUsers(primaryUser);
 
         // Dummy egress interface
@@ -1118,7 +1120,7 @@
 
     @Test
     public void testStartPlatformVpn() throws Exception {
-        startLegacyVpn(mVpnProfile);
+        startLegacyVpn(createVpn(primaryUser.id), mVpnProfile);
         // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
         // a subsequent patch.
     }
@@ -1153,7 +1155,7 @@
                     legacyRunnerReady.open();
                     return new Network(102);
                 });
-        final Vpn vpn = startLegacyVpn(profile);
+        final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
         final TestDeps deps = (TestDeps) vpn.mDeps;
         try {
             // udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK
@@ -1287,8 +1289,13 @@
         doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
         when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
                 .thenReturn(asUserContext);
-        return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService,
+        final TestLooper testLooper = new TestLooper();
+        final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
                 userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+        verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
+                provider -> provider.getName().contains("VpnNetworkProvider")
+        ));
+        return vpn;
     }
 
     private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/tools/codegen/src/com/android/codegen/ClassPrinter.kt b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
index b90e1bb..8cae14a 100644
--- a/tools/codegen/src/com/android/codegen/ClassPrinter.kt
+++ b/tools/codegen/src/com/android/codegen/ClassPrinter.kt
@@ -145,7 +145,6 @@
             }
             return when {
                 cliArgs.contains("--hidden-$kebabCase") -> true
-                this == FeatureFlag.BUILD_UPON -> FeatureFlag.BUILDER.hidden
                 else -> false
             }
         }
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 6a635d0..d9ad649 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.21"
+const val CODEGEN_VERSION = "1.0.22"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
index 84faeea..4c1fa6e 100644
--- a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -33,13 +33,12 @@
 import javax.tools.StandardLocation.CLASS_OUTPUT
 import kotlin.collections.set
 
-
 /**
  * The IntDefProcessor is intended to generate a mapping from ints to their respective string
  * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping.
  *
- * The processor will run when building :frameworks-all and dump all the IntDef mappings found the
- * files the make up :frameworks-all as json to outputPath.
+ * The processor will run when building :framework-minus-apex-intdefs and dump all the IntDef
+ * mappings found in the files that make up the build target as json to outputPath.
  */
 class IntDefProcessor : AbstractProcessor() {
     private val outputName = "intDefMapping.json"
@@ -72,8 +71,8 @@
     }
 
     private fun generateIntDefMapping(
-            annotatedElement: TypeElement,
-            annotationType: TypeElement
+        annotatedElement: TypeElement,
+        annotationType: TypeElement
     ): Map<Int, String> {
         // LinkedHashMap makes sure ordering is the same as in the code
         val mapping = LinkedHashMap<Int, String>()
@@ -151,8 +150,8 @@
 
     companion object {
         fun serializeTo(
-                annotationTypeToIntDefMapping: Map<String, IntDefMapping>,
-                writer: Writer
+            annotationTypeToIntDefMapping: Map<String, IntDefMapping>,
+            writer: Writer
         ) {
             val indent = "  "
 
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 911f2fb..ce2b8ca 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -14,6 +14,7 @@
     field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE = -3; // 0xfffffffd
     field public static final int EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED = -8; // 0xfffffff8
     field public static final int EASY_CONNECT_EVENT_FAILURE_TIMEOUT = -6; // 0xfffffffa
+    field public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13; // 0xfffffff3
   }
 
   public final class ScanResult implements android.os.Parcelable {
diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt
index c4a1766..5e8fc07 100644
--- a/wifi/api/system-current.txt
+++ b/wifi/api/system-current.txt
@@ -3,6 +3,7 @@
 
   public abstract class EasyConnectStatusCallback {
     ctor public EasyConnectStatusCallback();
+    method public void onBootstrapUriGenerated(@NonNull String);
     method public abstract void onConfiguratorSuccess(int);
     method public abstract void onEnrolleeSuccess(int);
     method public void onFailure(int);
@@ -497,6 +498,7 @@
     method @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE) public boolean setWifiConnectedNetworkScorer(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.WifiConnectedNetworkScorer);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+    method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeResponder(@Nullable String, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startLocalOnlyHotspot(@NonNull android.net.wifi.SoftApConfiguration, @Nullable java.util.concurrent.Executor, @Nullable android.net.wifi.WifiManager.LocalOnlyHotspotCallback);
     method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
@@ -523,6 +525,14 @@
     field public static final int DEVICE_MOBILITY_STATE_LOW_MVMT = 2; // 0x2
     field public static final int DEVICE_MOBILITY_STATE_STATIONARY = 3; // 0x3
     field public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; // 0x0
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3; // 0x3
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4; // 0x4
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5; // 0x5
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT = 0; // 0x0
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0; // 0x0
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1; // 0x1
+    field public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2; // 0x2
+    field public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40; // 0x28
     field public static final int EASY_CONNECT_NETWORK_ROLE_AP = 1; // 0x1
     field public static final int EASY_CONNECT_NETWORK_ROLE_STA = 0; // 0x0
     field public static final String EXTRA_CHANGE_REASON = "changeReason";
diff --git a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
index 6c2e6dd..ee70255 100644
--- a/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
+++ b/wifi/java/android/net/wifi/EasyConnectStatusCallback.java
@@ -161,6 +161,11 @@
      */
     public static final int EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION = -12;
 
+    /**
+     * Easy Connect Failure event: System failed to generate DPP URI.
+     */
+    public static final int EASY_CONNECT_EVENT_FAILURE_URI_GENERATION = -13;
+
     /** @hide */
     @IntDef(prefix = {"EASY_CONNECT_EVENT_FAILURE_"}, value = {
             EASY_CONNECT_EVENT_FAILURE_INVALID_URI,
@@ -175,6 +180,7 @@
             EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK,
             EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION,
             EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION,
+            EASY_CONNECT_EVENT_FAILURE_URI_GENERATION,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface EasyConnectFailureStatusCode {
@@ -264,4 +270,17 @@
      */
     @SystemApi
     public abstract void onProgress(@EasyConnectProgressStatusCode int code);
+
+    /**
+     * Called when local Easy Connect Responder successfully generates a DPP URI from
+     * the supplicant. This callback is the first successful outcome
+     * of a Easy Connect Responder flow starting with
+     * {@link WifiManager#startEasyConnectAsEnrolleeResponder(String, int, Executor,
+     * EasyConnectStatusCallback)} .
+     *
+     * @param uri DPP URI from the supplicant.
+     * @hide
+     */
+    @SystemApi
+    public void onBootstrapUriGenerated(@NonNull String uri) {};
 }
diff --git a/wifi/java/android/net/wifi/IDppCallback.aidl b/wifi/java/android/net/wifi/IDppCallback.aidl
index d7a958a..dcbe8468 100644
--- a/wifi/java/android/net/wifi/IDppCallback.aidl
+++ b/wifi/java/android/net/wifi/IDppCallback.aidl
@@ -45,4 +45,10 @@
      * to show progress.
      */
     void onProgress(int status);
+
+    /**
+     * Called when local DPP Responder successfully generates a URI.
+     */
+    void onBootstrapUriGenerated(String uri);
+
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index cc864ea..0b3c057 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -235,6 +235,9 @@
     void startDppAsEnrolleeInitiator(in IBinder binder, in String configuratorUri,
         in IDppCallback callback);
 
+    void startDppAsEnrolleeResponder(in IBinder binder, in String deviceInfo, int curve,
+        in IDppCallback callback);
+
     void stopDppSession();
 
     void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index da7c9c0..d089e1a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -5551,6 +5551,89 @@
     }
 
     /**
+     * Easy Connect Device information maximum allowed length.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH = 40;
+
+    /**
+     * Easy Connect Cryptography Curve name: prime256v1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1 = 0;
+
+    /**
+     * Easy Connect Cryptography Curve name: secp384r1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1 = 1;
+
+    /**
+     * Easy Connect Cryptography Curve name: secp521r1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1 = 2;
+
+
+    /**
+     * Easy Connect Cryptography Curve name: brainpoolP256r1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1 = 3;
+
+
+    /**
+     * Easy Connect Cryptography Curve name: brainpoolP384r1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1 = 4;
+
+
+    /**
+     * Easy Connect Cryptography Curve name: brainpoolP512r1
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1 = 5;
+
+    /**
+     * Easy Connect Cryptography Curve name: default
+     * This allows framework to choose manadatory curve prime256v1.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT =
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1;
+
+    /** @hide */
+    @IntDef(prefix = {"EASY_CONNECT_CRYPTOGRAPHY_CURVE_"}, value = {
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1,
+            EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EasyConnectCryptographyCurve {
+    }
+
+    /**
      * Start Easy Connect (DPP) in Configurator-Initiator role. The current device will initiate
      * Easy Connect bootstrapping with a peer, and configure the peer with the SSID and password of
      * the specified network using the Easy Connect protocol on an encrypted link.
@@ -5606,6 +5689,52 @@
     }
 
     /**
+     * Start Easy Connect (DPP) in Enrollee-Responder role.
+     * The device will:
+     * 1. Generate a DPP bootstrap URI and return it using the
+     * {@link EasyConnectStatusCallback#onBootstrapUriGenerated(String)} method.
+     * 2. Start DPP as a Responder, waiting for an Initiator device to start the DPP
+     * authentication process.
+     * The caller should use the URI provided in step #1, for instance display it as a QR code
+     * or communicate it in some other way to the initiator device.
+     *
+     * @param deviceInfo      Device specific information to add to the DPP URI. This field allows
+     *                        the users of the configurators to identify the device.
+     *                        Optional - if not provided or in case of an empty string,
+     *                        Info field (I:) will be skipped in the generated DPP URI.
+     *                        Allowed Range of ASCII characters in deviceInfo - %x20-7E.
+     *                        semicolon and space are not allowed.
+     *                        Due to the limitation of maximum allowed characters in QR code,
+     *                        framework limits to a max of
+     *                        {@link #EASY_CONNECT_DEVICE_INFO_MAXIMUM_LENGTH} characters in
+     *                        deviceInfo.
+     *                        Violation of these rules will result in an exception.
+     * @param curve           Elliptic curve cryptography used to generate DPP
+     *                        public/private key pair. If application is not interested in a
+     *                        specific curve, choose default curve
+     *                        {@link #EASY_CONNECT_CRYPTOGRAPHY_CURVE_DEFAULT}.
+     * @param callback        Callback for status updates
+     * @param executor        The Executor on which to run the callback.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(anyOf = {
+            android.Manifest.permission.NETWORK_SETTINGS,
+            android.Manifest.permission.NETWORK_SETUP_WIZARD})
+    public void startEasyConnectAsEnrolleeResponder(@Nullable String deviceInfo,
+            @EasyConnectCryptographyCurve int curve,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull EasyConnectStatusCallback callback) {
+        Binder binder = new Binder();
+        try {
+            mService.startDppAsEnrolleeResponder(binder, deviceInfo, curve,
+                    new EasyConnectCallbackProxy(executor, callback));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Stop or abort a current Easy Connect (DPP) session. This call, once processed, will
      * terminate any ongoing transaction, and clean up all associated resources. Caller should not
      * expect any callbacks once this call is made. However, due to the asynchronous nature of
@@ -5679,6 +5808,15 @@
                 mEasyConnectStatusCallback.onProgress(status);
             });
         }
+
+        @Override
+        public void onBootstrapUriGenerated(String uri) {
+            Log.d(TAG, "Easy Connect onBootstrapUriGenerated callback");
+            Binder.clearCallingIdentity();
+            mExecutor.execute(() -> {
+                mEasyConnectStatusCallback.onBootstrapUriGenerated(uri);
+            });
+        }
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java
index b101414..cf37b78 100644
--- a/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java
+++ b/wifi/tests/src/android/net/wifi/EasyConnectStatusCallbackTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.NonNull;
 import android.util.SparseArray;
 
 import androidx.test.filters.SmallTest;
@@ -52,6 +53,11 @@
             mOnFailureR1EventReceived = true;
             mLastCode = code;
         }
+
+        @Override
+        public void onBootstrapUriGenerated(@NonNull String uri) {
+
+        }
     };
     private boolean mOnFailureR1EventReceived;
     private int mLastCode;