Merge "[pm] reject installs of apks with renderscript on 64-bit only devices" into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index 62a3c38..5a24251 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19869,6 +19869,10 @@
     field public static final int ENCODING_IEC61937 = 13; // 0xd
     field public static final int ENCODING_INVALID = 0; // 0x0
     field public static final int ENCODING_MP3 = 9; // 0x9
+    field public static final int ENCODING_MPEGH_BL_L3 = 23; // 0x17
+    field public static final int ENCODING_MPEGH_BL_L4 = 24; // 0x18
+    field public static final int ENCODING_MPEGH_LC_L3 = 25; // 0x19
+    field public static final int ENCODING_MPEGH_LC_L4 = 26; // 0x1a
     field public static final int ENCODING_OPUS = 20; // 0x14
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_24BIT_PACKED = 21; // 0x15
@@ -53106,10 +53110,12 @@
     ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet);
     ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int);
     ctor @Deprecated public AnalogClock(android.content.Context, android.util.AttributeSet, int, int);
+    method @Deprecated @Nullable public String getTimeZone();
     method @Deprecated public void setDial(@NonNull android.graphics.drawable.Icon);
     method @Deprecated public void setHourHand(@NonNull android.graphics.drawable.Icon);
     method @Deprecated public void setMinuteHand(@NonNull android.graphics.drawable.Icon);
     method @Deprecated public void setSecondHand(@Nullable android.graphics.drawable.Icon);
+    method @Deprecated public void setTimeZone(@Nullable String);
   }
 
   public class ArrayAdapter<T> extends android.widget.BaseAdapter implements android.widget.Filterable android.widget.ThemedSpinnerAdapter {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 2c2443f..9bff793 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -9604,29 +9604,6 @@
 
 }
 
-package android.service.attestation {
-
-  public abstract class ImpressionAttestationService extends android.app.Service {
-    ctor public ImpressionAttestationService();
-    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
-    method @Nullable public abstract android.service.attestation.ImpressionToken onGenerateImpressionToken(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
-    method public abstract boolean onVerifyImpressionToken(@NonNull byte[], @NonNull android.service.attestation.ImpressionToken);
-  }
-
-  public final class ImpressionToken implements android.os.Parcelable {
-    ctor public ImpressionToken(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
-    method public int describeContents();
-    method @NonNull public android.graphics.Rect getBoundsInWindow();
-    method @NonNull public String getHashingAlgorithm();
-    method @NonNull public byte[] getHmac();
-    method @NonNull public byte[] getImageHash();
-    method public long getScreenshotTimeMillis();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.service.attestation.ImpressionToken> CREATOR;
-  }
-
-}
-
 package android.service.autofill {
 
   public abstract class AutofillFieldClassificationService extends android.app.Service {
@@ -10192,6 +10169,30 @@
 
 }
 
+package android.service.screenshot {
+
+  public final class ScreenshotHash implements android.os.Parcelable {
+    ctor public ScreenshotHash(long, @NonNull android.graphics.Rect, @NonNull String, @NonNull byte[], @NonNull byte[]);
+    method public int describeContents();
+    method @NonNull public android.graphics.Rect getBoundsInWindow();
+    method @NonNull public String getHashingAlgorithm();
+    method @NonNull public byte[] getHmac();
+    method @NonNull public byte[] getImageHash();
+    method public long getScreenshotTimeMillis();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.service.screenshot.ScreenshotHash> CREATOR;
+  }
+
+  public abstract class ScreenshotHasherService extends android.app.Service {
+    ctor public ScreenshotHasherService();
+    method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+    method @Nullable public abstract android.service.screenshot.ScreenshotHash onGenerateScreenshotHash(@NonNull byte[], @NonNull android.hardware.HardwareBuffer, @NonNull android.graphics.Rect, @NonNull String);
+    method public abstract boolean onVerifyScreenshotHash(@NonNull byte[], @NonNull android.service.screenshot.ScreenshotHash);
+    field public static final String SERVICE_INTERFACE = "android.service.screenshot.ScreenshotHasherService";
+  }
+
+}
+
 package android.service.search {
 
   public abstract class SearchUiService extends android.app.Service {
@@ -11806,7 +11807,7 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult setIccLockEnabled(boolean, @NonNull String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method public int setNrDualConnectivityState(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
diff --git a/core/java/android/os/BatteryConsumer.java b/core/java/android/os/BatteryConsumer.java
index 71d2177..4cd62eb 100644
--- a/core/java/android/os/BatteryConsumer.java
+++ b/core/java/android/os/BatteryConsumer.java
@@ -216,16 +216,6 @@
         }
 
         /**
-         * Sets the total amount of power consumed since BatteryStats reset, mAh.
-         */
-        @SuppressWarnings("unchecked")
-        @NonNull
-        public T setConsumedPower(double consumedPower) {
-            mPowerComponentsBuilder.setTotalPowerConsumed(consumedPower);
-            return (T) this;
-        }
-
-        /**
          * Sets the amount of time used by the specified component, e.g. CPU, WiFi etc.
          *
          * @param componentId              The ID of the time component, e.g.
diff --git a/core/java/android/os/PowerComponents.java b/core/java/android/os/PowerComponents.java
index 18dca68..f681c4f 100644
--- a/core/java/android/os/PowerComponents.java
+++ b/core/java/android/os/PowerComponents.java
@@ -36,11 +36,15 @@
     private final int mModeledPowerComponentOffset;
 
     PowerComponents(@NonNull Builder builder) {
-        mTotalPowerConsumed = builder.mTotalPowerConsumed;
         mCustomPowerComponentCount = builder.mCustomPowerComponentCount;
         mModeledPowerComponentOffset = builder.mModeledPowerComponentOffset;
         mPowerComponents = builder.mPowerComponents;
         mTimeComponents = builder.mTimeComponents;
+        double totalPower = 0;
+        for (int i = mPowerComponents.length - 1; i >= 0; i--) {
+            totalPower += mPowerComponents[i];
+        }
+        mTotalPowerConsumed = totalPower;
     }
 
     PowerComponents(@NonNull Parcel source) {
@@ -158,7 +162,6 @@
      * Builder for PowerComponents.
      */
     static final class Builder {
-        private double mTotalPowerConsumed;
         private final double[] mPowerComponents;
         private final int mCustomPowerComponentCount;
         private final long[] mTimeComponents;
@@ -181,15 +184,6 @@
         }
 
         /**
-         * Sets the sum amount of power consumed since BatteryStats reset.
-         */
-        @NonNull
-        public Builder setTotalPowerConsumed(double totalPowerConsumed) {
-            mTotalPowerConsumed = totalPowerConsumed;
-            return this;
-        }
-
-        /**
          * Sets the amount of drain attributed to the specified drain type, e.g. CPU, WiFi etc.
          *
          * @param componentId    The ID of the power component, e.g.
diff --git a/core/java/android/service/attestation/ImpressionAttestationService.java b/core/java/android/service/attestation/ImpressionAttestationService.java
deleted file mode 100644
index 968d533..0000000
--- a/core/java/android/service/attestation/ImpressionAttestationService.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * 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.service.attestation;
-
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.HardwareBuffer;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteCallback;
-
-/**
- * A service that handles generating and verify ImpressionTokens.
- *
- * The service will generate an ImpressionToken based on arguments passed in. Then later that same
- * ImpressionToken can be verified to determine that it was created by the system.
- *
- * @hide
- */
-@SystemApi
-public abstract class ImpressionAttestationService extends Service {
-    /** @hide **/
-    public static final String EXTRA_IMPRESSION_TOKEN =
-            "android.service.attestation.extra.IMPRESSION_TOKEN";
-
-    /** @hide **/
-    public static final String EXTRA_VERIFICATION_STATUS =
-            "android.service.attestation.extra.VERIFICATION_STATUS";
-
-    /**
-     * Manifest metadata key for the resource string array containing the names of all impression
-     * attestation algorithms provided by the service.
-     *
-     * @hide
-     */
-    public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
-            "android.attestation.available_algorithms";
-
-    /**
-     * The {@link Intent} action that must be declared as handled by a service in its manifest
-     * for the system to recognize it as an impression attestation providing service.
-     *
-     * @hide
-     */
-    public static final String SERVICE_INTERFACE =
-            "android.service.attestation.ImpressionAttestationService";
-
-    private ImpressionAttestationServiceWrapper mWrapper;
-    private Handler mHandler;
-
-    public ImpressionAttestationService() {
-    }
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mWrapper = new ImpressionAttestationServiceWrapper();
-        mHandler = new Handler(Looper.getMainLooper(), null, true);
-    }
-
-    @NonNull
-    @Override
-    public final IBinder onBind(@NonNull Intent intent) {
-        return mWrapper;
-    }
-
-    /**
-     * Generates the impression token that can be used to validate that the system
-     * generated the token.
-     *
-     * @param salt          The salt to use when generating the hmac. This should be unique to the
-     *                      caller so the token cannot be verified by any other process.
-     * @param screenshot    The screenshot buffer for the content to attest.
-     * @param bounds        The size and position of the content being attested in the window.
-     * @param hashAlgorithm The String for the hashing algorithm to use based values in
-     *                      {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
-     * @return An impression token that can be used to validate information about the content.
-     * Returns null when the arguments sent are invalid.
-     */
-    @Nullable
-    public abstract ImpressionToken onGenerateImpressionToken(@NonNull byte[] salt,
-            @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
-            @NonNull String hashAlgorithm);
-
-    /**
-     * Call to verify that the impressionToken passed in was generated by the system.
-     *
-     * @param salt            The salt value to use when verifying the hmac. This should be the
-     *                        same value that was passed to
-     *                        {@link #onGenerateImpressionToken(byte[],
-     *                        HardwareBuffer, Rect, String)} to
-     *                        generate the token.
-     * @param impressionToken The token to verify that it was generated by the system.
-     * @return true if the token can be verified that it was generated by the system.
-     */
-    public abstract boolean onVerifyImpressionToken(@NonNull byte[] salt,
-            @NonNull ImpressionToken impressionToken);
-
-    private void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
-            String hashAlgorithm, RemoteCallback callback) {
-        ImpressionToken impressionToken = onGenerateImpressionToken(salt, screenshot, bounds,
-                hashAlgorithm);
-        final Bundle data = new Bundle();
-        data.putParcelable(EXTRA_IMPRESSION_TOKEN, impressionToken);
-        callback.sendResult(data);
-    }
-
-    private void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
-            RemoteCallback callback) {
-        boolean verificationStatus = onVerifyImpressionToken(salt, impressionToken);
-        final Bundle data = new Bundle();
-        data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
-        callback.sendResult(data);
-    }
-
-    private final class ImpressionAttestationServiceWrapper extends
-            IImpressionAttestationService.Stub {
-        @Override
-        public void generateImpressionToken(byte[] salt, HardwareBuffer screenshot, Rect bounds,
-                String hashAlgorithm, RemoteCallback callback) {
-            mHandler.sendMessage(
-                    obtainMessage(ImpressionAttestationService::generateImpressionToken,
-                            ImpressionAttestationService.this, salt, screenshot, bounds,
-                            hashAlgorithm, callback));
-        }
-
-        @Override
-        public void verifyImpressionToken(byte[] salt, ImpressionToken impressionToken,
-                RemoteCallback callback) {
-            mHandler.sendMessage(obtainMessage(ImpressionAttestationService::verifyImpressionToken,
-                    ImpressionAttestationService.this, salt, impressionToken, callback));
-        }
-    }
-}
diff --git a/core/java/android/service/attestation/OWNERS b/core/java/android/service/attestation/OWNERS
deleted file mode 100644
index b9e7b99..0000000
--- a/core/java/android/service/attestation/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-chaviw@google.com
-ogunwale@google.com
diff --git a/core/java/android/service/attestation/IImpressionAttestationService.aidl b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
similarity index 66%
rename from core/java/android/service/attestation/IImpressionAttestationService.aidl
rename to core/java/android/service/screenshot/IScreenshotHasherService.aidl
index 5ff8f17..d14d147 100644
--- a/core/java/android/service/attestation/IImpressionAttestationService.aidl
+++ b/core/java/android/service/screenshot/IScreenshotHasherService.aidl
@@ -14,43 +14,43 @@
  * limitations under the License.
  */
 
-package android.service.attestation;
+package android.service.screenshot;
 
 import android.graphics.Rect;
 import android.hardware.HardwareBuffer;
 import android.os.RemoteCallback;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 
 /**
- * Service used to handle impression attestation requests.
+ * Service used to handle ScreenshotHash requests.
  *
  * @hide
  */
-oneway interface IImpressionAttestationService {
+oneway interface IScreenshotHasherService {
     /**
-     * Generates the impression token that can be used to validate that the system generated the
+     * Generates the ScreenshotHash token that can be used to validate that the system generated the
      * token.
      *
      * @param salt The salt to use when generating the hmac. This should be unique to the caller so
      *        the token cannot be verified by any other process.
      * @param screenshot The screenshot to generate the hash and add to the token.
-     * @param bounds The size and position of the content being attested in the window.
+     * @param bounds The size and position of the content being screenshot in the window.
      * @param hashAlgorithm The String for the hashing algorithm to use based on values in
      *        {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS}.
-     * @param Callback The callback invoked to send back the impression token.
+     * @param Callback The callback invoked to send back the ScreenshotHash token.
      */
-    void generateImpressionToken(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
+    void generateScreenshotHash(in byte[] salt, in HardwareBuffer screenshot, in Rect bounds,
                                  in String hashAlgorithm, in RemoteCallback callback);
 
     /**
-     * Call to verify that the impressionToken passed in was generated by the system. The result
+     * Call to verify that the ScreenshotHash passed in was generated by the system. The result
      * will be sent in the callback as a boolean with the key {@link #EXTRA_VERIFICATION_STATUS}.
      *
      * @param salt The salt value to use when verifying the hmac. This should be the same value that
-     *        was passed to {@link generateImpressionToken()} to generate the token.
-     * @param impressionToken The token to verify that it was generated by the system.
+     *        was passed to {@link generateScreenshotHash()} to generate the token.
+     * @param screenshotHash The hash to verify that it was generated by the system.
      * @param callback The callback invoked to send back the verification status.
      */
-    void verifyImpressionToken(in byte[] salt, in ImpressionToken impressionToken,
+    void verifyScreenshotHash(in byte[] salt, in ScreenshotHash screenshotHash,
                                in RemoteCallback callback);
 }
diff --git a/core/java/android/service/screenshot/OWNERS b/core/java/android/service/screenshot/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/core/java/android/service/screenshot/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/service/attestation/ImpressionToken.aidl b/core/java/android/service/screenshot/ScreenshotHash.aidl
similarity index 90%
rename from core/java/android/service/attestation/ImpressionToken.aidl
rename to core/java/android/service/screenshot/ScreenshotHash.aidl
index 284a4ba..a7c50db 100644
--- a/core/java/android/service/attestation/ImpressionToken.aidl
+++ b/core/java/android/service/screenshot/ScreenshotHash.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.service.attestation;
+package android.service.screenshot;
 
-parcelable ImpressionToken;
\ No newline at end of file
+parcelable ScreenshotHash;
diff --git a/core/java/android/service/attestation/ImpressionToken.java b/core/java/android/service/screenshot/ScreenshotHash.java
similarity index 81%
rename from core/java/android/service/attestation/ImpressionToken.java
rename to core/java/android/service/screenshot/ScreenshotHash.java
index 4355dc0..9ae4192 100644
--- a/core/java/android/service/attestation/ImpressionToken.java
+++ b/core/java/android/service/screenshot/ScreenshotHash.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.attestation;
+package android.service.screenshot;
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
@@ -25,14 +25,14 @@
 import com.android.internal.util.DataClass;
 
 /**
- * The ads impression token used to validate information about what was present on screen.
+ * The screenshot hash used to validate information about what was present on screen.
  * @hide
  *
  * TODO: Remove hide and SystemAPI since this will be a public class
  */
 @SystemApi
 @DataClass(genToString = true, genAidl = true)
-public final class ImpressionToken implements Parcelable {
+public final class ScreenshotHash implements Parcelable {
     /**
      * The timestamp when the screenshot was generated.
      */
@@ -42,23 +42,27 @@
      * The bounds of the requested area to take the screenshot. This is in window space passed in
      * by the client.
      */
-    private @NonNull final Rect mBoundsInWindow;
+    @NonNull
+    private final Rect mBoundsInWindow;
 
     /**
      * The selected hashing algorithm that generated the image hash.
      */
-    private @NonNull final String mHashingAlgorithm;
+    @NonNull
+    private final String mHashingAlgorithm;
 
     /**
-     * The image hash generated when creating the impression token from the screenshot taken.
+     * The image hash generated when creating the ScreenshotHash from the screenshot taken.
      */
-    private @NonNull final byte[] mImageHash;
+    @NonNull
+    private final byte[] mImageHash;
 
     /**
      * The hmac generated by the system and used to verify whether this token was generated by
      * the system. This should only be accessed by a system process.
      */
-    private @NonNull final byte[] mHmac;
+    @NonNull
+    private final byte[] mHmac;
 
     /**
      * The hmac generated by the system and used to verify whether this token was generated by
@@ -67,19 +71,21 @@
      * @hide
      */
     @SystemApi
-    public @NonNull byte[] getHmac() {
+    @NonNull
+    public byte[] getHmac() {
         return mHmac;
     }
 
 
 
-    // Code below generated by codegen v1.0.18.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/attestation/ImpressionToken.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/screenshot
+    // /ScreenshotHash.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -87,7 +93,7 @@
 
 
     /**
-     * Creates a new ImpressionToken.
+     * Creates a new ScreenshotHash.
      *
      * @param screenshotTimeMillis
      *   The timestamp when the screenshot was generated.
@@ -97,13 +103,13 @@
      * @param hashingAlgorithm
      *   The selected hashing algorithm that generated the image hash.
      * @param imageHash
-     *   The image hash generated when creating the impression token from the screenshot taken.
+     *   The image hash generated when creating the ScreenshotHash from the screenshot taken.
      * @param hmac
      *   The hmac generated by the system and used to verify whether this token was generated by
      *   the system. This should only be accessed by a system process.
      */
     @DataClass.Generated.Member
-    public ImpressionToken(
+    public ScreenshotHash(
             long screenshotTimeMillis,
             @NonNull Rect boundsInWindow,
             @NonNull String hashingAlgorithm,
@@ -152,7 +158,7 @@
     }
 
     /**
-     * The image hash generated when creating the impression token from the screenshot taken.
+     * The image hash generated when creating the ScreenshotHash from the screenshot taken.
      */
     @DataClass.Generated.Member
     public @NonNull byte[] getImageHash() {
@@ -165,7 +171,7 @@
         // You can override field toString logic by defining methods like:
         // String fieldNameToString() { ... }
 
-        return "ImpressionToken { " +
+        return "ScreenshotHash { " +
                 "screenshotTimeMillis = " + mScreenshotTimeMillis + ", " +
                 "boundsInWindow = " + mBoundsInWindow + ", " +
                 "hashingAlgorithm = " + mHashingAlgorithm + ", " +
@@ -194,7 +200,7 @@
     /** @hide */
     @SuppressWarnings({"unchecked", "RedundantCast"})
     @DataClass.Generated.Member
-    /* package-private */ ImpressionToken(@NonNull Parcel in) {
+    /* package-private */ ScreenshotHash(@NonNull Parcel in) {
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
@@ -222,24 +228,24 @@
     }
 
     @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<ImpressionToken> CREATOR
-            = new Parcelable.Creator<ImpressionToken>() {
+    public static final @NonNull Parcelable.Creator<ScreenshotHash> CREATOR
+            = new Parcelable.Creator<ScreenshotHash>() {
         @Override
-        public ImpressionToken[] newArray(int size) {
-            return new ImpressionToken[size];
+        public ScreenshotHash[] newArray(int size) {
+            return new ScreenshotHash[size];
         }
 
         @Override
-        public ImpressionToken createFromParcel(@NonNull Parcel in) {
-            return new ImpressionToken(in);
+        public ScreenshotHash createFromParcel(@NonNull Parcel in) {
+            return new ScreenshotHash(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1604539951959L,
-            codegenVersion = "1.0.18",
-            sourceFile = "frameworks/base/core/java/android/service/attestation/ImpressionToken.java",
-            inputSignatures = "private final  long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ImpressionToken extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
+            time = 1612383172822L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/service/screenshot/ScreenshotHash.java",
+            inputSignatures = "private final  long mScreenshotTimeMillis\nprivate final @android.annotation.NonNull android.graphics.Rect mBoundsInWindow\nprivate final @android.annotation.NonNull java.lang.String mHashingAlgorithm\nprivate final @android.annotation.NonNull byte[] mImageHash\nprivate final @android.annotation.NonNull byte[] mHmac\npublic @android.annotation.SystemApi @android.annotation.NonNull byte[] getHmac()\nclass ScreenshotHash extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/screenshot/ScreenshotHasherService.java b/core/java/android/service/screenshot/ScreenshotHasherService.java
new file mode 100644
index 0000000..d96cc7e
--- /dev/null
+++ b/core/java/android/service/screenshot/ScreenshotHasherService.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 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.service.screenshot;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+
+/**
+ * A service that handles generating and verify {@link ScreenshotHash}.
+ *
+ * The service will generate a ScreenshotHash based on arguments passed in. Then later that
+ * same ScreenshotHash can be verified to determine that it was created by the system.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class ScreenshotHasherService extends Service {
+    /** @hide **/
+    public static final String EXTRA_SCREENSHOT_HASH =
+            "android.service.screenshot.extra.SCREENSHOT_HASH";
+
+    /** @hide **/
+    public static final String EXTRA_VERIFICATION_STATUS =
+            "android.service.screenshot.extra.VERIFICATION_STATUS";
+
+    /**
+     * Manifest metadata key for the resource string array containing the names of all hashing
+     * algorithms provided by the service.
+     *
+     * @hide
+     */
+    public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS =
+            "android.screenshot.available_algorithms";
+
+    /**
+     * The {@link Intent} action that must be declared as handled by a service in its manifest
+     * for the system to recognize it as a ScreenshotHash providing service.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SERVICE_INTERFACE =
+            "android.service.screenshot.ScreenshotHasherService";
+
+    private ScreenshotHasherServiceWrapper mWrapper;
+    private Handler mHandler;
+
+    public ScreenshotHasherService() {
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mWrapper = new ScreenshotHasherServiceWrapper();
+        mHandler = new Handler(Looper.getMainLooper(), null, true);
+    }
+
+    @NonNull
+    @Override
+    public final IBinder onBind(@NonNull Intent intent) {
+        return mWrapper;
+    }
+
+    /**
+     * Generates the ScreenshotHash that can be used to validate that the system generated the
+     * token.
+     *
+     * @param salt          The salt to use when generating the hmac. This should be unique to the
+     *                      caller so the token cannot be verified by any other process.
+     * @param screenshot    The screenshot buffer for the content.
+     * @param bounds        The size and position of the content being screenshot in the window.
+     * @param hashAlgorithm The String for the hashing algorithm to use based values in
+     *                      {@link #SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS)}.
+     * @return A ScreenshotHash that can be used to validate information about the content.
+     * Returns null when the arguments sent are invalid.
+     */
+    @Nullable
+    public abstract ScreenshotHash onGenerateScreenshotHash(@NonNull byte[] salt,
+            @NonNull HardwareBuffer screenshot, @NonNull Rect bounds,
+            @NonNull String hashAlgorithm);
+
+    /**
+     * Call to verify that the ScreenshotHash passed in was generated by the system.
+     *
+     * @param salt               The salt value to use when verifying the hmac. This should be the
+     *                           same value that was passed to
+     *                           {@link #onGenerateScreenshotHash(byte[],
+     *                           HardwareBuffer, Rect, String)} to
+     *                           generate the token.
+     * @param screenshotHash The token to verify that it was generated by the system.
+     * @return true if the token can be verified that it was generated by the system.
+     */
+    public abstract boolean onVerifyScreenshotHash(@NonNull byte[] salt,
+            @NonNull ScreenshotHash screenshotHash);
+
+    private void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+            String hashAlgorithm, RemoteCallback callback) {
+        ScreenshotHash screenshotHash = onGenerateScreenshotHash(salt, screenshot,
+                bounds,
+                hashAlgorithm);
+        final Bundle data = new Bundle();
+        data.putParcelable(EXTRA_SCREENSHOT_HASH, screenshotHash);
+        callback.sendResult(data);
+    }
+
+    private void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+            RemoteCallback callback) {
+        boolean verificationStatus = onVerifyScreenshotHash(salt, screenshotHash);
+        final Bundle data = new Bundle();
+        data.putBoolean(EXTRA_VERIFICATION_STATUS, verificationStatus);
+        callback.sendResult(data);
+    }
+
+    private final class ScreenshotHasherServiceWrapper extends
+            IScreenshotHasherService.Stub {
+        @Override
+        public void generateScreenshotHash(byte[] salt, HardwareBuffer screenshot, Rect bounds,
+                String hashAlgorithm, RemoteCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(ScreenshotHasherService::generateScreenshotHash,
+                            ScreenshotHasherService.this, salt, screenshot, bounds,
+                            hashAlgorithm, callback));
+        }
+
+        @Override
+        public void verifyScreenshotHash(byte[] salt, ScreenshotHash screenshotHash,
+                RemoteCallback callback) {
+            mHandler.sendMessage(
+                    obtainMessage(ScreenshotHasherService::verifyScreenshotHash,
+                            ScreenshotHasherService.this, salt, screenshotHash,
+                            callback));
+        }
+    }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 790773f..b229212 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -65,7 +65,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "true");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
-        DEFAULT_FLAGS.put("settings_silky_home", "true");
+        DEFAULT_FLAGS.put("settings_silky_home", "false");
         DEFAULT_FLAGS.put("settings_contextual_home", "false");
         DEFAULT_FLAGS.put(SETTINGS_PROVIDER_MODEL, "false");
     }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 68a6de8..7843411 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,7 @@
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 import android.view.DisplayCutout;
 import android.view.IApplicationToken;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -765,21 +765,21 @@
     /**
      * 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.
+     * the ScreenshotHash.
      *
      * @return a String array of supported hashing algorithms.
      */
-    String[] getSupportedImpressionAlgorithms();
+    String[] getSupportedScreenshotHashingAlgorithms();
 
     /**
-     * Validate the impression token was generated by the system. The impression token passed in
-     * should be the token generated when calling {@link IWindowSession#generateImpressionToken}
+     * Validate the ScreenshotHash was generated by the system. The ScreenshotHash passed in
+     * should be the token generated when calling {@link IWindowSession#generateScreenshotHash}
      *
-     * @param impressionToken The token to verify that it was generated by the system.
+     * @param ScreenshotHash 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);
+    boolean verifyScreenshotHash(in ScreenshotHash screenshotHash);
 
     /**
      * Registers a listener for a {@link android.app.WindowContext} to handle configuration changes
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 85498cb..7b15f52 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -22,7 +22,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 import android.util.MergedConfiguration;
 import android.view.DisplayCutout;
 import android.view.InputChannel;
@@ -345,14 +345,14 @@
     void grantEmbeddedWindowFocus(IWindow window, in IBinder inputToken, boolean grantFocus);
 
     /**
-     * Generates an impression token that can be used to validate whether specific content was on
+     * Generates an ScreenshotHash 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 window The token for the window where the view to screenshot 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()}
+     *                      from {@link IWindowManager#getSupportedHashingAlgorithms()}
      */
-    ImpressionToken generateImpressionToken(IWindow window, in Rect boundsInWindow,
+    ScreenshotHash generateScreenshotHash(IWindow window, in Rect boundsInWindow,
             in String hashAlgorithm);
 }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index dd56c15..5ae66e3 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -24,7 +24,7 @@
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 import android.util.Log;
 import android.util.MergedConfiguration;
 import android.window.ClientWindowFrames;
@@ -466,7 +466,7 @@
     }
 
     @Override
-    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+    public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
             String hashAlgorithm) {
         return null;
     }
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 98738ef..93b2d8a 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -30,14 +30,19 @@
 import android.graphics.drawable.Icon;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.RemotableViewMethod;
 import android.view.View;
+import android.view.inspector.InspectableProperty;
 import android.widget.RemoteViews.RemoteView;
 
 import java.time.Clock;
+import java.time.DateTimeException;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
+import java.util.Formatter;
+import java.util.Locale;
 
 /**
  * This widget display an analogic clock with two hands for hours and
@@ -47,15 +52,19 @@
  * @attr ref android.R.styleable#AnalogClock_hand_hour
  * @attr ref android.R.styleable#AnalogClock_hand_minute
  * @attr ref android.R.styleable#AnalogClock_hand_second
+ * @attr ref android.R.styleable#AnalogClock_timeZone
  * @deprecated This widget is no longer supported.
  */
 @RemoteView
 @Deprecated
 public class AnalogClock extends View {
+    private static final String LOG_TAG = "AnalogClock";
     /** How often the clock should refresh to make the seconds hand advance at ~15 FPS. */
     private static final long SECONDS_TICK_FREQUENCY_MS = 1000 / 15;
 
     private Clock mClock;
+    @Nullable
+    private ZoneId mTimeZone;
 
     @UnsupportedAppUsage
     private Drawable mHourHand;
@@ -114,7 +123,8 @@
 
         mSecondHand = a.getDrawable(com.android.internal.R.styleable.AnalogClock_hand_second);
 
-        mClock = Clock.systemDefaultZone();
+        mTimeZone = toZoneId(a.getString(com.android.internal.R.styleable.AnalogClock_timeZone));
+        createClock();
 
         mDialWidth = mDial.getIntrinsicWidth();
         mDialHeight = mDial.getIntrinsicHeight();
@@ -162,6 +172,46 @@
         invalidate();
     }
 
+    /**
+     * Indicates which time zone is currently used by this view.
+     *
+     * @return The ID of the current time zone or null if the default time zone,
+     *         as set by the user, must be used
+     *
+     * @see java.util.TimeZone
+     * @see java.util.TimeZone#getAvailableIDs()
+     * @see #setTimeZone(String)
+     */
+    @InspectableProperty
+    @Nullable
+    public String getTimeZone() {
+        ZoneId zoneId = mTimeZone;
+        return zoneId == null ? null : zoneId.getId();
+    }
+
+    /**
+     * Sets the specified time zone to use in this clock. When the time zone
+     * is set through this method, system time zone changes (when the user
+     * sets the time zone in settings for instance) will be ignored.
+     *
+     * @param timeZone The desired time zone's ID as specified in {@link java.util.TimeZone}
+     *                 or null to user the time zone specified by the user
+     *                 (system time zone)
+     *
+     * @see #getTimeZone()
+     * @see java.util.TimeZone#getAvailableIDs()
+     * @see java.util.TimeZone#getTimeZone(String)
+     *
+     * @attr ref android.R.styleable#AnalogClock_timeZone
+     */
+    @RemotableViewMethod
+    public void setTimeZone(@Nullable String timeZone) {
+        mTimeZone = toZoneId(timeZone);
+
+        createClock();
+        onTimeChanged();
+    }
+
     @Override
     public void onVisibilityAggregated(boolean isVisible) {
         super.onVisibilityAggregated(isVisible);
@@ -198,8 +248,8 @@
         // NOTE: It's safe to do these after registering the receiver since the receiver always runs
         // in the main thread, therefore the receiver can't run before this method returns.
 
-        // The time zone may have changed while the receiver wasn't registered, so update the Time
-        mClock = Clock.systemDefaultZone();
+        // The time zone may have changed while the receiver wasn't registered, so update the clock.
+        createClock();
 
         // Make sure we update to the current time
         onTimeChanged();
@@ -340,8 +390,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
-                String tz = intent.getStringExtra(Intent.EXTRA_TIMEZONE);
-                mClock = Clock.system(ZoneId.of(tz));
+                createClock();
             }
 
             onTimeChanged();
@@ -365,9 +414,26 @@
         }
     };
 
+    private void createClock() {
+        ZoneId zoneId = mTimeZone;
+        if (zoneId == null) {
+            mClock = Clock.systemDefaultZone();
+        } else {
+            mClock = Clock.system(zoneId);
+        }
+    }
+
     private void updateContentDescription(long timeMillis) {
         final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR;
-        String contentDescription = DateUtils.formatDateTime(mContext, timeMillis, flags);
+        String contentDescription =
+                DateUtils.formatDateRange(
+                        mContext,
+                        new Formatter(new StringBuilder(50), Locale.getDefault()),
+                        timeMillis /* startMillis */,
+                        timeMillis /* endMillis */,
+                        flags,
+                        getTimeZone())
+                        .toString();
         setContentDescription(contentDescription);
     }
 
@@ -378,4 +444,22 @@
         Instant instant = Instant.ofEpochMilli(timeMillis);
         return LocalDateTime.ofInstant(instant, zoneId);
     }
+
+    /**
+     * Tries to parse a {@link ZoneId} from {@code timeZone}, returning null if it is null or there
+     * is an error parsing.
+     */
+    @Nullable
+    private static ZoneId toZoneId(@Nullable String timeZone) {
+        if (timeZone == null) {
+            return null;
+        }
+
+        try {
+            return ZoneId.of(timeZone);
+        } catch (DateTimeException e) {
+            Log.w(LOG_TAG, "Failed to parse time zone from " + timeZone, e);
+            return null;
+        }
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index e0f9554..3d896c8 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -59,6 +59,7 @@
 import com.android.internal.jank.FrameTracker.FrameMetricsWrapper;
 import com.android.internal.jank.FrameTracker.ThreadedRendererWrapper;
 import com.android.internal.jank.FrameTracker.ViewRootWrapper;
+import com.android.internal.util.PerfettoTrigger;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/core/java/com/android/internal/jank/PerfettoTrigger.java b/core/java/com/android/internal/jank/PerfettoTrigger.java
deleted file mode 100644
index 643d24a..0000000
--- a/core/java/com/android/internal/jank/PerfettoTrigger.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- */
-
-//TODO (165884885): Make PerfettoTrigger more generic and move it to another package.
-package com.android.internal.jank;
-
-import android.annotation.NonNull;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-/**
- * A trigger implementation with perfetto backend.
- * @hide
- */
-public class PerfettoTrigger {
-    private static final String TAG = PerfettoTrigger.class.getSimpleName();
-    private static final boolean DEBUG = false;
-    private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
-
-    /**
-     * @param triggerName The name of the trigger. Must match the value defined in the AOT
-     *                    Perfetto config.
-     */
-    public static void trigger(String triggerName) {
-        try {
-            ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
-            if (DEBUG) {
-                StringBuilder sb = new StringBuilder();
-                for (String arg : pb.command()) {
-                    sb.append(arg).append(" ");
-                }
-                Log.d(TAG, "Triggering " + sb.toString());
-            }
-            Process process = pb.start();
-            if (DEBUG) {
-                readConsoleOutput(process);
-            }
-        } catch (IOException | InterruptedException e) {
-            Log.w(TAG, "Failed to trigger " + triggerName, e);
-        }
-    }
-
-    private static void readConsoleOutput(@NonNull Process process)
-            throws IOException, InterruptedException {
-        process.waitFor();
-        try (BufferedReader errReader =
-                     new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
-            StringBuilder errLine = new StringBuilder();
-            String line;
-            while ((line = errReader.readLine()) != null) {
-                errLine.append(line).append("\n");
-            }
-            errLine.append(", code=").append(process.exitValue());
-            Log.d(TAG, "err message=" + errLine.toString());
-        }
-    }
-}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
index ea2fb88..964568c 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsProvider.java
@@ -114,14 +114,9 @@
                         .setDischargePercentage(batteryStatsHelper.getStats().getDischargeAmount(0))
                         .setConsumedPower(batteryStatsHelper.getTotalPower());
 
-        final List<BatterySipper> usageList = batteryStatsHelper.getUsageList();
-        for (int i = 0; i < usageList.size(); i++) {
-            final BatterySipper sipper = usageList.get(i);
-            if (sipper.drainType == BatterySipper.DrainType.APP) {
-                batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(sipper.uidObj)
-                        .setPackageWithHighestDrain(sipper.packageWithHighestDrain)
-                        .setConsumedPower(sipper.sumPower());
-            }
+        SparseArray<? extends BatteryStats.Uid> uidStats = mStats.getUidStats();
+        for (int i = uidStats.size() - 1; i >= 0; i--) {
+            batteryUsageStatsBuilder.getOrCreateUidBatteryConsumerBuilder(uidStats.valueAt(i));
         }
 
         final long realtimeUs = SystemClock.elapsedRealtime() * 1000;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fda87be..4b343af 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -103,7 +103,7 @@
      */
     public static final int PROFILE_FROM_SHELL = 1 << 15;
 
-    /*
+    /**
      * Enable using the ART app image startup cache
      */
     public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
@@ -116,13 +116,6 @@
      */
     public static final int DEBUG_IGNORE_APP_SIGNAL_HANDLER = 1 << 17;
 
-    /**
-     * Disable runtime access to {@link android.annotation.TestApi} annotated members.
-     *
-     * <p>This only takes effect if Hidden API access restrictions are enabled as well.
-     */
-    public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
-
     public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
     /**
      * Enable pointer tagging in this process.
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 30cd94c..f42f468 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -14,6 +14,7 @@
 
 package com.android.internal.util;
 
+import android.annotation.IntDef;
 import android.content.Context;
 import android.os.Build;
 import android.os.SystemClock;
@@ -23,9 +24,12 @@
 import android.util.Log;
 import android.util.SparseLongArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.EventLogTags;
 import com.android.internal.os.BackgroundThread;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
@@ -91,6 +95,34 @@
      */
     public static final int ACTION_START_RECENTS_ANIMATION = 8;
 
+    private static final int[] ACTIONS_ALL = {
+        ACTION_EXPAND_PANEL,
+        ACTION_TOGGLE_RECENTS,
+        ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+        ACTION_CHECK_CREDENTIAL,
+        ACTION_CHECK_CREDENTIAL_UNLOCKED,
+        ACTION_TURN_ON_SCREEN,
+        ACTION_ROTATE_SCREEN,
+        ACTION_FACE_WAKE_AND_UNLOCK,
+        ACTION_START_RECENTS_ANIMATION
+    };
+
+    /** @hide */
+    @IntDef({
+        ACTION_EXPAND_PANEL,
+        ACTION_TOGGLE_RECENTS,
+        ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+        ACTION_CHECK_CREDENTIAL,
+        ACTION_CHECK_CREDENTIAL_UNLOCKED,
+        ACTION_TURN_ON_SCREEN,
+        ACTION_ROTATE_SCREEN,
+        ACTION_FACE_WAKE_AND_UNLOCK,
+        ACTION_START_RECENTS_ANIMATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Action {
+    }
+
     private static final int[] STATSD_ACTION = new int[]{
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
@@ -105,9 +137,14 @@
 
     private static LatencyTracker sLatencyTracker;
 
+    private final Object mLock = new Object();
     private final SparseLongArray mStartRtc = new SparseLongArray();
-    private volatile int mSamplingInterval;
-    private volatile boolean mEnabled;
+    @GuardedBy("mLock")
+    private final int[] mTraceThresholdPerAction = new int[ACTIONS_ALL.length];
+    @GuardedBy("mLock")
+    private int mSamplingInterval;
+    @GuardedBy("mLock")
+    private boolean mEnabled;
 
     public static LatencyTracker getInstance(Context context) {
         if (sLatencyTracker == null) {
@@ -132,20 +169,26 @@
     }
 
     private void updateProperties(DeviceConfig.Properties properties) {
-        mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
-                DEFAULT_SAMPLING_INTERVAL);
-        mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+        synchronized (mLock) {
+            mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY,
+                    DEFAULT_SAMPLING_INTERVAL);
+            mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED);
+            for (int action : ACTIONS_ALL) {
+                mTraceThresholdPerAction[action] =
+                    properties.getInt(getTraceTriggerNameForAction(action), -1);
+            }
+        }
     }
 
     /**
      * A helper method to translate action type to name.
      *
-     * @param action the action type defined in AtomsProto.java
+     * @param atomsProtoAction the action type defined in AtomsProto.java
      * @return the name of the action
      */
-    public static String getNameOfAction(int action) {
+    public static String getNameOfAction(int atomsProtoAction) {
         // Defined in AtomsProto.java
-        switch (action) {
+        switch (atomsProtoAction) {
             case 0:
                 return "UNKNOWN";
             case 1:
@@ -171,16 +214,22 @@
         }
     }
 
-    private static String getTraceNameOfAction(int action) {
+    private static String getTraceNameOfAction(@Action int action) {
         return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
     }
 
+    private static String getTraceTriggerNameForAction(@Action int action) {
+        return "latency-tracker-" + getNameOfAction(STATSD_ACTION[action]);
+    }
+
     public static boolean isEnabled(Context ctx) {
         return getInstance(ctx).isEnabled();
     }
 
     public boolean isEnabled() {
-        return mEnabled;
+        synchronized (mLock) {
+            return mEnabled;
+        }
     }
 
     /**
@@ -188,7 +237,7 @@
      *
      * @param action The action to start. One of the ACTION_* values.
      */
-    public void onActionStart(int action) {
+    public void onActionStart(@Action int action) {
         if (!isEnabled()) {
             return;
         }
@@ -201,7 +250,7 @@
      *
      * @param action The action to end. One of the ACTION_* values.
      */
-    public void onActionEnd(int action) {
+    public void onActionEnd(@Action int action) {
         if (!isEnabled()) {
             return;
         }
@@ -221,19 +270,30 @@
      * @param action   The action to end. One of the ACTION_* values.
      * @param duration The duration of the action in ms.
      */
-    public void logAction(int action, int duration) {
-        boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+    public void logAction(@Action int action, int duration) {
+        boolean shouldSample;
+        int traceThreshold;
+        synchronized (mLock) {
+            shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0;
+            traceThreshold = mTraceThresholdPerAction[action];
+        }
+
+        if (traceThreshold > 0 && duration >= traceThreshold) {
+            PerfettoTrigger.trigger(getTraceTriggerNameForAction(action));
+        }
+
         logActionDeprecated(action, duration, shouldSample);
     }
 
     /**
      * Logs an action that has started and ended. This needs to be called from the main thread.
      *
-     * @param action          The action to end. One of the ACTION_* values.
-     * @param duration        The duration of the action in ms.
+     * @param action The action to end. One of the ACTION_* values.
+     * @param duration The duration of the action in ms.
      * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
      */
-    public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
+    public static void logActionDeprecated(
+            @Action int action, int duration, boolean writeToStatsLog) {
         Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
         EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
 
diff --git a/core/java/com/android/internal/util/PerfettoTrigger.java b/core/java/com/android/internal/util/PerfettoTrigger.java
new file mode 100644
index 0000000..9c87c69
--- /dev/null
+++ b/core/java/com/android/internal/util/PerfettoTrigger.java
@@ -0,0 +1,44 @@
+/*
+ * 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.util;
+
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * A trigger implementation with perfetto backend.
+ * @hide
+ */
+public class PerfettoTrigger {
+    private static final String TAG = "PerfettoTrigger";
+    private static final String TRIGGER_COMMAND = "/system/bin/trigger_perfetto";
+
+    /**
+     * @param triggerName The name of the trigger. Must match the value defined in the AOT
+     *                    Perfetto config.
+     */
+    public static void trigger(String triggerName) {
+        try {
+            ProcessBuilder pb = new ProcessBuilder(TRIGGER_COMMAND, triggerName);
+            Log.v(TAG, "Triggering " + String.join(" ", pb.command()));
+            Process process = pb.start();
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to trigger " + triggerName, e);
+        }
+    }
+}
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 66753e4..f1998a5 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -47,6 +47,7 @@
 static struct assetsprovider_offsets_t {
   jclass classObject;
   jmethodID loadAssetFd;
+  jmethodID toString;
 } gAssetsProviderOffsets;
 
 static struct {
@@ -72,9 +73,22 @@
 class LoaderAssetsProvider : public AssetsProvider {
  public:
   static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
-    return (!assets_provider) ? nullptr
+    return (!assets_provider) ? EmptyAssetsProvider::Create()
                               : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
-                                  env->NewGlobalRef(assets_provider)));
+                                    env, assets_provider));
+  }
+
+  bool ForEachFile(const std::string& /* root_path */,
+                   const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+    return true;
+  }
+
+  const std::string& GetDebugName() const override {
+    return debug_name_;
+  }
+
+  bool IsUpToDate() const override {
+    return true;
   }
 
   ~LoaderAssetsProvider() override {
@@ -142,20 +156,26 @@
       *file_exists = true;
     }
 
-    return ApkAssets::CreateAssetFromFd(base::unique_fd(fd),
-                                        nullptr /* path */,
-                                        static_cast<off64_t>(mOffset),
-                                        static_cast<off64_t>(mLength));
+    return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
+                                             nullptr /* path */,
+                                             static_cast<off64_t>(mOffset),
+                                             static_cast<off64_t>(mLength));
   }
 
  private:
   DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider);
 
-  explicit LoaderAssetsProvider(jobject assets_provider)
-    : assets_provider_(assets_provider) { }
+  explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) {
+    assets_provider_ = env->NewGlobalRef(assets_provider);
+    auto string_result = static_cast<jstring>(env->CallObjectMethod(
+        assets_provider_, gAssetsProviderOffsets.toString));
+    ScopedUtfChars str(env, string_result);
+    debug_name_ = std::string(str.c_str(), str.size());
+  }
 
   // The global reference to the AssetsProvider
   jobject assets_provider_;
+  std::string debug_name_;
 };
 
 static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format,
@@ -170,18 +190,26 @@
   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
   std::unique_ptr<const ApkAssets> apk_assets;
   switch (format) {
-    case FORMAT_APK:
-      apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets));
+    case FORMAT_APK: {
+      auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+                                                ZipAssetsProvider::Create(path.c_str()));
+      apk_assets = ApkAssets::Load(std::move(assets), property_flags);
       break;
+    }
     case FORMAT_IDMAP:
       apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
       break;
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets));
+      apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+                                        std::move(loader_assets),
+                                        property_flags);
       break;
-    case FORMAT_DIRECTORY:
-      apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags,  std::move(loader_assets));
+    case FORMAT_DIRECTORY: {
+      auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
+                                                DirectoryAssetsProvider::Create(path.c_str()));
+      apk_assets = ApkAssets::Load(std::move(assets), property_flags);
       break;
+    }
     default:
       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -221,13 +249,17 @@
   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
   std::unique_ptr<const ApkAssets> apk_assets;
   switch (format) {
-    case FORMAT_APK:
-      apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
-                                         property_flags, std::move(loader_assets));
+    case FORMAT_APK: {
+      auto assets = MultiAssetsProvider::Create(
+          std::move(loader_assets),
+          ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str()));
+      apk_assets = ApkAssets::Load(std::move(assets), property_flags);
       break;
+    }
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
-                                              property_flags, std::move(loader_assets));
+      apk_assets = ApkAssets::LoadTable(
+          AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
+          std::move(loader_assets), property_flags);
       break;
     default:
       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -282,17 +314,20 @@
   auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
   std::unique_ptr<const ApkAssets> apk_assets;
   switch (format) {
-    case FORMAT_APK:
-      apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
-                                         property_flags, std::move(loader_assets),
-                                         static_cast<off64_t>(offset),
-                                         static_cast<off64_t>(length));
+    case FORMAT_APK: {
+      auto assets = MultiAssetsProvider::Create(
+          std::move(loader_assets),
+          ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(),
+                                    static_cast<off64_t>(offset), static_cast<off64_t>(length)));
+      apk_assets = ApkAssets::Load(std::move(assets), property_flags);
       break;
+    }
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(),
-                                              property_flags, std::move(loader_assets),
-                                              static_cast<off64_t>(offset),
-                                              static_cast<off64_t>(length));
+      apk_assets = ApkAssets::LoadTable(
+          AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
+                                            static_cast<off64_t>(offset),
+                                            static_cast<off64_t>(length)),
+          std::move(loader_assets), property_flags);
       break;
     default:
       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
@@ -310,8 +345,7 @@
 }
 
 static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
-  auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider);
-  auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets));
+  auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
   return reinterpret_cast<jlong>(apk_assets.release());
 }
 
@@ -458,6 +492,8 @@
   gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie(
       env, gAssetsProviderOffsets.classObject, "loadAssetFd",
       "(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;");
+  gAssetsProviderOffsets.toString = GetMethodIDOrDie(
+      env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;");
 
   jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
   gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I");
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index 5f2dcdf..5630a1e 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -41,6 +41,10 @@
 #define ENCODING_OPUS           20
 #define ENCODING_PCM_24BIT_PACKED 21
 #define ENCODING_PCM_32BIT 22
+#define ENCODING_MPEGH_BL_L3 23
+#define ENCODING_MPEGH_BL_L4 24
+#define ENCODING_MPEGH_LC_L3 25
+#define ENCODING_MPEGH_LC_L4 26
 
 #define ENCODING_INVALID    0
 #define ENCODING_DEFAULT    1
@@ -98,6 +102,14 @@
         return AUDIO_FORMAT_PCM_24_BIT_PACKED;
     case ENCODING_PCM_32BIT:
         return AUDIO_FORMAT_PCM_32_BIT;
+    case ENCODING_MPEGH_BL_L3:
+        return AUDIO_FORMAT_MPEGH_BL_L3;
+    case ENCODING_MPEGH_BL_L4:
+        return AUDIO_FORMAT_MPEGH_BL_L4;
+    case ENCODING_MPEGH_LC_L3:
+        return AUDIO_FORMAT_MPEGH_LC_L3;
+    case ENCODING_MPEGH_LC_L4:
+        return AUDIO_FORMAT_MPEGH_LC_L4;
     default:
         return AUDIO_FORMAT_INVALID;
     }
@@ -159,6 +171,14 @@
         return ENCODING_DOLBY_MAT;
     case AUDIO_FORMAT_OPUS:
         return ENCODING_OPUS;
+    case AUDIO_FORMAT_MPEGH_BL_L3:
+        return ENCODING_MPEGH_BL_L3;
+    case AUDIO_FORMAT_MPEGH_BL_L4:
+        return ENCODING_MPEGH_BL_L4;
+    case AUDIO_FORMAT_MPEGH_LC_L3:
+        return ENCODING_MPEGH_LC_L3;
+    case AUDIO_FORMAT_MPEGH_LC_L4:
+        return ENCODING_MPEGH_LC_L4;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 989e25d..5442c03 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5412,11 +5412,12 @@
     <permission android:name="android.permission.CONTROL_DEVICE_STATE"
                 android:protectionLevel="signature" />
 
-    <!-- Must be required by an {@link android.service.attestation.ImpressionAttestationService}
-         to ensure that only the system can bind to it.
-         @hide This is not a third-party API (intended for OEMs and system apps).
+    <!-- Must be required by a
+        {@link android.service.screenshot.ScreenshotHasherService}
+        to ensure that only the system can bind to it.
+        @hide This is not a third-party API (intended for OEMs and system apps).
     -->
-    <permission android:name="android.permission.BIND_IMPRESSION_ATTESTATION_SERVICE"
+    <permission android:name="android.permission.BIND_SCREENSHOT_HASHER_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- @hide @TestApi Allows an application to enable/disable toast rate limiting.
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 39e60d0..a8ef2cf 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4062,6 +4062,12 @@
         <attr name="hand_hour" format="reference"/>
         <attr name="hand_minute" format="reference"/>
         <attr name="hand_second" format="reference"/>
+        <!-- Specifies the time zone to use. When this attribute is specified, the
+             TextClock will ignore the time zone of the system. To use the user's
+             time zone, do not specify this attribute. The default value is the
+             user's time zone. Please refer to {@link java.util.TimeZone} for more
+             information about time zone ids. -->
+        <attr name="timeZone" format="string"/>
     </declare-styleable>
     <declare-styleable name="Button">
     </declare-styleable>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
index 96e9c4a..7b1ca8f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsTest.java
@@ -73,7 +73,6 @@
         final UidBatteryConsumer.Builder uidBatteryConsumerBuilder =
                 builder.getOrCreateUidBatteryConsumerBuilder(batteryStatsUid);
         uidBatteryConsumerBuilder.setPackageWithHighestDrain("foo");
-        uidBatteryConsumerBuilder.setConsumedPower(200);
         uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, 300);
         uidBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 400);
         uidBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
@@ -90,7 +89,6 @@
         final SystemBatteryConsumer.Builder systemBatteryConsumerBuilder =
                 builder.getOrCreateSystemBatteryConsumerBuilder(
                         SystemBatteryConsumer.DRAIN_TYPE_CAMERA);
-        systemBatteryConsumerBuilder.setConsumedPower(10000);
         systemBatteryConsumerBuilder.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 10100);
         systemBatteryConsumerBuilder.setConsumedPowerForCustomComponent(
                 BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID, 10200);
@@ -114,7 +112,6 @@
         for (UidBatteryConsumer uidBatteryConsumer : uidBatteryConsumers) {
             if (uidBatteryConsumer.getUid() == 2000) {
                 assertThat(uidBatteryConsumer.getPackageWithHighestDrain()).isEqualTo("foo");
-                assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(200);
                 assertThat(uidBatteryConsumer.getConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_USAGE)).isEqualTo(300);
                 assertThat(uidBatteryConsumer.getConsumedPower(
@@ -130,6 +127,7 @@
                         BatteryConsumer.TIME_COMPONENT_CPU_FOREGROUND)).isEqualTo(700);
                 assertThat(uidBatteryConsumer.getUsageDurationForCustomComponentMillis(
                         BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(800);
+                assertThat(uidBatteryConsumer.getConsumedPower()).isEqualTo(1710);
             } else {
                 fail("Unexpected UID " + uidBatteryConsumer.getUid());
             }
@@ -139,7 +137,6 @@
                 batteryUsageStats.getSystemBatteryConsumers();
         for (SystemBatteryConsumer systemBatteryConsumer : systemBatteryConsumers) {
             if (systemBatteryConsumer.getDrainType() == SystemBatteryConsumer.DRAIN_TYPE_CAMERA) {
-                assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(10000);
                 assertThat(systemBatteryConsumer.getConsumedPower(
                         BatteryConsumer.POWER_COMPONENT_CPU)).isEqualTo(10100);
                 assertThat(systemBatteryConsumer.getConsumedPowerForCustomComponent(
@@ -151,6 +148,7 @@
                         BatteryConsumer.TIME_COMPONENT_CPU)).isEqualTo(10300);
                 assertThat(systemBatteryConsumer.getUsageDurationForCustomComponentMillis(
                         BatteryConsumer.FIRST_CUSTOM_TIME_COMPONENT_ID)).isEqualTo(10400);
+                assertThat(systemBatteryConsumer.getConsumedPower()).isEqualTo(30510);
             } else {
                 fail("Unexpected drain type " + systemBatteryConsumer.getDrainType());
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
index 52648d9..fe97e24 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java
@@ -16,7 +16,7 @@
 
 package com.android.wm.shell;
 
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
 
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.common.ShellExecutor;
@@ -155,7 +155,7 @@
         }
         final int taskId = new Integer(args[2]);
         final int sideStagePosition = args.length > 3
-                ? new Integer(args[3]) : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+                ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT;
         mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition));
         return true;
     }
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 800150c..35dcdd5 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
@@ -34,8 +34,11 @@
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
@@ -84,8 +87,7 @@
     private DragSession mSession;
 
     public DragAndDropPolicy(Context context, SplitScreen splitScreen) {
-        this(context, ActivityTaskManager.getInstance(), splitScreen,
-                new DefaultStarter(context, splitScreen));
+        this(context, ActivityTaskManager.getInstance(), splitScreen, new DefaultStarter(context));
     }
 
     @VisibleForTesting
@@ -94,7 +96,7 @@
         mContext = context;
         mActivityTaskManager = activityTaskManager;
         mSplitScreen = splitScreen;
-        mStarter = starter;
+        mStarter = mSplitScreen != null ? mSplitScreen : starter;
     }
 
     /**
@@ -195,39 +197,23 @@
             return;
         }
 
-        final ClipDescription description = data.getDescription();
-        final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
-        final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
-        final Intent dragData = mSession.dragData;
         final boolean inSplitScreen = mSplitScreen != null && mSplitScreen.isSplitScreenVisible();
         final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT;
-        final Bundle opts = dragData.hasExtra(EXTRA_ACTIVITY_OPTIONS)
-                ? dragData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)
-                : new Bundle();
 
-        if (target.type == TYPE_FULLSCREEN) {
-            // Exit split stages if needed
-            mStarter.exitSplitScreen();
-        } else if (mSplitScreen != null) {
+        @SplitScreen.StageType int stage = STAGE_TYPE_UNDEFINED;
+        @SplitScreen.StagePosition int position = STAGE_POSITION_UNDEFINED;
+        if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) {
             // Update launch options for the split side we are targeting.
-            final int position = leftOrTop
-                    ? SIDE_STAGE_POSITION_TOP_OR_LEFT : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+            position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT;
             if (!inSplitScreen) {
-                // Update the side stage position to match where we want to launch.
-                mSplitScreen.setSideStagePosition(position);
+                // Launch in the side stage if we are not in split-screen already.
+                stage = STAGE_TYPE_SIDE;
             }
-            mSplitScreen.updateActivityOptions(opts, position);
         }
 
-        if (isTask) {
-            mStarter.startTask(dragData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID), opts);
-        } else if (isShortcut) {
-            mStarter.startShortcut(dragData.getStringExtra(EXTRA_PACKAGE_NAME),
-                    dragData.getStringExtra(EXTRA_SHORTCUT_ID),
-                    opts, dragData.getParcelableExtra(EXTRA_USER));
-        } else {
-            mStarter.startIntent(dragData.getParcelableExtra(EXTRA_PENDING_INTENT), opts);
-        }
+        final ClipDescription description = data.getDescription();
+        final Intent dragData = mSession.dragData;
+        mStarter.startClipDescription(description, dragData, stage, position);
     }
 
     /**
@@ -247,7 +233,6 @@
         int runningTaskActType = ACTIVITY_TYPE_STANDARD;
         boolean runningTaskIsResizeable;
         boolean dragItemSupportsSplitscreen;
-        boolean isPhone;
 
         DragSession(Context context, ActivityTaskManager activityTaskManager,
                 DisplayLayout dispLayout, ClipData data) {
@@ -275,7 +260,6 @@
             final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo();
             dragItemSupportsSplitscreen = info == null
                     || ActivityInfo.isResizeableMode(info.resizeMode);
-            isPhone = mContext.getResources().getConfiguration().smallestScreenWidthDp < 600;
             dragData = mInitialDragData.getItemAt(0).getIntent();
         }
     }
@@ -284,11 +268,33 @@
      * Interface for actually committing the task launches.
      */
     @VisibleForTesting
-    interface Starter {
-        void startTask(int taskId, Bundle activityOptions);
-        void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
-                UserHandle user);
-        void startIntent(PendingIntent intent, Bundle activityOptions);
+    public interface Starter {
+        default void startClipDescription(ClipDescription description, Intent intent,
+                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position) {
+            final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK);
+            final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT);
+            final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)
+                    ? intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS) : new Bundle();
+
+            if (isTask) {
+                final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID);
+                startTask(taskId, stage, position, opts);
+            } else if (isShortcut) {
+                final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+                final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID);
+                final UserHandle user = intent.getParcelableExtra(EXTRA_USER);
+                startShortcut(packageName, id, stage, position, opts, user);
+            } else {
+                startIntent(intent.getParcelableExtra(EXTRA_PENDING_INTENT), stage, position, opts);
+            }
+        }
+        void startTask(int taskId, @SplitScreen.StageType int stage,
+                @SplitScreen.StagePosition int position, @Nullable Bundle options);
+        void startShortcut(String packageName, String shortcutId,
+                @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position,
+                @Nullable Bundle options, UserHandle user);
+        void startIntent(PendingIntent intent, @SplitScreen.StageType int stage,
+                @SplitScreen.StagePosition int position, @Nullable Bundle options);
         void enterSplitScreen(int taskId, boolean leftOrTop);
         void exitSplitScreen();
     }
@@ -299,39 +305,39 @@
      */
     private static class DefaultStarter implements Starter {
         private final Context mContext;
-        private final SplitScreen mSplitScreen;
 
-        public DefaultStarter(Context context, SplitScreen splitScreen) {
+        public DefaultStarter(Context context) {
             mContext = context;
-            mSplitScreen = splitScreen;
         }
 
         @Override
-        public void startTask(int taskId, Bundle activityOptions) {
+        public void startTask(int taskId, int stage, int position,
+                @Nullable Bundle options) {
             try {
-                ActivityTaskManager.getService().startActivityFromRecents(taskId, activityOptions);
+                ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to launch task", e);
             }
         }
 
         @Override
-        public void startShortcut(String packageName, String shortcutId, Bundle activityOptions,
-                UserHandle user) {
+        public void startShortcut(String packageName, String shortcutId, int stage, int position,
+                @Nullable Bundle options, UserHandle user) {
             try {
                 LauncherApps launcherApps =
                         mContext.getSystemService(LauncherApps.class);
                 launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
-                        activityOptions, user);
+                        options, user);
             } catch (ActivityNotFoundException e) {
                 Slog.e(TAG, "Failed to launch shortcut", e);
             }
         }
 
         @Override
-        public void startIntent(PendingIntent intent, Bundle activityOptions) {
+        public void startIntent(PendingIntent intent, int stage, int position,
+                @Nullable Bundle options) {
             try {
-                intent.send(null, 0, null, null, null, null, activityOptions);
+                intent.send(null, 0, null, null, null, null, options);
             } catch (PendingIntent.CanceledException e) {
                 Slog.e(TAG, "Failed to launch activity", e);
             }
@@ -339,14 +345,12 @@
 
         @Override
         public void enterSplitScreen(int taskId, boolean leftOrTop) {
-            mSplitScreen.moveToSideStage(taskId,
-                    leftOrTop ? SIDE_STAGE_POSITION_TOP_OR_LEFT
-                            : SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+            throw new UnsupportedOperationException("enterSplitScreen not implemented by starter");
         }
 
         @Override
         public void exitSplitScreen() {
-            mSplitScreen.exitSplitScreen();
+            throw new UnsupportedOperationException("exitSplitScreen not implemented by starter");
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
index 2df3e2e..962c467 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java
@@ -231,7 +231,6 @@
                     && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
             mAllowTouches = !disallowTouchesUntilAnimationEnd;
             cancelDelayedHide();
-            updateActionViews(stackBounds);
             if (mMenuContainerAnimator != null) {
                 mMenuContainerAnimator.cancel();
             }
@@ -280,6 +279,7 @@
                 setVisibility(VISIBLE);
                 mMenuContainerAnimator.start();
             }
+            updateActionViews(stackBounds);
         } else {
             // If we are already visible, then just start the delayed dismiss and unregister any
             // existing input consumers from the previous drag
@@ -395,7 +395,7 @@
             return true;
         });
 
-        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE || mMenuState == MENU_STATE_NONE) {
             actionsContainer.setVisibility(View.INVISIBLE);
         } else {
             actionsContainer.setVisibility(View.VISIBLE);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 7c1b9d8..2c68092 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -18,12 +18,16 @@
 
 import android.annotation.IntDef;
 import android.app.ActivityManager;
+import android.app.PendingIntent;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.UserHandle;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.draganddrop.DragAndDropPolicy;
 
 import java.io.PrintWriter;
 
@@ -31,46 +35,93 @@
  * Interface to engage split-screen feature.
  */
 @ExternalThread
-public interface SplitScreen {
+public interface SplitScreen extends DragAndDropPolicy.Starter {
     /**
-     * Specifies that the side-stage is positioned at the top half of the screen if
+     * Stage position isn't specified normally meaning to use what ever it is currently set to.
+     */
+    int STAGE_POSITION_UNDEFINED = -1;
+    /**
+     * Specifies that a stage is positioned at the top half of the screen if
      * in portrait mode or at the left half of the screen if in landscape mode.
      */
-    int SIDE_STAGE_POSITION_TOP_OR_LEFT = 0;
+    int STAGE_POSITION_TOP_OR_LEFT = 0;
 
     /**
-     * Specifies that the side-stage is positioned at the bottom half of the screen if
+     * Specifies that a stage is positioned at the bottom half of the screen if
      * in portrait mode or at the right half of the screen if in landscape mode.
      */
-    int SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
+    int STAGE_POSITION_BOTTOM_OR_RIGHT = 1;
 
-    @IntDef(prefix = { "SIDE_STAGE_POSITION_" }, value = {
-            SIDE_STAGE_POSITION_TOP_OR_LEFT,
-            SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT
+    @IntDef(prefix = { "STAGE_POSITION_" }, value = {
+            STAGE_POSITION_UNDEFINED,
+            STAGE_POSITION_TOP_OR_LEFT,
+            STAGE_POSITION_BOTTOM_OR_RIGHT
     })
-    @interface SideStagePosition {}
+    @interface StagePosition {}
+
+    /**
+     * Stage type isn't specified normally meaning to use what ever the default is.
+     * E.g. exit split-screen and launch the app in fullscreen.
+     */
+    int STAGE_TYPE_UNDEFINED = -1;
+    /**
+     * The main stage type.
+     * @see MainStage
+     */
+    int STAGE_TYPE_MAIN = 0;
+
+    /**
+     * The side stage type.
+     * @see SideStage
+     */
+    int STAGE_TYPE_SIDE = 1;
+
+    @IntDef(prefix = { "STAGE_TYPE_" }, value = {
+            STAGE_TYPE_UNDEFINED,
+            STAGE_TYPE_MAIN,
+            STAGE_TYPE_SIDE
+    })
+    @interface StageType {}
+
+    /** Callback interface for listening to changes in a split-screen stage. */
+    interface SplitScreenListener {
+        void onStagePositionChanged(@StageType int stage, @StagePosition int position);
+        void onTaskStageChanged(int taskId, @StageType int stage);
+    }
 
     /** @return {@code true} if split-screen is currently visible. */
     boolean isSplitScreenVisible();
     /** Moves a task in the side-stage of split-screen. */
-    boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition);
+    boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition);
     /** Moves a task in the side-stage of split-screen. */
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
-            @SideStagePosition int sideStagePosition);
+            @StagePosition int sideStagePosition);
     /** Removes a task from the side-stage of split-screen. */
     boolean removeFromSideStage(int taskId);
     /** Sets the position of the side-stage. */
-    void setSideStagePosition(@SideStagePosition int sideStagePosition);
+    void setSideStagePosition(@StagePosition int sideStagePosition);
     /** Hides the side-stage if it is currently visible. */
     void setSideStageVisibility(boolean visible);
+    default void enterSplitScreen(int taskId, boolean leftOrTop) {
+        moveToSideStage(taskId,
+                leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT);
+    }
     /** Removes the split-screen stages. */
     void exitSplitScreen();
     /** Gets the stage bounds. */
     void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds);
-    /** Updates the launch activity options for the split position we want to launch it in. */
-    void updateActivityOptions(Bundle opts, @SideStagePosition int position);
     /** Dumps current status of split-screen. */
     void dump(@NonNull PrintWriter pw, String prefix);
     /** Called when the shell organizer has been registered. */
     void onOrganizerRegistered();
+
+    void registerSplitScreenListener(SplitScreenListener listener);
+    void unregisterSplitScreenListener(SplitScreenListener listener);
+
+    void startTask(int taskId,
+            @StageType int stage, @StagePosition int position, @Nullable Bundle options);
+    void startShortcut(String packageName, String shortcutId, @StageType int stage,
+            @StagePosition int position, @Nullable Bundle options, UserHandle user);
+    void startIntent(PendingIntent intent,
+            @StageType int stage, @StagePosition int position, @Nullable Bundle options);
 }
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 27d3b81..18dd53b 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
@@ -19,11 +19,19 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.pm.LauncherApps;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTaskOrganizer;
@@ -69,7 +77,7 @@
     }
 
     @Override
-    public boolean moveToSideStage(int taskId, @SideStagePosition int sideStagePosition) {
+    public boolean moveToSideStage(int taskId, @StagePosition int sideStagePosition) {
         final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
         if (task == null) {
             throw new IllegalArgumentException("Unknown taskId" + taskId);
@@ -79,7 +87,7 @@
 
     @Override
     public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
-            @SideStagePosition int sideStagePosition) {
+            @StagePosition int sideStagePosition) {
         return mStageCoordinator.moveToSideStage(task, sideStagePosition);
     }
 
@@ -89,7 +97,7 @@
     }
 
     @Override
-    public void setSideStagePosition(@SideStagePosition int sideStagePosition) {
+    public void setSideStagePosition(@StagePosition int sideStagePosition) {
         mStageCoordinator.setSideStagePosition(sideStagePosition);
     }
 
@@ -109,8 +117,103 @@
     }
 
     @Override
-    public void updateActivityOptions(Bundle opts, @SideStagePosition int position) {
-        mStageCoordinator.updateActivityOptions(opts, position);
+    public void registerSplitScreenListener(SplitScreenListener listener) {
+        mStageCoordinator.registerSplitScreenListener(listener);
+    }
+
+    @Override
+    public void unregisterSplitScreenListener(SplitScreenListener listener) {
+        mStageCoordinator.unregisterSplitScreenListener(listener);
+    }
+
+    @Override
+    public void startTask(int taskId,
+            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+        options = resolveStartStage(stage, position, options);
+
+        try {
+            ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to launch task", e);
+        }
+    }
+
+    @Override
+    public void startShortcut(String packageName, String shortcutId, @StageType int stage,
+            @StagePosition int position, @Nullable Bundle options, UserHandle user) {
+        options = resolveStartStage(stage, position, options);
+
+        try {
+            LauncherApps launcherApps =
+                    mContext.getSystemService(LauncherApps.class);
+            launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
+                    options, user);
+        } catch (ActivityNotFoundException e) {
+            Slog.e(TAG, "Failed to launch shortcut", e);
+        }
+    }
+
+    @Override
+    public void startIntent(PendingIntent intent,
+            @StageType int stage, @StagePosition int position, @Nullable Bundle options) {
+        options = resolveStartStage(stage, position, options);
+
+        try {
+            intent.send(null, 0, null, null, null, null, options);
+        } catch (PendingIntent.CanceledException e) {
+            Slog.e(TAG, "Failed to launch activity", e);
+        }
+    }
+
+    private Bundle resolveStartStage(@StageType int stage, @StagePosition int position,
+            @Nullable Bundle options) {
+        switch (stage) {
+            case STAGE_TYPE_UNDEFINED: {
+                // Use the stage of the specified position is valid.
+                if (position != STAGE_POSITION_UNDEFINED) {
+                    if (position == mStageCoordinator.getSideStagePosition()) {
+                        options = resolveStartStage(STAGE_TYPE_SIDE, position, options);
+                    } else {
+                        options = resolveStartStage(STAGE_TYPE_MAIN, position, options);
+                    }
+                } else {
+                    // Exit split-screen and launch fullscreen since stage wasn't specified.
+                    mStageCoordinator.exitSplitScreen();
+                }
+                break;
+            }
+            case STAGE_TYPE_SIDE: {
+                if (position != STAGE_POSITION_UNDEFINED) {
+                    mStageCoordinator.setSideStagePosition(position);
+                } else {
+                    position = mStageCoordinator.getSideStagePosition();
+                }
+                if (options == null) {
+                    options = new Bundle();
+                }
+                mStageCoordinator.updateActivityOptions(options, position);
+                break;
+            }
+            case STAGE_TYPE_MAIN: {
+                if (position != STAGE_POSITION_UNDEFINED) {
+                    // Set the side stage opposite of what we want to the main stage.
+                    final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT
+                            ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+                    mStageCoordinator.setSideStagePosition(sideStagePosition);
+                } else {
+                    position = mStageCoordinator.getMainStagePosition();
+                }
+                if (options == null) {
+                    options = new Bundle();
+                }
+                mStageCoordinator.updateActivityOptions(options, position);
+                break;
+            }
+            default:
+                throw new IllegalArgumentException("Unknown stage=" + stage);
+        }
+
+        return options;
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index d571e75..176852b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -17,12 +17,14 @@
 package com.android.wm.shell.splitscreen;
 
 import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -41,6 +43,8 @@
 import com.android.wm.shell.common.split.SplitLayout;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
@@ -64,8 +68,7 @@
     private final StageListenerImpl mMainStageListener = new StageListenerImpl();
     private final SideStage mSideStage;
     private final StageListenerImpl mSideStageListener = new StageListenerImpl();
-    private @SplitScreen.SideStagePosition int mSideStagePosition =
-            SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+    private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT;
 
     private final int mDisplayId;
     private SplitLayout mSplitLayout;
@@ -75,6 +78,7 @@
     private final ShellTaskOrganizer mTaskOrganizer;
     private DisplayAreaInfo mDisplayAreaInfo;
     private final Context mContext;
+    private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>();
 
     StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer) {
@@ -107,7 +111,7 @@
     }
 
     boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
-            @SplitScreen.SideStagePosition int sideStagePosition) {
+            @SplitScreen.StagePosition int sideStagePosition) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
         mSideStagePosition = sideStagePosition;
         mMainStage.activate(getMainStageBounds(), wct);
@@ -130,7 +134,16 @@
         return result;
     }
 
-    void setSideStagePosition(@SplitScreen.SideStagePosition int sideStagePosition) {
+    @SplitScreen.StagePosition int getSideStagePosition() {
+        return mSideStagePosition;
+    }
+
+    @SplitScreen.StagePosition int getMainStagePosition() {
+        return mSideStagePosition ==  STAGE_POSITION_TOP_OR_LEFT
+                ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT;
+    }
+
+    void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) {
         mSideStagePosition = sideStagePosition;
         if (mSideStageListener.mVisible) {
             onStageVisibilityChanged(mSideStageListener);
@@ -163,7 +176,7 @@
         outBottomOrRightBounds.set(mSplitLayout.getBounds2());
     }
 
-    void updateActivityOptions(Bundle opts, @SplitScreen.SideStagePosition int position) {
+    void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) {
         final StageTaskListener stage = position == mSideStagePosition ? mSideStage : mMainStage;
         opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
 
@@ -176,6 +189,35 @@
         }
     }
 
+    void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+        if (mListeners.contains(listener)) return;
+        mListeners.add(listener);
+        listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition());
+        listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition());
+        mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE);
+        mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN);
+    }
+
+    void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) {
+        mListeners.remove(listener);
+    }
+
+    private void onStageChildTaskStatusChanged(
+            StageListenerImpl stageListener, int taskId, boolean present) {
+
+        int stage;
+        if (present) {
+            stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+        } else {
+            // No longer on any stage
+            stage = STAGE_TYPE_UNDEFINED;
+        }
+
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            mListeners.get(i).onTaskStageChanged(taskId, stage);
+        }
+    }
+
     private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
         if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
             final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -299,7 +341,7 @@
     @Override
     public void onSnappedToDismiss(boolean bottomOrRight) {
         final boolean mainStageToTop = bottomOrRight
-                && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+                && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT;
         exitSplitScreen(mainStageToTop ? mMainStage : mSideStage);
     }
 
@@ -326,8 +368,8 @@
 
     @Override
     public void onDoubleTappedDivider() {
-        setSideStagePosition(mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
-                ? SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT : SIDE_STAGE_POSITION_TOP_OR_LEFT);
+        setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
+                ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT);
     }
 
     @Override
@@ -380,12 +422,12 @@
     }
 
     private Rect getSideStageBounds() {
-        return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+        return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
                 ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2();
     }
 
     private Rect getMainStageBounds() {
-        return mSideStagePosition == SIDE_STAGE_POSITION_TOP_OR_LEFT
+        return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT
                 ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1();
     }
 
@@ -429,6 +471,11 @@
         }
 
         @Override
+        public void onChildTaskStatusChanged(int taskId, boolean present) {
+            StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present);
+        }
+
+        @Override
         public void onRootTaskVanished() {
             reset();
             StageCoordinator.this.onStageRootTaskVanished(this);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 1aa7552..6532993 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -58,6 +58,7 @@
     public interface StageListenerCallbacks {
         void onRootTaskAppeared();
         void onStatusChanged(boolean visible, boolean hasChildren);
+        void onChildTaskStatusChanged(int taskId, boolean present);
         void onRootTaskVanished();
     }
     private final StageListenerCallbacks mCallbacks;
@@ -83,9 +84,11 @@
             mRootTaskInfo = taskInfo;
             mCallbacks.onRootTaskAppeared();
         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
-            mChildrenLeashes.put(taskInfo.taskId, leash);
-            mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
+            final int taskId = taskInfo.taskId;
+            mChildrenLeashes.put(taskId, leash);
+            mChildrenTaskInfo.put(taskId, taskInfo);
             updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */);
+            mCallbacks.onChildTaskStatusChanged(taskId, true /* present */);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -120,6 +123,7 @@
             mChildrenTaskInfo.remove(taskId);
             mChildrenLeashes.remove(taskId);
             sendStatusChanged();
+            mCallbacks.onChildTaskStatusChanged(taskId, false /* present */);
         } else {
             throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo
                     + "\n mRootTaskInfo: " + mRootTaskInfo);
@@ -134,6 +138,13 @@
         wct.reorder(mRootTaskInfo.token, visible /* onTop */);
     }
 
+    void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener,
+            @SplitScreen.StageType int stage) {
+        for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) {
+            listener.onTaskStageChanged(mChildrenTaskInfo.keyAt(i), stage);
+        }
+    }
+
     private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo,
             SurfaceControl leash, boolean firstAppeared) {
         final Point taskPositionInParent = taskInfo.positionInParent;
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 79bdaf4..2572106 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
@@ -29,6 +29,10 @@
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
 import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
 
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
@@ -86,11 +90,9 @@
     @Mock
     private ActivityTaskManager mActivityTaskManager;
 
+    // Both the split-screen and start interface.
     @Mock
-    private SplitScreen mSplitScreen;
-
-    @Mock
-    private DragAndDropPolicy.Starter mStarter;
+    private SplitScreen mSplitScreenStarter;
 
     private DisplayLayout mLandscapeDisplayLayout;
     private DisplayLayout mPortraitDisplayLayout;
@@ -126,7 +128,7 @@
         mInsets = Insets.of(0, 0, 0, 0);
 
         mPolicy = new DragAndDropPolicy(
-                mContext, mActivityTaskManager, mSplitScreen, mStarter);
+                mContext, mActivityTaskManager, mSplitScreenStarter, mSplitScreenStarter);
         mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY);
         setClipDataResizeable(mNonResizeableActivityClipData, false);
@@ -191,7 +193,7 @@
     }
 
     private void setInSplitScreen(boolean inSplitscreen) {
-        doReturn(inSplitscreen).when(mSplitScreen).isSplitScreenVisible();
+        doReturn(inSplitscreen).when(mSplitScreenStarter).isSplitScreenVisible();
     }
 
     @Test
@@ -202,7 +204,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
     }
 
     @Test
@@ -213,12 +216,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).exitSplitScreen();
-        verify(mStarter).startIntent(any(), any());
-        reset(mStarter);
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
     }
 
     @Test
@@ -229,12 +233,13 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).exitSplitScreen();
-        verify(mStarter).startIntent(any(), any());
-        reset(mStarter);
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        reset(mSplitScreenStarter);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
     }
 
     @Test
@@ -245,7 +250,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
     }
 
     @Test
@@ -256,7 +262,8 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
     }
 
     @Test
@@ -268,12 +275,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
-        reset(mStarter);
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
     }
 
     @Test
@@ -285,12 +294,14 @@
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN, TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
 
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
-        reset(mStarter);
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED));
+        reset(mSplitScreenStarter);
 
         // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs
         mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData);
-        verify(mStarter).startIntent(any(), any());
+        verify(mSplitScreenStarter).startClipDescription(any(), any(),
+                eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT));
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 168e0df..d2d1812 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -19,7 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
 
-import static com.android.wm.shell.splitscreen.SplitScreen.SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
@@ -71,7 +71,7 @@
     public void testMoveToSideStage() {
         final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
 
-        mStageCoordinator.moveToSideStage(task, SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT);
+        mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT);
 
         verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class));
         verify(mSideStage).addTask(eq(task), any(Rect.class),
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index b54f7d8..4b4284a 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -41,6 +41,7 @@
         "AssetDir.cpp",
         "AssetManager.cpp",
         "AssetManager2.cpp",
+        "AssetsProvider.cpp",
         "AttributeResolution.cpp",
         "ChunkIterator.cpp",
         "ConfigDescription.cpp",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 011a0de..ca5981c0 100755
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -16,533 +16,145 @@
 
 #include "androidfw/ApkAssets.h"
 
-#include <algorithm>
-
 #include "android-base/errors.h"
-#include "android-base/file.h"
 #include "android-base/logging.h"
-#include "android-base/stringprintf.h"
-#include "android-base/unique_fd.h"
-#include "android-base/utf8.h"
-#include "utils/Compat.h"
-#include "ziparchive/zip_archive.h"
-
-#include "androidfw/Asset.h"
-#include "androidfw/Idmap.h"
-#include "androidfw/misc.h"
-#include "androidfw/Util.h"
 
 namespace android {
 
 using base::SystemErrorCodeToString;
 using base::unique_fd;
 
-static const std::string kResourcesArsc("resources.arsc");
+constexpr const char* kResourcesArsc = "resources.arsc";
 
-ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
-                     std::string path,
-                     time_t last_mod_time,
-                     package_property_t property_flags)
-    : assets_provider_(std::move(assets_provider)),
-      path_(std::move(path)),
-      last_mod_time_(last_mod_time),
-      property_flags_(property_flags) {
+ApkAssets::ApkAssets(std::unique_ptr<Asset> resources_asset,
+                     std::unique_ptr<LoadedArsc> loaded_arsc,
+                     std::unique_ptr<AssetsProvider> assets,
+                     package_property_t property_flags,
+                     std::unique_ptr<Asset> idmap_asset,
+                     std::unique_ptr<LoadedIdmap> loaded_idmap)
+    : resources_asset_(std::move(resources_asset)),
+      loaded_arsc_(std::move(loaded_arsc)),
+      assets_provider_(std::move(assets)),
+      property_flags_(property_flags),
+      idmap_asset_(std::move(idmap_asset)),
+      loaded_idmap_(std::move(loaded_idmap)) {}
+
+std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path, package_property_t flags) {
+  return Load(ZipAssetsProvider::Create(path), flags);
 }
 
-// Provides asset files from a zip file.
-class ZipAssetsProvider : public AssetsProvider {
- public:
-  ~ZipAssetsProvider() override = default;
-
-  static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
-    ::ZipArchiveHandle unmanaged_handle;
-    const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
-    if (result != 0) {
-      LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
-      ::CloseArchive(unmanaged_handle);
-      return {};
-    }
-
-    return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
-  }
-
-  static std::unique_ptr<const AssetsProvider> Create(
-      unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
-      const off64_t length = ApkAssets::kUnknownLength) {
-
-    ::ZipArchiveHandle unmanaged_handle;
-    const int32_t result = (length == ApkAssets::kUnknownLength)
-        ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
-        : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
-                               offset);
-
-    if (result != 0) {
-      LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
-                 << " and length " << length << ": " << ::ErrorCodeString(result);
-      ::CloseArchive(unmanaged_handle);
-      return {};
-    }
-
-    return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
-                                                                 unmanaged_handle));
-  }
-
-  // Iterate over all files and directories within the zip. The order of iteration is not
-  // guaranteed to be the same as the order of elements in the central directory but is stable for a
-  // given zip file.
-  bool ForEachFile(const std::string& root_path,
-                   const std::function<void(const StringPiece&, FileType)>& f) const override {
-    // If this is a resource loader from an .arsc, there will be no zip handle
-    if (zip_handle_ == nullptr) {
-      return false;
-    }
-
-    std::string root_path_full = root_path;
-    if (root_path_full.back() != '/') {
-      root_path_full += '/';
-    }
-
-    void* cookie;
-    if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
-      return false;
-    }
-
-    std::string name;
-    ::ZipEntry entry{};
-
-    // We need to hold back directories because many paths will contain them and we want to only
-    // surface one.
-    std::set<std::string> dirs{};
-
-    int32_t result;
-    while ((result = ::Next(cookie, &entry, &name)) == 0) {
-      StringPiece full_file_path(name);
-      StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
-
-      if (!leaf_file_path.empty()) {
-        auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
-        if (iter != leaf_file_path.end()) {
-          std::string dir =
-              leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
-          dirs.insert(std::move(dir));
-        } else {
-          f(leaf_file_path, kFileTypeRegular);
-        }
-      }
-    }
-    ::EndIteration(cookie);
-
-    // Now present the unique directories.
-    for (const std::string& dir : dirs) {
-      f(dir, kFileTypeDirectory);
-    }
-
-    // -1 is end of iteration, anything else is an error.
-    return result == -1;
-  }
-
- protected:
-  std::unique_ptr<Asset> OpenInternal(
-      const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
-    if (file_exists) {
-      *file_exists = false;
-    }
-
-    ::ZipEntry entry;
-    int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
-    if (result != 0) {
-      return {};
-    }
-
-    if (file_exists) {
-      *file_exists = true;
-    }
-
-    const int fd = ::GetFileDescriptor(zip_handle_.get());
-    const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
-    incfs::IncFsFileMap asset_map;
-    if (entry.method == kCompressDeflated) {
-      if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
-        LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
-        return {};
-      }
-
-      std::unique_ptr<Asset> asset =
-          Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
-      if (asset == nullptr) {
-        LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
-        return {};
-      }
-      return asset;
-    }
-
-    if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
-      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
-      return {};
-    }
-
-    unique_fd ufd;
-    if (!GetPath()) {
-      // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
-      // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
-      // to create new file descriptors.
-      ufd = unique_fd(dup(fd));
-      if (!ufd.ok()) {
-        LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
-        return {};
-      }
-    }
-
-    auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
-    if (asset == nullptr) {
-      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
-      return {};
-    }
-    return asset;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
-
-  explicit ZipAssetsProvider(std::string path,
-                             std::string friendly_name,
-                             ZipArchiveHandle unmanaged_handle)
-                             : zip_handle_(unmanaged_handle, ::CloseArchive),
-                               path_(std::move(path)),
-                               friendly_name_(std::move(friendly_name)) { }
-
-  const char* GetPath() const {
-    return path_.empty() ? nullptr : path_.c_str();
-  }
-
-  using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
-  ZipArchivePtr zip_handle_;
-  std::string path_;
-  std::string friendly_name_;
-};
-
-class DirectoryAssetsProvider : AssetsProvider {
- public:
-  ~DirectoryAssetsProvider() override = default;
-
-  static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
-    struct stat sb{};
-    const int result = stat(path.c_str(), &sb);
-    if (result == -1) {
-      LOG(ERROR) << "Failed to find directory '" << path << "'.";
-      return nullptr;
-    }
-
-    if (!S_ISDIR(sb.st_mode)) {
-      LOG(ERROR) << "Path '" << path << "' is not a directory.";
-      return nullptr;
-    }
-
-    return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
-  }
-
- protected:
-  std::unique_ptr<Asset> OpenInternal(
-      const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
-    const std::string resolved_path = ResolvePath(path);
-    if (file_exists) {
-      struct stat sb{};
-      const int result = stat(resolved_path.c_str(), &sb);
-      *file_exists = result != -1 && S_ISREG(sb.st_mode);
-    }
-
-    return ApkAssets::CreateAssetFromFile(resolved_path);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
-
-  explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
-
-  inline std::string ResolvePath(const std::string& path) const {
-    return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
-  }
-
-  const std::string path_;
-};
-
-// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
-class EmptyAssetsProvider : public AssetsProvider {
- public:
-  EmptyAssetsProvider() = default;
-  ~EmptyAssetsProvider() override = default;
-
- protected:
-  std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
-                                      Asset::AccessMode /* mode */,
-                                      bool* file_exists) const override {
-    if (file_exists) {
-      *file_exists = false;
-    }
-    return nullptr;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
-};
-
-// AssetProvider implementation
-class MultiAssetsProvider : public AssetsProvider {
- public:
-  ~MultiAssetsProvider() override = default;
-
-  static std::unique_ptr<const AssetsProvider> Create(
-      std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
-    CHECK(parent != nullptr) << "parent provider must not be null";
-    return (!child) ? std::move(parent)
-                    : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
-                        std::move(child), std::move(parent)));
-  }
-
-  bool ForEachFile(const std::string& root_path,
-                   const std::function<void(const StringPiece&, FileType)>& f) const override {
-    // TODO: Only call the function once for files defined in the parent and child
-    return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
-  }
-
- protected:
-  std::unique_ptr<Asset> OpenInternal(
-      const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
-    auto asset = child_->Open(path, mode, file_exists);
-    return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
-
-  MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
-                      std::unique_ptr<const AssetsProvider> parent)
-                      : child_(std::move(child)), parent_(std::move(parent)) { }
-
-  std::unique_ptr<const AssetsProvider> child_;
-  std::unique_ptr<const AssetsProvider> parent_;
-};
-
-// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
-// file.
-std::unique_ptr<const ApkAssets> ApkAssets::Load(
-    const std::string& path, const package_property_t flags,
-    std::unique_ptr<const AssetsProvider> override_asset) {
-  auto assets = ZipAssetsProvider::Create(path);
-  return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
-                  : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadFromFd(base::unique_fd fd,
+                                                 const std::string& debug_name,
+                                                 package_property_t flags,
+                                                 off64_t offset,
+                                                 off64_t len) {
+  return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags);
 }
 
-// Opens the archive using the file file descriptor with the specified file offset and read length.
-// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
-    unique_fd fd, const std::string& friendly_name, const package_property_t flags,
-    std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
-    const off64_t length) {
-  CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
-  CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
-                                                 << kUnknownLength;
-
-  auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
-  return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
-                  : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::Load(std::unique_ptr<AssetsProvider> assets,
+                                           package_property_t flags) {
+  return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
 }
 
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
-    const std::string& path, const package_property_t flags,
-    std::unique_ptr<const AssetsProvider> override_asset) {
-
-  auto assets = CreateAssetFromFile(path);
-  return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
-                  : nullptr;
+std::unique_ptr<ApkAssets> ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
+                                                std::unique_ptr<AssetsProvider> assets,
+                                                package_property_t flags) {
+  if (resources_asset == nullptr) {
+    return {};
+  }
+  return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */,
+                  nullptr /* loaded_idmap */);
 }
 
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
-    unique_fd fd, const std::string& friendly_name, const package_property_t flags,
-    std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
-    const off64_t length) {
-
-  auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
-  return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
-                                  std::move(override_asset))
-                  : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
-                                                        const package_property_t flags) {
+std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
+                                                  package_property_t flags) {
   CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
-  std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
+  auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path);
   if (idmap_asset == nullptr) {
+    LOG(ERROR) << "failed to read IDMAP " << idmap_path;
     return {};
   }
 
-  const StringPiece idmap_data(
-      reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
-      static_cast<size_t>(idmap_asset->getLength()));
-  std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
+  StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)),
+                         static_cast<size_t>(idmap_asset->getLength()));
+  auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
   if (loaded_idmap == nullptr) {
     LOG(ERROR) << "failed to load IDMAP " << idmap_path;
     return {};
   }
-  
-  auto overlay_path = std::string(loaded_idmap->OverlayApkPath());
-  auto assets = ZipAssetsProvider::Create(overlay_path);
-  return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
-                             nullptr /* override_asset */, std::move(idmap_asset),
-                             std::move(loaded_idmap))
-                  : nullptr;
-}
 
-std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
-    const std::string& path, const package_property_t flags,
-    std::unique_ptr<const AssetsProvider> override_asset) {
-
-  auto assets = DirectoryAssetsProvider::Create(path);
-  return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
-                  : nullptr;
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
-    const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
-
-  auto assets = (override_asset) ? std::move(override_asset)
-                                 : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
-  std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
-                                                      -1 /* last_mod-time */, flags));
-  loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
-  // Need to force a move for mingw32.
-  return std::move(loaded_apk);
-}
-
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
-  unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
-  if (!fd.ok()) {
-    LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
+  const std::string overlay_path(loaded_idmap->OverlayApkPath());
+  auto overlay_assets = ZipAssetsProvider::Create(overlay_path);
+  if (overlay_assets == nullptr) {
     return {};
   }
 
-  return CreateAssetFromFd(std::move(fd), path.c_str());
+  return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset),
+                  std::move(loaded_idmap));
 }
 
-std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
-                                                    const char* path,
-                                                    off64_t offset,
-                                                    off64_t length) {
-  CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
-  CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
-                                                 << kUnknownLength;
-  if (length == kUnknownLength) {
-    length = lseek64(fd, 0, SEEK_END);
-    if (length < 0) {
-      LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
-                 << SystemErrorCodeToString(errno);
-      return {};
-    }
-  }
-
-  incfs::IncFsFileMap file_map;
-  if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
-    LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
-               << SystemErrorCodeToString(errno);
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
+                                               package_property_t property_flags,
+                                               std::unique_ptr<Asset> idmap_asset,
+                                               std::unique_ptr<LoadedIdmap> loaded_idmap) {
+  if (assets == nullptr) {
     return {};
   }
 
-  // If `path` is set, do not pass ownership of the `fd` to the new Asset since
-  // Asset::openFileDescriptor can use `path` to create new file descriptors.
-  return Asset::createFromUncompressedMap(std::move(file_map),
-                                          Asset::AccessMode::ACCESS_RANDOM,
-                                          (path) ? base::unique_fd(-1) : std::move(fd));
-}
-
-std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
-    std::unique_ptr<const AssetsProvider> assets, const std::string& path,
-    package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
-    std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
-
-  const time_t last_mod_time = getFileModDate(path.c_str());
-
   // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
   bool resources_asset_exists = false;
-  auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
-                                       &resources_asset_exists);
-
-  assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
-
-  // Wrap the handle in a unique_ptr so it gets automatically closed.
-  std::unique_ptr<ApkAssets>
-      loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
-
-  if (!resources_asset_exists) {
-    loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
-    return std::move(loaded_apk);
-  }
-
-  loaded_apk->resources_asset_ = std::move(resources_asset_);
-  if (!loaded_apk->resources_asset_) {
-    LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
+  auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
+                                      &resources_asset_exists);
+  if (resources_asset == nullptr && resources_asset_exists) {
+    LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName()
+               << "'.";
     return {};
   }
 
-  // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
-  loaded_apk->idmap_asset_ = std::move(idmap_asset);
-  loaded_apk->loaded_idmap_ = std::move(idmap);
-
-  const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
-  const size_t length = loaded_apk->resources_asset_->getLength();
-  if (!data || length == 0) {
-    LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
-    return {};
-  }
-
-  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
-                                              property_flags);
-  if (!loaded_apk->loaded_arsc_) {
-    LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
-    return {};
-  }
-
-  // Need to force a move for mingw32.
-  return std::move(loaded_apk);
+  return LoadImpl(std::move(resources_asset), std::move(assets), property_flags,
+                  std::move(idmap_asset), std::move(loaded_idmap));
 }
 
-std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
-    std::unique_ptr<Asset> resources_asset, const std::string& path,
-    package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
-
-  const time_t last_mod_time = getFileModDate(path.c_str());
-
-  auto assets = (override_assets) ? std::move(override_assets)
-                                  : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
-
-  std::unique_ptr<ApkAssets> loaded_apk(
-      new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
-  loaded_apk->resources_asset_ = std::move(resources_asset);
-
-  const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
-  const size_t length = loaded_apk->resources_asset_->getLength();
-  if (!data || length == 0) {
-    LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
+std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
+                                               std::unique_ptr<AssetsProvider> assets,
+                                               package_property_t property_flags,
+                                               std::unique_ptr<Asset> idmap_asset,
+                                               std::unique_ptr<LoadedIdmap> loaded_idmap) {
+  if (assets == nullptr ) {
     return {};
   }
 
-  loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
-                                              property_flags);
-  if (loaded_apk->loaded_arsc_ == nullptr) {
-    LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
+  std::unique_ptr<LoadedArsc> loaded_arsc;
+  if (resources_asset != nullptr) {
+    const auto data = resources_asset->getIncFsBuffer(true /* aligned */);
+    const size_t length = resources_asset->getLength();
+    if (!data || length == 0) {
+      LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'.";
+      return {};
+    }
+    loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags);
+  } else {
+    loaded_arsc = LoadedArsc::CreateEmpty();
+  }
+
+  if (loaded_arsc == nullptr) {
+    LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'.";
     return {};
   }
 
-  // Need to force a move for mingw32.
-  return std::move(loaded_apk);
+  return std::unique_ptr<ApkAssets>(new ApkAssets(std::move(resources_asset),
+                                                  std::move(loaded_arsc), std::move(assets),
+                                                  property_flags, std::move(idmap_asset),
+                                                  std::move(loaded_idmap)));
+}
+
+const std::string& ApkAssets::GetPath() const {
+  return assets_provider_->GetDebugName();
 }
 
 bool ApkAssets::IsUpToDate() const {
-  if (IsLoader()) {
-    // Loaders are invalidated by the app, not the system, so assume they are up to date.
-    return true;
-  }
-  return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
-      last_mod_time_ == getFileModDate(path_.c_str());
+  // Loaders are invalidated by the app, not the system, so assume they are up to date.
+  return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
+                        && assets_provider_->IsUpToDate());
 }
-
 }  // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
new file mode 100644
index 0000000..23cacf8
--- /dev/null
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2021 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 "androidfw/AssetsProvider.h"
+
+#include <sys/stat.h>
+
+#include <android-base/errors.h>
+#include <android-base/stringprintf.h>
+#include <android-base/utf8.h>
+#include <ziparchive/zip_archive.h>
+
+namespace android {
+namespace {
+constexpr const char* kEmptyDebugString = "<empty>";
+} // namespace
+
+std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
+                                            bool* file_exists) const {
+  return OpenInternal(path, mode, file_exists);
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFile(const std::string& path) {
+  base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC));
+  if (!fd.ok()) {
+    LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno);
+    return {};
+  }
+
+  return CreateAssetFromFd(std::move(fd), path.c_str());
+}
+
+std::unique_ptr<Asset> AssetsProvider::CreateAssetFromFd(base::unique_fd fd,
+                                                         const char* path,
+                                                         off64_t offset,
+                                                         off64_t length) {
+  CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
+  CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
+                                                 << kUnknownLength;
+  if (length == kUnknownLength) {
+    length = lseek64(fd, 0, SEEK_END);
+    if (length < 0) {
+      LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
+                 << base::SystemErrorCodeToString(errno);
+      return {};
+    }
+  }
+
+  incfs::IncFsFileMap file_map;
+  if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
+    LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': "
+               << base::SystemErrorCodeToString(errno);
+    return {};
+  }
+
+  // If `path` is set, do not pass ownership of the `fd` to the new Asset since
+  // Asset::openFileDescriptor can use `path` to create new file descriptors.
+  return Asset::createFromUncompressedMap(std::move(file_map),
+                                          Asset::AccessMode::ACCESS_RANDOM,
+                                          (path != nullptr) ? base::unique_fd(-1) : std::move(fd));
+}
+
+ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path)
+    : value_(std::forward<std::string>(value)), is_path_(is_path) {}
+
+const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const {
+  return is_path_ ? &value_ : nullptr;
+}
+
+const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const {
+  return value_;
+}
+
+ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path,
+                                     time_t last_mod_time)
+    : zip_handle_(handle, ::CloseArchive),
+      name_(std::forward<PathOrDebugName>(path)),
+      last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path) {
+  ZipArchiveHandle handle;
+  if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) {
+    LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
+    CloseArchive(handle);
+    return {};
+  }
+
+  struct stat sb{.st_mtime = -1};
+  if (stat(path.c_str(), &sb) < 0) {
+    // Stat requires execute permissions on all directories path to the file. If the process does
+    // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+    // always have to return true.
+    LOG(WARNING) << "Failed to stat file '" << path << "': "
+                 << base::SystemErrorCodeToString(errno);
+  }
+
+  return std::unique_ptr<ZipAssetsProvider>(
+      new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
+                                                    true /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
+                                                             std::string friendly_name,
+                                                             off64_t offset,
+                                                             off64_t len) {
+  ZipArchiveHandle handle;
+  const int released_fd = fd.release();
+  const int32_t result = (len == AssetsProvider::kUnknownLength)
+      ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle)
+      : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset);
+
+  if (result != 0) {
+    LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
+               << " and length " << len << ": " << ::ErrorCodeString(result);
+    CloseArchive(handle);
+    return {};
+  }
+
+  struct stat sb{.st_mtime = -1};
+  if (fstat(released_fd, &sb) < 0) {
+    // Stat requires execute permissions on all directories path to the file. If the process does
+    // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
+    // always have to return true.
+    LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': "
+                 << base::SystemErrorCodeToString(errno);
+  }
+
+  return std::unique_ptr<ZipAssetsProvider>(
+      new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
+                                                    false /* is_path */}, sb.st_mtime));
+}
+
+std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
+                                                       Asset::AccessMode mode,
+                                                       bool* file_exists) const {
+    if (file_exists != nullptr) {
+      *file_exists = false;
+    }
+
+    ZipEntry entry;
+    if (FindEntry(zip_handle_.get(), path, &entry) != 0) {
+      return {};
+    }
+
+    if (file_exists != nullptr) {
+      *file_exists = true;
+    }
+
+    const int fd = GetFileDescriptor(zip_handle_.get());
+    const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get());
+    incfs::IncFsFileMap asset_map;
+    if (entry.method == kCompressDeflated) {
+      if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length,
+                            name_.GetDebugName().c_str())) {
+        LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName()
+                   << "'";
+        return {};
+      }
+
+      std::unique_ptr<Asset> asset =
+          Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
+      if (asset == nullptr) {
+        LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName()
+                   << "'";
+        return {};
+      }
+      return asset;
+    }
+
+    if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length,
+                          name_.GetDebugName().c_str())) {
+      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+      return {};
+    }
+
+    base::unique_fd ufd;
+    if (name_.GetPath() == nullptr) {
+      // If the zip name does not represent a path, create a new `fd` for the new Asset to own in
+      // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a
+      // path, it will be used to create new file descriptors.
+      ufd = base::unique_fd(dup(fd));
+      if (!ufd.ok()) {
+        LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'";
+        return {};
+      }
+    }
+
+    auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
+    if (asset == nullptr) {
+      LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'";
+      return {};
+    }
+    return asset;
+}
+
+bool ZipAssetsProvider::ForEachFile(const std::string& root_path,
+                                    const std::function<void(const StringPiece&, FileType)>& f)
+                                    const {
+    std::string root_path_full = root_path;
+    if (root_path_full.back() != '/') {
+      root_path_full += '/';
+    }
+
+    void* cookie;
+    if (StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
+      return false;
+    }
+
+    std::string name;
+    ::ZipEntry entry{};
+
+    // We need to hold back directories because many paths will contain them and we want to only
+    // surface one.
+    std::set<std::string> dirs{};
+
+    int32_t result;
+    while ((result = Next(cookie, &entry, &name)) == 0) {
+      StringPiece full_file_path(name);
+      StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
+
+      if (!leaf_file_path.empty()) {
+        auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
+        if (iter != leaf_file_path.end()) {
+          std::string dir =
+              leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
+          dirs.insert(std::move(dir));
+        } else {
+          f(leaf_file_path, kFileTypeRegular);
+        }
+      }
+    }
+    EndIteration(cookie);
+
+    // Now present the unique directories.
+    for (const std::string& dir : dirs) {
+      f(dir, kFileTypeDirectory);
+    }
+
+    // -1 is end of iteration, anything else is an error.
+    return result == -1;
+}
+
+const std::string& ZipAssetsProvider::GetDebugName() const {
+  return name_.GetDebugName();
+}
+
+bool ZipAssetsProvider::IsUpToDate() const {
+  struct stat sb{};
+  if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
+    // If fstat fails on the zip archive, return true so the zip archive the resource system does
+    // attempt to refresh the ApkAsset.
+    return true;
+  }
+  return last_mod_time_ == sb.st_mtime;
+}
+
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
+    : dir_(std::forward<std::string>(path)), last_mod_time_(last_mod_time) {}
+
+std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
+  struct stat sb{};
+  const int result = stat(path.c_str(), &sb);
+  if (result == -1) {
+    LOG(ERROR) << "Failed to find directory '" << path << "'.";
+    return nullptr;
+  }
+
+  if (!S_ISDIR(sb.st_mode)) {
+    LOG(ERROR) << "Path '" << path << "' is not a directory.";
+    return nullptr;
+  }
+
+  if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+    path += OS_PATH_SEPARATOR;
+  }
+
+  return std::unique_ptr<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
+                                                                              sb.st_mtime));
+}
+
+std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
+                                                             Asset::AccessMode /* mode */,
+                                                             bool* file_exists) const {
+  const std::string resolved_path = dir_ + path;
+  if (file_exists != nullptr) {
+    struct stat sb{};
+    *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode);
+  }
+
+  return CreateAssetFromFile(resolved_path);
+}
+
+bool DirectoryAssetsProvider::ForEachFile(
+    const std::string& /* root_path */,
+    const std::function<void(const StringPiece&, FileType)>& /* f */)
+    const {
+  return true;
+}
+
+const std::string& DirectoryAssetsProvider::GetDebugName() const {
+  return dir_;
+}
+
+bool DirectoryAssetsProvider::IsUpToDate() const {
+  struct stat sb{};
+  if (stat(dir_.c_str(), &sb) < 0) {
+    // If stat fails on the zip archive, return true so the zip archive the resource system does
+    // attempt to refresh the ApkAsset.
+    return true;
+  }
+  return last_mod_time_ == sb.st_mtime;
+}
+
+MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+                                         std::unique_ptr<AssetsProvider>&& secondary)
+                      : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
+                        secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) {
+  if (primary_->GetDebugName() == kEmptyDebugString) {
+    debug_name_ = secondary_->GetDebugName();
+  } else if (secondary_->GetDebugName() == kEmptyDebugString) {
+    debug_name_ = primary_->GetDebugName();
+  } else {
+    debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName();
+  }
+}
+
+std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
+    std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
+  if (primary == nullptr || secondary == nullptr) {
+    return nullptr;
+  }
+  return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
+                                                                      std::move(secondary)));
+}
+
+std::unique_ptr<Asset> MultiAssetsProvider::OpenInternal(const std::string& path,
+                                                         Asset::AccessMode mode,
+                                                         bool* file_exists) const {
+  auto asset = primary_->Open(path, mode, file_exists);
+  return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists);
+}
+
+bool MultiAssetsProvider::ForEachFile(const std::string& root_path,
+                                      const std::function<void(const StringPiece&, FileType)>& f)
+                                      const {
+  return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f);
+}
+
+const std::string& MultiAssetsProvider::GetDebugName() const {
+  return debug_name_;
+}
+
+bool MultiAssetsProvider::IsUpToDate() const {
+  return primary_->IsUpToDate() && secondary_->IsUpToDate();
+}
+
+std::unique_ptr<AssetsProvider> EmptyAssetsProvider::Create() {
+  return std::make_unique<EmptyAssetsProvider>();
+}
+
+std::unique_ptr<Asset> EmptyAssetsProvider::OpenInternal(const std::string& /* path */,
+                                                         Asset::AccessMode /* mode */,
+                                                         bool* file_exists) const {
+  if (file_exists) {
+    *file_exists = false;
+  }
+  return nullptr;
+}
+
+bool EmptyAssetsProvider::ForEachFile(
+    const std::string& /* root_path */,
+    const std::function<void(const StringPiece&, FileType)>& /* f */) const {
+  return true;
+}
+
+const std::string& EmptyAssetsProvider::GetDebugName() const {
+  const static std::string kEmpty = kEmptyDebugString;
+  return kEmpty;
+}
+
+bool EmptyAssetsProvider::IsUpToDate() const {
+  return true;
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index adb383f95..f216f55 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -257,8 +257,8 @@
        target_apk_path_(target_apk_path),
        idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
 
-std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
-                                                     const StringPiece& idmap_data) {
+std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
+                                               const StringPiece& idmap_data) {
   ATRACE_CALL();
   size_t data_size = idmap_data.size();
   auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index 996b424..2a70f0d 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -767,10 +767,10 @@
   return true;
 }
 
-std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
-                                                   const size_t length,
-                                                   const LoadedIdmap* loaded_idmap,
-                                                   const package_property_t property_flags) {
+std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
+                                             const size_t length,
+                                             const LoadedIdmap* loaded_idmap,
+                                             const package_property_t property_flags) {
   ATRACE_NAME("LoadedArsc::Load");
 
   // Not using make_unique because the constructor is private.
@@ -799,11 +799,10 @@
     }
   }
 
-  // Need to force a move for mingw32.
-  return std::move(loaded_arsc);
+  return loaded_arsc;
 }
 
-std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() {
+std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
   return std::unique_ptr<LoadedArsc>(new LoadedArsc());
 }
 
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index e57490a..d0019ed 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -24,104 +24,49 @@
 #include "android-base/unique_fd.h"
 
 #include "androidfw/Asset.h"
+#include "androidfw/AssetsProvider.h"
 #include "androidfw/Idmap.h"
 #include "androidfw/LoadedArsc.h"
 #include "androidfw/misc.h"
 
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
 namespace android {
 
-class LoadedIdmap;
-
-// Interface for retrieving assets provided by an ApkAssets.
-class AssetsProvider {
- public:
-  virtual ~AssetsProvider() = default;
-
-  // Opens a file for reading.
-  std::unique_ptr<Asset> Open(const std::string& path,
-                              Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
-                              bool* file_exists = nullptr) const {
-    return OpenInternal(path, mode, file_exists);
-  }
-
-  // Iterate over all files and directories provided by the zip. The order of iteration is stable.
-  virtual bool ForEachFile(const std::string& /* path */,
-                           const std::function<void(const StringPiece&, FileType)>& /* f */) const {
-    return true;
-  }
-
- protected:
-  AssetsProvider() = default;
-
-  virtual std::unique_ptr<Asset> OpenInternal(const std::string& path,
-                                              Asset::AccessMode mode,
-                                              bool* file_exists) const = 0;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(AssetsProvider);
-};
-
-class ZipAssetsProvider;
-
 // Holds an APK.
 class ApkAssets {
  public:
-  // This means the data extends to the end of the file.
-  static constexpr off64_t kUnknownLength = -1;
 
-  // Creates an ApkAssets.
-  // If `system` is true, the package is marked as a system package, and allows some functions to
-  // filter out this package when computing what configurations/resources are available.
-  static std::unique_ptr<const ApkAssets> Load(
-      const std::string& path, package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+  // Creates an ApkAssets from a path on device.
+  static std::unique_ptr<ApkAssets> Load(const std::string& path,
+                                         package_property_t flags = 0U);
 
-  // Creates an ApkAssets from the given file descriptor, and takes ownership of the file
-  // descriptor. The `friendly_name` is some name that will be used to identify the source of
-  // this ApkAssets in log messages and other debug scenarios.
-  // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read
-  // using the `offset` into the file descriptor and will be `length` bytes long.
-  static std::unique_ptr<const ApkAssets> LoadFromFd(
-      base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
-      off64_t length = kUnknownLength);
+  // Creates an ApkAssets from an open file descriptor.
+  static std::unique_ptr<ApkAssets> LoadFromFd(base::unique_fd fd,
+                                               const std::string& debug_name,
+                                               package_property_t flags = 0U,
+                                               off64_t offset = 0,
+                                               off64_t len = AssetsProvider::kUnknownLength);
 
-  // Creates an ApkAssets from the given path which points to a resources.arsc.
-  static std::unique_ptr<const ApkAssets> LoadTable(
-      const std::string& path, package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr);
+  // Creates an ApkAssets from an AssetProvider.
+  // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed.
+  static std::unique_ptr<ApkAssets> Load(std::unique_ptr<AssetsProvider> assets,
+                                         package_property_t flags = 0U);
 
-  // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and
-  // takes ownership of the file descriptor.
-  // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read
-  // using the `offset` into the file descriptor and will be `length` bytes long.
-  static std::unique_ptr<const ApkAssets> LoadTableFromFd(
-      base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
-      off64_t length = kUnknownLength);
+  // Creates an ApkAssets from the given asset file representing a resources.arsc.
+  static std::unique_ptr<ApkAssets> LoadTable(std::unique_ptr<Asset> resources_asset,
+                                              std::unique_ptr<AssetsProvider> assets,
+                                              package_property_t flags = 0U);
 
   // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay
   // data.
-  static std::unique_ptr<const ApkAssets> LoadOverlay(const std::string& idmap_path,
-                                                      package_property_t flags = 0U);
+  static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path,
+                                                package_property_t flags = 0U);
 
-  // Creates an ApkAssets from the directory path. File-based resources are read within the
-  // directory as if the directory is an APK.
-  static std::unique_ptr<const ApkAssets> LoadFromDir(
-      const std::string& path, package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
-  // Creates a totally empty ApkAssets with no resources table and no file entries.
-  static std::unique_ptr<const ApkAssets> LoadEmpty(
-      package_property_t flags = 0U,
-      std::unique_ptr<const AssetsProvider> override_asset = nullptr);
-
-  const std::string& GetPath() const {
-    return path_;
-  }
+  // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same.
+  //  With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause
+  //  bugs when path is used for comparison because multiple ApkAssets could have the same "firendly
+  //  name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the
+  //  same asset should have the same pointer.
+  const std::string& GetPath() const;
 
   const AssetsProvider* GetAssetsProvider() const {
     return assets_provider_.get();
@@ -146,53 +91,40 @@
 
   // Returns whether the resources.arsc is allocated in RAM (not mmapped).
   bool IsTableAllocated() const {
-    return resources_asset_ && resources_asset_->isAllocated();
+    return resources_asset_ != nullptr && resources_asset_->isAllocated();
   }
 
   bool IsUpToDate() const;
 
-  // Creates an Asset from a file on disk.
-  static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
-
-  // Creates an Asset from a file descriptor.
-  //
-  // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
-  // must equal 0; otherwise, the asset data will be read using the `offset` into the file
-  // descriptor and will be `length` bytes long.
-  static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
-                                                  const char* path,
-                                                  off64_t offset = 0,
-                                                  off64_t length = kUnknownLength);
  private:
-  DISALLOW_COPY_AND_ASSIGN(ApkAssets);
+  static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<AssetsProvider> assets,
+                                             package_property_t property_flags,
+                                             std::unique_ptr<Asset> idmap_asset,
+                                             std::unique_ptr<LoadedIdmap> loaded_idmap);
 
-  static std::unique_ptr<const ApkAssets> LoadImpl(
-      std::unique_ptr<const AssetsProvider> assets, const std::string& path,
-      package_property_t property_flags,
-      std::unique_ptr<const AssetsProvider> override_assets = nullptr,
-      std::unique_ptr<Asset> idmap_asset = nullptr,
-      std::unique_ptr<const LoadedIdmap> idmap  = nullptr);
+  static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<Asset> resources_asset,
+                                             std::unique_ptr<AssetsProvider> assets,
+                                             package_property_t property_flags,
+                                             std::unique_ptr<Asset> idmap_asset,
+                                             std::unique_ptr<LoadedIdmap> loaded_idmap);
 
-  static std::unique_ptr<const ApkAssets> LoadTableImpl(
-      std::unique_ptr<Asset> resources_asset, const std::string& path,
-      package_property_t property_flags,
-      std::unique_ptr<const AssetsProvider> override_assets = nullptr);
+  ApkAssets(std::unique_ptr<Asset> resources_asset,
+            std::unique_ptr<LoadedArsc> loaded_arsc,
+            std::unique_ptr<AssetsProvider> assets,
+            package_property_t property_flags,
+            std::unique_ptr<Asset> idmap_asset,
+            std::unique_ptr<LoadedIdmap> loaded_idmap);
 
-  ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
-            std::string path,
-            time_t last_mod_time,
-            package_property_t property_flags);
-
-  std::unique_ptr<const AssetsProvider> assets_provider_;
-  const std::string path_;
-  time_t last_mod_time_;
-  package_property_t property_flags_ = 0U;
   std::unique_ptr<Asset> resources_asset_;
+  std::unique_ptr<LoadedArsc> loaded_arsc_;
+
+  std::unique_ptr<AssetsProvider> assets_provider_;
+  package_property_t property_flags_ = 0U;
+
   std::unique_ptr<Asset> idmap_asset_;
-  std::unique_ptr<const LoadedArsc> loaded_arsc_;
-  std::unique_ptr<const LoadedIdmap> loaded_idmap_;
+  std::unique_ptr<LoadedIdmap> loaded_idmap_;
 };
 
-}  // namespace android
+} // namespace android
 
-#endif /* APKASSETS_H_ */
+#endif // APKASSETS_H_
\ No newline at end of file
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 80bae20..40c91a6 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -167,8 +167,8 @@
 private:
     /* AssetManager needs access to our "create" functions */
     friend class AssetManager;
-    friend class ApkAssets;
-    friend class ZipAssetsProvider;
+    friend struct ZipAssetsProvider;
+    friend struct AssetsProvider;
 
     /*
      * Create the asset from a named file on disk.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
new file mode 100644
index 0000000..7b06947
--- /dev/null
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#ifndef ANDROIDFW_ASSETSPROVIDER_H
+#define ANDROIDFW_ASSETSPROVIDER_H
+
+#include <memory>
+#include <string>
+
+#include "android-base/macros.h"
+#include "android-base/unique_fd.h"
+
+#include "androidfw/Asset.h"
+#include "androidfw/Idmap.h"
+#include "androidfw/LoadedArsc.h"
+#include "androidfw/misc.h"
+
+struct ZipArchive;
+
+namespace android {
+
+// Interface responsible for opening and iterating through asset files.
+struct AssetsProvider {
+  static constexpr off64_t kUnknownLength = -1;
+
+  // Opens a file for reading. If `file_exists` is not null, it will be set to `true` if the file
+  // exists. This is useful for determining if the file exists but was unable to be opened due to
+  // an I/O error.
+  std::unique_ptr<Asset> Open(const std::string& path,
+                              Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM,
+                              bool* file_exists = nullptr) const;
+
+  // Iterate over all files and directories provided by the interface. The order of iteration is
+  // stable.
+  virtual bool ForEachFile(const std::string& path,
+                           const std::function<void(const StringPiece&, FileType)>& f) const = 0;
+
+  // Retrieves a name that represents the interface. This may or may not be the path of the
+  // interface source.
+  WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
+
+  // Returns whether the interface provides the most recent version of its files.
+  WARN_UNUSED virtual bool IsUpToDate() const = 0;
+
+  // Creates an Asset from a file on disk.
+  static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
+
+  // Creates an Asset from a file descriptor.
+  //
+  // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset
+  // must equal 0; otherwise, the asset data will be read using the `offset` into the file
+  // descriptor and will be `length` bytes long.
+  static std::unique_ptr<Asset> CreateAssetFromFd(base::unique_fd fd,
+                                                  const char* path,
+                                                  off64_t offset = 0,
+                                                  off64_t length = AssetsProvider::kUnknownLength);
+
+  virtual ~AssetsProvider() = default;
+ protected:
+  virtual std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+                                              bool* file_exists) const = 0;
+};
+
+// Supplies assets from a zip archive.
+struct ZipAssetsProvider : public AssetsProvider {
+  static std::unique_ptr<ZipAssetsProvider> Create(std::string path);
+  static std::unique_ptr<ZipAssetsProvider> Create(base::unique_fd fd,
+                                                   std::string friendly_name,
+                                                   off64_t offset = 0,
+                                                   off64_t len = kUnknownLength);
+
+  bool ForEachFile(const std::string& root_path,
+                   const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+  WARN_UNUSED const std::string& GetDebugName() const override;
+  WARN_UNUSED bool IsUpToDate() const override;
+
+  ~ZipAssetsProvider() override = default;
+ protected:
+  std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+                                      bool* file_exists) const override;
+
+ private:
+  struct PathOrDebugName;
+  ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time);
+
+  struct PathOrDebugName {
+    PathOrDebugName(std::string&& value, bool is_path);
+
+    // Retrieves the path or null if this class represents a debug name.
+    WARN_UNUSED const std::string* GetPath() const;
+
+    // Retrieves a name that represents the interface. This may or may not represent a path.
+    WARN_UNUSED const std::string& GetDebugName() const;
+
+   private:
+    std::string value_;
+    bool is_path_;
+  };
+
+  std::unique_ptr<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
+  PathOrDebugName name_;
+  time_t last_mod_time_;
+};
+
+// Supplies assets from a root directory.
+struct DirectoryAssetsProvider : public AssetsProvider {
+  static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
+
+  bool ForEachFile(const std::string& path,
+                   const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+  WARN_UNUSED const std::string& GetDebugName() const override;
+  WARN_UNUSED bool IsUpToDate() const override;
+
+  ~DirectoryAssetsProvider() override = default;
+ protected:
+  std::unique_ptr<Asset> OpenInternal(const std::string& path,
+                                      Asset::AccessMode mode,
+                                      bool* file_exists) const override;
+
+ private:
+  explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+  std::string dir_;
+  time_t last_mod_time_;
+};
+
+// Supplies assets from a `primary` asset provider and falls back to supplying assets from the
+// `secondary` asset provider if the asset cannot be found in the `primary`.
+struct MultiAssetsProvider : public AssetsProvider {
+  static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
+                                                std::unique_ptr<AssetsProvider>&& secondary);
+
+  bool ForEachFile(const std::string& root_path,
+                   const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+  WARN_UNUSED const std::string& GetDebugName() const override;
+  WARN_UNUSED bool IsUpToDate() const override;
+
+  ~MultiAssetsProvider() override = default;
+ protected:
+  std::unique_ptr<Asset> OpenInternal(
+      const std::string& path, Asset::AccessMode mode, bool* file_exists) const override;
+
+ private:
+  MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
+                      std::unique_ptr<AssetsProvider>&& secondary);
+
+  std::unique_ptr<AssetsProvider> primary_;
+  std::unique_ptr<AssetsProvider> secondary_;
+  std::string debug_name_;
+};
+
+// Does not provide any assets.
+struct EmptyAssetsProvider : public AssetsProvider {
+  static std::unique_ptr<AssetsProvider> Create();
+
+  bool ForEachFile(const std::string& path,
+                  const std::function<void(const StringPiece&, FileType)>& f) const override;
+
+  WARN_UNUSED const std::string& GetDebugName() const override;
+  WARN_UNUSED bool IsUpToDate() const override;
+
+  ~EmptyAssetsProvider() override = default;
+ protected:
+  std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
+                                      bool* file_exists) const override;
+};
+
+}  // namespace android
+
+#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index fd9a8d13..0ded793 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -149,8 +149,8 @@
 class LoadedIdmap {
  public:
   // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed.
-  static std::unique_ptr<const LoadedIdmap> Load(const StringPiece& idmap_path,
-                                                 const StringPiece& idmap_data);
+  static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
+                                           const StringPiece& idmap_data);
 
   // Returns the path to the IDMAP.
   std::string_view IdmapPath() const {
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index 891fb90..d9225cd 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -300,17 +300,14 @@
  public:
   // Load a resource table from memory pointed to by `data` of size `len`.
   // The lifetime of `data` must out-live the LoadedArsc returned from this method.
-  // If `system` is set to true, the LoadedArsc is considered as a system provided resource.
-  // If `load_as_shared_library` is set to true, the application package (0x7f) is treated
-  // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an
-  // ID.
-  static std::unique_ptr<const LoadedArsc> Load(incfs::map_ptr<void> data,
-                                                size_t length,
-                                                const LoadedIdmap* loaded_idmap = nullptr,
-                                                package_property_t property_flags = 0U);
+
+  static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> data,
+                                          size_t length,
+                                          const LoadedIdmap* loaded_idmap = nullptr,
+                                          package_property_t property_flags = 0U);
 
   // Create an empty LoadedArsc. This is used when an APK has no resources.arsc.
-  static std::unique_ptr<const LoadedArsc> CreateEmpty();
+  static std::unique_ptr<LoadedArsc> CreateEmpty();
 
   // Returns the string pool where all string resource values
   // (Res_value::dataType == Res_value::TYPE_STRING) are indexed.
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index 3f0c7cb..b434915 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -27,6 +27,8 @@
 #include "data/overlayable/R.h"
 #include "data/system/R.h"
 
+using ::testing::NotNull;
+
 namespace overlay = com::android::overlay;
 namespace overlayable = com::android::overlayable;
 
@@ -195,7 +197,11 @@
 }
 
 TEST_F(IdmapTest, OverlayLoaderInterop) {
-  auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER);
+  auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+  ASSERT_THAT(asset, NotNull());
+
+  auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(),
+      PROPERTY_LOADER);
   AssetManager2 asset_manager;
   asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(),
                               overlay_assets_.get()});
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index 9aa3634..f356c8130 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -339,10 +339,8 @@
 }
 
 TEST(LoadedArscTest, LoadCustomLoader) {
-  std::string contents;
-
-  std::unique_ptr<Asset>
-      asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+  auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
+  ASSERT_THAT(asset, NotNull());
 
   const StringPiece data(
       reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 255c15c..eedb996 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -316,6 +316,15 @@
      * Not guaranteed to be supported by devices, may be emulated if not supported. */
     public static final int ENCODING_PCM_32BIT = 22;
 
+    /** Audio data format: MPEG-H baseline profile, level 3 */
+    public static final int ENCODING_MPEGH_BL_L3 = 23;
+    /** Audio data format: MPEG-H baseline profile, level 4 */
+    public static final int ENCODING_MPEGH_BL_L4 = 24;
+    /** Audio data format: MPEG-H low complexity profile, level 3 */
+    public static final int ENCODING_MPEGH_LC_L3 = 25;
+    /** Audio data format: MPEG-H low complexity profile, level 4 */
+    public static final int ENCODING_MPEGH_LC_L4 = 26;
+
     /** @hide */
     public static String toLogFriendlyEncoding(int enc) {
         switch(enc) {
@@ -363,6 +372,14 @@
                 return "ENCODING_PCM_24BIT_PACKED";
             case ENCODING_PCM_32BIT:
                 return "ENCODING_PCM_32BIT";
+            case ENCODING_MPEGH_BL_L3:
+                return "ENCODING_MPEGH_BL_L3";
+            case ENCODING_MPEGH_BL_L4:
+                return "ENCODING_MPEGH_BL_L4";
+            case ENCODING_MPEGH_LC_L3:
+                return "ENCODING_MPEGH_LC_L3";
+            case ENCODING_MPEGH_LC_L4:
+                return "ENCODING_MPEGH_LC_L4";
             default :
                 return "invalid encoding " + enc;
         }
@@ -594,6 +611,10 @@
             case ENCODING_OPUS:
             case ENCODING_PCM_24BIT_PACKED:
             case ENCODING_PCM_32BIT:
+            case ENCODING_MPEGH_BL_L3:
+            case ENCODING_MPEGH_BL_L4:
+            case ENCODING_MPEGH_LC_L3:
+            case ENCODING_MPEGH_LC_L4:
                 return true;
             default:
                 return false;
@@ -625,6 +646,10 @@
             case ENCODING_OPUS:
             case ENCODING_PCM_24BIT_PACKED:
             case ENCODING_PCM_32BIT:
+            case ENCODING_MPEGH_BL_L3:
+            case ENCODING_MPEGH_BL_L4:
+            case ENCODING_MPEGH_LC_L3:
+            case ENCODING_MPEGH_LC_L4:
                 return true;
             default:
                 return false;
@@ -659,6 +684,10 @@
             case ENCODING_E_AC3_JOC:
             case ENCODING_DOLBY_MAT:
             case ENCODING_OPUS:
+            case ENCODING_MPEGH_BL_L3:
+            case ENCODING_MPEGH_BL_L4:
+            case ENCODING_MPEGH_LC_L3:
+            case ENCODING_MPEGH_LC_L4:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -693,6 +722,10 @@
             case ENCODING_E_AC3_JOC:
             case ENCODING_DOLBY_MAT:
             case ENCODING_OPUS:
+            case ENCODING_MPEGH_BL_L3:
+            case ENCODING_MPEGH_BL_L4:
+            case ENCODING_MPEGH_LC_L3:
+            case ENCODING_MPEGH_LC_L4:
                 return false;
             case ENCODING_INVALID:
             default:
@@ -975,6 +1008,10 @@
                 case ENCODING_OPUS:
                 case ENCODING_PCM_24BIT_PACKED:
                 case ENCODING_PCM_32BIT:
+                case ENCODING_MPEGH_BL_L3:
+                case ENCODING_MPEGH_BL_L4:
+                case ENCODING_MPEGH_LC_L3:
+                case ENCODING_MPEGH_LC_L4:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -1197,7 +1234,11 @@
         ENCODING_DOLBY_MAT,
         ENCODING_OPUS,
         ENCODING_PCM_24BIT_PACKED,
-        ENCODING_PCM_32BIT }
+        ENCODING_PCM_32BIT,
+        ENCODING_MPEGH_BL_L3,
+        ENCODING_MPEGH_BL_L4,
+        ENCODING_MPEGH_LC_L3,
+        ENCODING_MPEGH_LC_L4 }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
@@ -1213,6 +1254,10 @@
             ENCODING_AC4,
             ENCODING_E_AC3_JOC,
             ENCODING_DOLBY_MAT,
+            ENCODING_MPEGH_BL_L3,
+            ENCODING_MPEGH_BL_L4,
+            ENCODING_MPEGH_LC_L3,
+            ENCODING_MPEGH_LC_L4,
     };
 
     /** @hide */
@@ -1225,7 +1270,11 @@
             ENCODING_DOLBY_TRUEHD,
             ENCODING_AC4,
             ENCODING_E_AC3_JOC,
-            ENCODING_DOLBY_MAT }
+            ENCODING_DOLBY_MAT,
+            ENCODING_MPEGH_BL_L3,
+            ENCODING_MPEGH_BL_L4,
+            ENCODING_MPEGH_LC_L3,
+            ENCODING_MPEGH_LC_L4 }
     )
     @Retention(RetentionPolicy.SOURCE)
     public @interface SurroundSoundEncoding {}
@@ -1259,6 +1308,14 @@
                 return "Dolby Atmos in Dolby Digital Plus";
             case ENCODING_DOLBY_MAT:
                 return "Dolby MAT";
+            case ENCODING_MPEGH_BL_L3:
+                return "MPEG-H 3D Audio baseline profile level 3";
+            case ENCODING_MPEGH_BL_L4:
+                return "MPEG-H 3D Audio baseline profile level 4";
+            case ENCODING_MPEGH_LC_L3:
+                return "MPEG-H 3D Audio low complexity profile level 3";
+            case ENCODING_MPEGH_LC_L4:
+                return "MPEG-H 3D Audio low complexity profile level 4";
             default:
                 return "Unknown surround sound format";
         }
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 65e75cd..d411831 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -83,9 +83,9 @@
     <string name="untrusted_external_source_warning" product="tablet" msgid="6539403649459942547">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak tableta honetan."</string>
     <string name="untrusted_external_source_warning" product="tv" msgid="1206648674551321364">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan."</string>
     <string name="untrusted_external_source_warning" product="default" msgid="7279739265754475165">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan."</string>
-    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
-    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
-    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+    <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+    <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
+    <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartuko duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string>
     <string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string>
     <string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string>
     <string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 5563003..7556ace 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1484,5 +1484,5 @@
     <!-- Content description of the Ethernet connection when disconnected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ethernet_disconnected">Ethernet disconnected.</string>
     <!-- Content description of the Ethernet connection when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
-    <string name="accessibility_ethernet_connected">Ethernet connected.</string>
+    <string name="accessibility_ethernet_connected">Ethernet.</string>
 </resources>
diff --git a/packages/SystemUI/res/xml/people_space_widget_info.xml b/packages/SystemUI/res/xml/people_space_widget_info.xml
index 10e28c4..d0c63a8 100644
--- a/packages/SystemUI/res/xml/people_space_widget_info.xml
+++ b/packages/SystemUI/res/xml/people_space_widget_info.xml
@@ -15,8 +15,10 @@
   -->
 
 <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
-    android:minWidth="180dp"
+    android:minWidth="140dp"
     android:minHeight="40dp"
+    android:minResizeWidth="110dp"
+    android:minResizeHeight="40dp"
     android:updatePeriodMillis="60000"
     android:previewImage="@drawable/ic_android"
     android:resizeMode="horizontal|vertical"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index 7773fe9..75ef4b32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -128,6 +128,8 @@
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
+            if (child.getVisibility() != View.VISIBLE) continue;
+
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
 
             if (lp.maxWidth > 0 && lp.maxWidth < maxWidth) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b2ad4c..191b85b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -54,6 +54,9 @@
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 import com.android.systemui.statusbar.policy.WifiIcons;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 import javax.inject.Inject;
 
 /** Quick settings tile: Internet **/
@@ -65,7 +68,7 @@
     protected final NetworkController mController;
     private final DataUsageController mDataController;
     private final QSTile.SignalState mStateBeforeClick = newTileState();
-    // The last updated tile state, 0: mobile, 1: wifi
+    // The last updated tile state, 0: mobile, 1: wifi, 2: ethernet.
     private int mLastTileState = -1;
 
     protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
@@ -140,6 +143,21 @@
         return string;
     }
 
+    private static final class EthernetCallbackInfo {
+        boolean mConnected;
+        int mEthernetSignalIconId;
+        String mEthernetContentDescription;
+
+        @Override
+        public String toString() {
+            return new StringBuilder("EthernetCallbackInfo[")
+                    .append("mConnected=").append(mConnected)
+                    .append(",mEthernetSignalIconId=").append(mEthernetSignalIconId)
+                    .append(",mEthernetContentDescription=").append(mEthernetContentDescription)
+                    .append(']').toString();
+        }
+    }
+
     private static final class WifiCallbackInfo {
         boolean mAirplaneModeEnabled;
         boolean mEnabled;
@@ -212,6 +230,8 @@
     protected final class InternetSignalCallback implements SignalCallback {
         final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
         final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+        final EthernetCallbackInfo mEthernetInfo = new EthernetCallbackInfo();
+
 
         @Override
         public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
@@ -230,14 +250,12 @@
             }
             // When airplane mode is enabled, we need to refresh the Internet Tile even if the WiFi
             // is not the default network.
-            if (qsIcon == null && !mWifiInfo.mAirplaneModeEnabled) {
+            if (qsIcon == null) {
                 return;
             }
-            if (qsIcon != null) {
-                mWifiInfo.mConnected = qsIcon.visible;
-                mWifiInfo.mWifiSignalIconId = qsIcon.icon;
-                mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
-            }
+            mWifiInfo.mConnected = qsIcon.visible;
+            mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+            mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
             mWifiInfo.mEnabled = enabled;
             mWifiInfo.mSsid = description;
             mWifiInfo.mActivityIn = activityIn;
@@ -287,6 +305,20 @@
         }
 
         @Override
+        public void setEthernetIndicators(IconState icon) {
+            if (DEBUG) {
+                Log.d(TAG, "setEthernetIndicators: "
+                        + "icon = " + (icon == null ? "" :  icon.toString()));
+            }
+            mEthernetInfo.mConnected = icon.visible;
+            mEthernetInfo.mEthernetSignalIconId = icon.icon;
+            mEthernetInfo.mEthernetContentDescription = icon.contentDescription;
+            if (icon.visible) {
+                refreshState(mEthernetInfo);
+            }
+        }
+
+        @Override
         public void setNoSims(boolean show, boolean simDetected) {
             if (DEBUG) {
                 Log.d(TAG, "setNoSims: "
@@ -299,7 +331,6 @@
                 mCellularInfo.mMobileSignalIconId = 0;
                 mCellularInfo.mQsTypeIcon = 0;
             }
-            refreshState(mCellularInfo);
         }
 
         @Override
@@ -310,7 +341,9 @@
             }
             mCellularInfo.mAirplaneModeEnabled = icon.visible;
             mWifiInfo.mAirplaneModeEnabled = icon.visible;
-            refreshState(mCellularInfo);
+            if (!mSignalCallback.mEthernetInfo.mConnected) {
+                refreshState(mCellularInfo);
+            }
         }
 
         @Override
@@ -330,6 +363,15 @@
             mWifiInfo.mNoNetworksAvailable = noNetworksAvailable;
             refreshState(mWifiInfo);
         }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("InternetSignalCallback[")
+                .append("mWifiInfo=").append(mWifiInfo)
+                .append(",mCellularInfo=").append(mCellularInfo)
+                .append(",mEthernetInfo=").append(mEthernetInfo)
+                .append(']').toString();
+        }
     }
 
     @Override
@@ -340,6 +382,9 @@
         } else if (arg instanceof WifiCallbackInfo) {
             mLastTileState = 1;
             handleUpdateWifiState(state, arg);
+        } else if (arg instanceof EthernetCallbackInfo) {
+            mLastTileState = 2;
+            handleUpdateEthernetState(state, arg);
         } else {
             // handleUpdateState will be triggered when user expands the QuickSetting panel with
             // arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
@@ -348,6 +393,8 @@
                 handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
             } else if (mLastTileState == 1) {
                 handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+            } else if (mLastTileState == 2) {
+                handleUpdateEthernetState(state, mSignalCallback.mEthernetInfo);
             }
         }
     }
@@ -440,7 +487,6 @@
             Log.d(TAG, "handleUpdateCellularState: " + "CellularCallbackInfo = " + cb.toString());
         }
         final Resources r = mContext.getResources();
-        // TODO(b/174753536): Use the new "Internet" string as state.label once available.
         state.label = r.getString(R.string.quick_settings_internet_label);
         state.state = Tile.STATE_ACTIVE;
         boolean mobileDataEnabled = mDataController.isMobileDataSupported()
@@ -478,6 +524,18 @@
         }
     }
 
+    private void handleUpdateEthernetState(SignalState state, Object arg) {
+        EthernetCallbackInfo cb = (EthernetCallbackInfo) arg;
+        if (DEBUG) {
+            Log.d(TAG, "handleUpdateEthernetState: " + "EthernetCallbackInfo = " + cb.toString());
+        }
+        final Resources r = mContext.getResources();
+        state.label = r.getString(R.string.quick_settings_internet_label);
+        state.state = Tile.STATE_ACTIVE;
+        state.icon = ResourceIcon.get(cb.mEthernetSignalIconId);
+        state.secondaryLabel = cb.mEthernetContentDescription;
+    }
+
     private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
         if (TextUtils.isEmpty(dataType)) {
             return Html.fromHtml((current == null ? "" : current.toString()), 0);
@@ -519,4 +577,15 @@
             return d;
         }
     }
+
+    /**
+     * Dumps the state of this tile along with its name.
+     */
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(this.getClass().getSimpleName() + ":");
+        pw.print("    "); pw.println(getState().toString());
+        pw.print("    "); pw.println("mLastTileState=" + mLastTileState);
+        pw.print("    "); pw.println("mSignalCallback=" + mSignalCallback.toString());
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 0a7eea4..953b40b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -388,12 +388,6 @@
             }
         });
 
-        // ignore system bar insets for the purpose of window layout
-        mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
-                new WindowInsets.Builder(insets)
-                        .setInsets(WindowInsets.Type.all(), Insets.NONE)
-                        .build()));
-
         // TODO(159460485): Remove this when focus is handled properly in the system
         mScreenshotView.setOnTouchListener((v, event) -> {
             if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
@@ -533,9 +527,6 @@
 
 
         attachWindow();
-        if (DEBUG_WINDOW) {
-            Log.d(TAG, "setContentView: " + mScreenshotView);
-        }
         mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
                 new ViewTreeObserver.OnPreDrawListener() {
                     @Override
@@ -549,7 +540,13 @@
                     }
                 });
         mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+        if (DEBUG_WINDOW) {
+            Log.d(TAG, "setContentView: " + mScreenshotView);
+        }
         setContentView(mScreenshotView);
+        // ignore system bar insets for the purpose of window layout
+        mWindow.getDecorView().setOnApplyWindowInsetsListener(
+                (v, insets) -> WindowInsets.CONSUMED);
         cancelTimeout(); // restarted after animation
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
index 2eff04e..80b75a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
@@ -53,13 +53,21 @@
     public void notifyListeners(SignalCallback callback) {
         boolean ethernetVisible = mCurrentState.connected;
         String contentDescription = getTextIfExists(getContentDescription()).toString();
-
         // TODO: wire up data transfer using WifiSignalPoller.
         callback.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(),
                 contentDescription));
     }
 
     @Override
+    public int getContentDescription() {
+        if (mCurrentState.connected) {
+            return getIcons().contentDesc[1];
+        } else {
+            return getIcons().discContentDesc;
+        }
+    }
+
+    @Override
     public State cleanState() {
         return new State();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f9450ae..e13e30b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -524,6 +524,10 @@
         return mWifiSignalController.isCarrierMergedWifi(subId);
     }
 
+    boolean isEthernetDefault() {
+        return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+    }
+
     String getNonDefaultMobileDataNetworkName(int subId) {
         MobileSignalController controller = getControllerWithSubId(subId);
         return controller != null ? controller.getNonDefaultCarrierName() : "";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 9669522..b2120d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -106,7 +106,8 @@
         IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription);
         if (mProviderModel) {
             IconState qsIcon = null;
-            if (mCurrentState.isDefault) {
+            if (mCurrentState.isDefault || (!mNetworkController.isRadioOn()
+                    && !mNetworkController.isEthernetDefault())) {
                 qsIcon = new IconState(mCurrentState.connected,
                         mWifiTracker.isCaptivePortal ? R.drawable.ic_qs_wifi_disconnected
                                 : getQsCurrentIconId(), contentDescription);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
index 7824fd9..9c54100 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTapAndHold.java
@@ -44,7 +44,7 @@
 
     @Override
     protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        if (mCompletedTapCount + 1 == mTargetFingerCount) {
+        if (mCompletedTapCount + 1 == mTargetTapCount) {
             // Calling super.onUp  would complete the multi-tap version of this.
             cancelGesture(event, rawEvent, policyFlags);
         } else {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6216fc0..7da4d64 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -671,34 +671,29 @@
         }
 
         if (fgRequired) {
-            if (isFgsBgStart(r.mAllowStartForeground)) {
-                if (!r.mLoggedInfoAllowStartForeground) {
-                    Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground);
-                    r.mLoggedInfoAllowStartForeground = true;
+            logFgsBackgroundStart(r);
+            if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
+                String msg = "startForegroundService() not allowed due to "
+                        + "mAllowStartForeground false: service "
+                        + r.shortInstanceName;
+                Slog.w(TAG, msg);
+                showFgsBgRestrictedNotificationLocked(r);
+                ApplicationInfo aInfo = null;
+                try {
+                    aInfo = AppGlobals.getPackageManager().getApplicationInfo(
+                            callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
+                            userId);
+                } catch (android.os.RemoteException e) {
+                    // pm is in same process, this will never happen.
                 }
-                if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) {
-                    String msg = "startForegroundService() not allowed due to "
-                            + "mAllowStartForeground false: service "
-                            + r.shortInstanceName;
-                    Slog.w(TAG, msg);
-                    showFgsBgRestrictedNotificationLocked(r);
-                    ApplicationInfo aInfo = null;
-                    try {
-                        aInfo = AppGlobals.getPackageManager().getApplicationInfo(
-                                callingPackage, ActivityManagerService.STOCK_PM_FLAGS,
-                                userId);
-                    } catch (android.os.RemoteException e) {
-                        // pm is in same process, this will never happen.
-                    }
-                    if (aInfo == null) {
-                        throw new SecurityException("startServiceLocked failed, "
-                                + "could not resolve client package " + callingPackage);
-                    }
-                    if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
-                        throw new IllegalStateException(msg);
-                    }
-                    return null;
+                if (aInfo == null) {
+                    throw new SecurityException("startServiceLocked failed, "
+                            + "could not resolve client package " + callingPackage);
                 }
+                if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) {
+                    throw new IllegalStateException(msg);
+                }
+                return null;
             }
         }
 
@@ -1768,25 +1763,19 @@
                 }
 
                 if (!ignoreForeground) {
-                    if (isFgsBgStart(r.mAllowStartForeground)) {
-                        if (!r.mLoggedInfoAllowStartForeground) {
-                            Slog.wtf(TAG, "Background started FGS "
-                                    + r.mInfoAllowStartForeground);
-                            r.mLoggedInfoAllowStartForeground = true;
-                        }
-                        if (r.mAllowStartForeground == FGS_FEATURE_DENIED
-                                && isBgFgsRestrictionEnabled(r)) {
-                            final String msg = "Service.startForeground() not allowed due to "
-                                    + "mAllowStartForeground false: service "
-                                    + r.shortInstanceName;
-                            Slog.w(TAG, msg);
-                            showFgsBgRestrictedNotificationLocked(r);
-                            updateServiceForegroundLocked(r.app, true);
-                            ignoreForeground = true;
-                            if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
-                                    r.appInfo.uid)) {
-                                throw new IllegalStateException(msg);
-                            }
+                    logFgsBackgroundStart(r);
+                    if (r.mAllowStartForeground == FGS_FEATURE_DENIED
+                            && isBgFgsRestrictionEnabled(r)) {
+                        final String msg = "Service.startForeground() not allowed due to "
+                                + "mAllowStartForeground false: service "
+                                + r.shortInstanceName;
+                        Slog.w(TAG, msg);
+                        showFgsBgRestrictedNotificationLocked(r);
+                        updateServiceForegroundLocked(r.app, true);
+                        ignoreForeground = true;
+                        if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
+                                r.appInfo.uid)) {
+                            throw new IllegalStateException(msg);
                         }
                     }
                 }
@@ -5732,4 +5721,23 @@
         return mAm.mConstants.mFlagFgsStartRestrictionEnabled
                 && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
     }
+
+    private void logFgsBackgroundStart(ServiceRecord r) {
+        // Only log if FGS is started from background.
+        if (!isFgsBgStart(r.mAllowStartForeground)) {
+            return;
+        }
+        if (!r.mLoggedInfoAllowStartForeground) {
+            final String msg = "Background started FGS: "
+                    + ((r.mAllowStartForeground != FGS_FEATURE_DENIED) ? "Allowed " : "Disallowed ")
+                    + r.mInfoAllowStartForeground;
+            Slog.wtfQuiet(TAG, msg);
+            if (r.mAllowStartForeground != FGS_FEATURE_DENIED) {
+                Slog.i(TAG, msg);
+            } else {
+                Slog.w(TAG, msg);
+            }
+            r.mLoggedInfoAllowStartForeground = true;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c8f5f8e..cdf5c98 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5909,18 +5909,10 @@
                 abiOverride, zygotePolicyFlags);
     }
 
-    @GuardedBy("this")
-    final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
-            boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
-        return addAppLocked(info, customProcess, isolated, disableHiddenApiChecks,
-                false /* disableTestApiChecks */, abiOverride, zygotePolicyFlags);
-    }
-
     // TODO: Move to ProcessList?
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
-            boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            String abiOverride, int zygotePolicyFlags) {
+            boolean disableHiddenApiChecks, String abiOverride, int zygotePolicyFlags) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -5955,7 +5947,7 @@
             mPersistentStartingProcesses.add(app);
             mProcessList.startProcessLocked(app, new HostingRecord("added application",
                     customProcess != null ? customProcess : app.processName),
-                    zygotePolicyFlags, disableHiddenApiChecks, disableTestApiChecks, abiOverride);
+                    zygotePolicyFlags, disableHiddenApiChecks, abiOverride);
         }
 
         return app;
@@ -13349,11 +13341,12 @@
                     || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
             boolean disableTestApiChecks = disableHiddenApiChecks
                     || (flags & INSTR_FLAG_DISABLE_TEST_API_CHECKS) != 0;
+
             if (disableHiddenApiChecks || disableTestApiChecks) {
                 enforceCallingPermission(android.Manifest.permission.DISABLE_HIDDEN_API_CHECKS,
                         "disable hidden API checks");
 
-                enableTestApiAccess(ii.packageName);
+                enableTestApiAccess(ai.packageName);
             }
 
             final long origId = Binder.clearCallingIdentity();
@@ -13370,11 +13363,10 @@
                     mUsageStatsService.reportEvent(ii.targetPackage, userId,
                             UsageEvents.Event.SYSTEM_INTERACTION);
                 }
-                app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
-                        disableTestApiChecks, abiOverride, ZYGOTE_POLICY_FLAG_EMPTY);
+                app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks, abiOverride,
+                        ZYGOTE_POLICY_FLAG_EMPTY);
             }
 
-
             app.setActiveInstrumentation(activeInstr);
             activeInstr.mFinished = false;
             activeInstr.mSourceUid = callingUid;
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bbf927b..8f99459 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1767,8 +1767,7 @@
      */
     @GuardedBy("mService")
     boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
-            int zygotePolicyFlags, boolean disableHiddenApiChecks, boolean disableTestApiChecks,
-            String abiOverride) {
+            int zygotePolicyFlags, boolean disableHiddenApiChecks, String abiOverride) {
         if (app.pendingStart) {
             return true;
         }
@@ -1914,10 +1913,6 @@
                     throw new IllegalStateException("Invalid API policy: " + policy);
                 }
                 runtimeFlags |= policyBits;
-
-                if (disableTestApiChecks) {
-                    runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
-                }
             }
 
             String useAppImageCache = SystemProperties.get(
@@ -2367,8 +2362,7 @@
     final boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
             int zygotePolicyFlags, String abiOverride) {
         return startProcessLocked(app, hostingRecord, zygotePolicyFlags,
-                false /* disableHiddenApiChecks */, false /* disableTestApiChecks */,
-                abiOverride);
+                false /* disableHiddenApiChecks */, abiOverride);
     }
 
     @GuardedBy("mService")
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 8e5215b..9036812 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -26,7 +26,6 @@
 import android.graphics.fonts.FontManager;
 import android.graphics.fonts.SystemFonts;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SharedMemory;
 import android.os.ShellCallback;
@@ -67,13 +66,12 @@
     private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";
 
     @Override
-    public FontConfig getFontConfig() throws RemoteException {
+    public FontConfig getFontConfig() {
         return getSystemFontConfig();
     }
 
     @Override
-    public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion)
-            throws RemoteException {
+    public int updateFont(ParcelFileDescriptor fd, byte[] signature, int baseVersion) {
         Objects.requireNonNull(fd);
         Objects.requireNonNull(signature);
         Preconditions.checkArgumentNonnegative(baseVersion);
@@ -183,14 +181,21 @@
     @NonNull
     private final Context mContext;
 
-    @GuardedBy("FontManagerService.this")
+    private final Object mUpdatableFontDirLock = new Object();
+
+    @GuardedBy("mUpdatableFontDirLock")
     @NonNull
     private final FontCrashDetector mFontCrashDetector;
 
+    @GuardedBy("mUpdatableFontDirLock")
     @Nullable
     private final UpdatableFontDir mUpdatableFontDir;
 
-    @GuardedBy("FontManagerService.this")
+    // mSerializedFontMapLock can be acquired while holding mUpdatableFontDirLock.
+    // mUpdatableFontDirLock should not be newly acquired while holding mSerializedFontMapLock.
+    private final Object mSerializedFontMapLock = new Object();
+
+    @GuardedBy("mSerializedFontMapLock")
     @Nullable
     private SharedMemory mSerializedFontMap = null;
 
@@ -212,9 +217,9 @@
     }
 
     private void initialize() {
-        synchronized (FontManagerService.this) {
+        synchronized (mUpdatableFontDirLock) {
             if (mUpdatableFontDir == null) {
-                mSerializedFontMap = buildNewSerializedFontMap();
+                updateSerializedFontMap();
                 return;
             }
             if (mFontCrashDetector.hasCrashed()) {
@@ -228,7 +233,7 @@
             }
             try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
                 mUpdatableFontDir.loadFontFileMap();
-                mSerializedFontMap = buildNewSerializedFontMap();
+                updateSerializedFontMap();
             }
         }
     }
@@ -239,7 +244,7 @@
     }
 
     @Nullable /* package */ SharedMemory getCurrentFontMap() {
-        synchronized (FontManagerService.this) {
+        synchronized (mSerializedFontMapLock) {
             return mSerializedFontMap;
         }
     }
@@ -251,7 +256,7 @@
                     FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
                     "The font updater is disabled.");
         }
-        synchronized (FontManagerService.this) {
+        synchronized (mUpdatableFontDirLock) {
             // baseVersion == -1 only happens from shell command. This is filtered and treated as
             // error from SystemApi call.
             if (baseVersion != -1 && mUpdatableFontDir.getConfigVersion() != baseVersion) {
@@ -261,7 +266,7 @@
             }
             try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
                 mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
-                mSerializedFontMap = buildNewSerializedFontMap();
+                updateSerializedFontMap();
             }
         }
     }
@@ -272,10 +277,10 @@
                     FontManager.RESULT_ERROR_FONT_UPDATER_DISABLED,
                     "The font updater is disabled.");
         }
-        synchronized (FontManagerService.this) {
+        synchronized (mUpdatableFontDirLock) {
             try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
                 mUpdatableFontDir.clearUpdates();
-                mSerializedFontMap = buildNewSerializedFontMap();
+                updateSerializedFontMap();
             }
         }
     }
@@ -283,7 +288,8 @@
     /* package */ Map<String, File> getFontFileMap() {
         if (mUpdatableFontDir == null) {
             return Collections.emptyMap();
-        } else {
+        }
+        synchronized (mUpdatableFontDirLock) {
             return mUpdatableFontDir.getFontFileMap();
         }
     }
@@ -301,7 +307,7 @@
             @Nullable FileDescriptor err,
             @NonNull String[] args,
             @Nullable ShellCallback callback,
-            @NonNull ResultReceiver result) throws RemoteException {
+            @NonNull ResultReceiver result) {
         new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
     }
 
@@ -309,24 +315,28 @@
      * Returns an active system font configuration.
      */
     public @NonNull FontConfig getSystemFontConfig() {
-        if (mUpdatableFontDir != null) {
-            return mUpdatableFontDir.getSystemFontConfig();
-        } else {
+        if (mUpdatableFontDir == null) {
             return SystemFonts.getSystemPreinstalledFontConfig();
         }
+        synchronized (mUpdatableFontDirLock) {
+            return mUpdatableFontDir.getSystemFontConfig();
+        }
     }
 
     /**
-     * Make new serialized font map data.
+     * Makes new serialized font map data and updates mSerializedFontMap.
      */
-    public @Nullable SharedMemory buildNewSerializedFontMap() {
+    public void updateSerializedFontMap() {
         try {
             final FontConfig fontConfig = getSystemFontConfig();
             final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
             final Map<String, Typeface> typefaceMap =
                     SystemFonts.buildSystemTypefaces(fontConfig, fallback);
 
-            return Typeface.serializeFontMap(typefaceMap);
+            SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+            synchronized (mSerializedFontMapLock) {
+                mSerializedFontMap = serializeFontMap;
+            }
         } catch (IOException | ErrnoException e) {
             Slog.w(TAG, "Failed to serialize updatable font map. "
                     + "Retrying with system image fonts.", e);
@@ -338,11 +348,13 @@
             final Map<String, Typeface> typefaceMap =
                     SystemFonts.buildSystemTypefaces(fontConfig, fallback);
 
-            return Typeface.serializeFontMap(typefaceMap);
+            SharedMemory serializeFontMap = Typeface.serializeFontMap(typefaceMap);
+            synchronized (mSerializedFontMapLock) {
+                mSerializedFontMap = serializeFontMap;
+            }
         } catch (IOException | ErrnoException e) {
             Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
         }
-        return null;
     }
 
 }
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 0cb7045..8fac086 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -29,8 +29,6 @@
 import android.util.Base64;
 import android.util.Slog;
 
-import com.android.internal.annotations.GuardedBy;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
@@ -44,6 +42,11 @@
 import java.util.List;
 import java.util.Map;
 
+/**
+ * Manages set of updatable font files.
+ *
+ * <p>This class is not thread safe.
+ */
 final class UpdatableFontDir {
 
     private static final String TAG = "UpdatableFontDir";
@@ -113,11 +116,9 @@
     private final File mConfigFile;
     private final File mTmpConfigFile;
 
-    @GuardedBy("UpdatableFontDir.this")
     private final PersistentSystemFontConfig.Config mConfig =
             new PersistentSystemFontConfig.Config();
 
-    @GuardedBy("UpdatableFontDir.this")
     private int mConfigVersion = 1;
 
     /**
@@ -125,7 +126,6 @@
      * FontFileInfo}. All files in this map are validated, and have higher revision numbers than
      * corresponding font files in {@link #mPreinstalledFontDirs}.
      */
-    @GuardedBy("UpdatableFontDir.this")
     private final Map<String, FontFileInfo> mFontFileInfoMap = new HashMap<>();
 
     UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
@@ -145,57 +145,53 @@
     }
 
     /* package */ void loadFontFileMap() {
-        synchronized (UpdatableFontDir.this) {
-            boolean success = false;
+        boolean success = false;
 
-            try (FileInputStream fis = new FileInputStream(mConfigFile)) {
-                PersistentSystemFontConfig.loadFromXml(fis, mConfig);
-            } catch (IOException | XmlPullParserException e) {
-                mConfig.reset();
+        try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+            PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+        } catch (IOException | XmlPullParserException e) {
+            mConfig.reset();
+        }
+
+        mFontFileInfoMap.clear();
+        try {
+            File[] dirs = mFilesDir.listFiles();
+            if (dirs == null) return;
+            for (File dir : dirs) {
+                if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
+                File[] files = dir.listFiles();
+                if (files == null || files.length != 1) return;
+                FontFileInfo fontFileInfo = validateFontFile(files[0]);
+                addFileToMapIfNewer(fontFileInfo, true /* deleteOldFile */);
             }
-
-            mFontFileInfoMap.clear();
-            try {
-                File[] dirs = mFilesDir.listFiles();
-                if (dirs == null) return;
-                for (File dir : dirs) {
-                    if (!dir.getName().startsWith(RANDOM_DIR_PREFIX)) return;
-                    File[] files = dir.listFiles();
-                    if (files == null || files.length != 1) return;
-                    FontFileInfo fontFileInfo = validateFontFile(files[0]);
-                    addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
-                }
-                success = true;
-            } catch (Throwable t) {
-                // If something happened during loading system fonts, clear all contents in finally
-                // block. Here, just dumping errors.
-                Slog.e(TAG, "Failed to load font mappings.", t);
-            } finally {
-                // Delete all files just in case if we find a problematic file.
-                if (!success) {
-                    mFontFileInfoMap.clear();
-                    FileUtils.deleteContents(mFilesDir);
-                }
+            success = true;
+        } catch (Throwable t) {
+            // If something happened during loading system fonts, clear all contents in finally
+            // block. Here, just dumping errors.
+            Slog.e(TAG, "Failed to load font mappings.", t);
+        } finally {
+            // Delete all files just in case if we find a problematic file.
+            if (!success) {
+                mFontFileInfoMap.clear();
+                FileUtils.deleteContents(mFilesDir);
             }
         }
     }
 
     /* package */ void clearUpdates() throws SystemFontException {
-        synchronized (UpdatableFontDir.this) {
-            mFontFileInfoMap.clear();
-            FileUtils.deleteContents(mFilesDir);
+        mFontFileInfoMap.clear();
+        FileUtils.deleteContents(mFilesDir);
 
-            mConfig.reset();
-            mConfig.lastModifiedDate = Instant.now().getEpochSecond();
-            try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
-                PersistentSystemFontConfig.writeToXml(fos, mConfig);
-            } catch (Exception e) {
-                throw new SystemFontException(
-                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
-                        "Failed to write config XML.", e);
-            }
-            mConfigVersion++;
+        mConfig.reset();
+        mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+        try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+            PersistentSystemFontConfig.writeToXml(fos, mConfig);
+        } catch (Exception e) {
+            throw new SystemFontException(
+                    FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+                    "Failed to write config XML.", e);
         }
+        mConfigVersion++;
     }
 
     /**
@@ -210,110 +206,108 @@
      * @throws SystemFontException if error occurs.
      */
     void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
-        synchronized (UpdatableFontDir.this) {
-            File newDir = getRandomDir(mFilesDir);
-            if (!newDir.mkdir()) {
+        File newDir = getRandomDir(mFilesDir);
+        if (!newDir.mkdir()) {
+            throw new SystemFontException(
+                    FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+                    "Failed to create font directory.");
+        }
+        try {
+            // Make newDir executable so that apps can access font file inside newDir.
+            Os.chmod(newDir.getAbsolutePath(), 0711);
+        } catch (ErrnoException e) {
+            throw new SystemFontException(
+                    FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+                    "Failed to change mode to 711", e);
+        }
+        boolean success = false;
+        try {
+            File tempNewFontFile = new File(newDir, "font.ttf");
+            try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
+                FileUtils.copy(fd, out.getFD());
+            } catch (IOException e) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
-                        "Failed to create font directory.");
+                        "Failed to write font file to storage.", e);
             }
             try {
-                // Make newDir executable so that apps can access font file inside newDir.
-                Os.chmod(newDir.getAbsolutePath(), 0711);
+                // Do not parse font file before setting up fs-verity.
+                // setUpFsverity throws IOException if failed.
+                mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+                        pkcs7Signature);
+            } catch (IOException e) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
+                        "Failed to setup fs-verity.", e);
+            }
+            String postScriptName;
+            try {
+                postScriptName = mParser.getPostScriptName(tempNewFontFile);
+            } catch (IOException e) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_INVALID_FONT_FILE,
+                        "Failed to read PostScript name from font file", e);
+            }
+            if (postScriptName == null) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_INVALID_FONT_NAME,
+                        "Failed to read PostScript name from font file");
+            }
+            File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
+            if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
+                        "Failed to move verified font file.");
+            }
+            try {
+                // Make the font file readable by apps.
+                Os.chmod(newFontFile.getAbsolutePath(), 0644);
             } catch (ErrnoException e) {
                 throw new SystemFontException(
                         FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
                         "Failed to change mode to 711", e);
             }
-            boolean success = false;
-            try {
-                File tempNewFontFile = new File(newDir, "font.ttf");
-                try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
-                    FileUtils.copy(fd, out.getFD());
-                } catch (IOException e) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
-                            "Failed to write font file to storage.", e);
-                }
-                try {
-                    // Do not parse font file before setting up fs-verity.
-                    // setUpFsverity throws IOException if failed.
-                    mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
-                            pkcs7Signature);
-                } catch (IOException e) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_VERIFICATION_FAILURE,
-                            "Failed to setup fs-verity.", e);
-                }
-                String postScriptName;
-                try {
-                    postScriptName = mParser.getPostScriptName(tempNewFontFile);
-                } catch (IOException e) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_INVALID_FONT_FILE,
-                            "Failed to read PostScript name from font file", e);
-                }
-                if (postScriptName == null) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_INVALID_FONT_NAME,
-                            "Failed to read PostScript name from font file");
-                }
-                File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
-                if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
-                            "Failed to move verified font file.");
-                }
-                try {
-                    // Make the font file readable by apps.
-                    Os.chmod(newFontFile.getAbsolutePath(), 0644);
-                } catch (ErrnoException e) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_FAILED_TO_WRITE_FONT_FILE,
-                            "Failed to change mode to 711", e);
-                }
-                FontFileInfo fontFileInfo = validateFontFile(newFontFile);
+            FontFileInfo fontFileInfo = validateFontFile(newFontFile);
 
-                // Write config file.
-                PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
-                mConfig.copyTo(copied);
+            // Write config file.
+            PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
+            mConfig.copyTo(copied);
 
-                copied.lastModifiedDate = Instant.now().getEpochSecond();
-                try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
-                    PersistentSystemFontConfig.writeToXml(fos, copied);
-                } catch (Exception e) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
-                            "Failed to write config XML.", e);
-                }
+            copied.lastModifiedDate = Instant.now().getEpochSecond();
+            try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+                PersistentSystemFontConfig.writeToXml(fos, copied);
+            } catch (Exception e) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+                        "Failed to write config XML.", e);
+            }
 
-                // Backup the mapping for rollback.
-                HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
-                if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_DOWNGRADING,
-                            "Downgrading font file is forbidden.");
-                }
+            // Backup the mapping for rollback.
+            HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
+            if (!addFileToMapIfNewer(fontFileInfo, false)) {
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_DOWNGRADING,
+                        "Downgrading font file is forbidden.");
+            }
 
-                if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
-                    // If we fail to stage the config file, need to rollback the config.
-                    mFontFileInfoMap.clear();
-                    mFontFileInfoMap.putAll(backup);
-                    throw new SystemFontException(
-                            FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
-                            "Failed to stage the config file.");
-                }
+            if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+                // If we fail to stage the config file, need to rollback the config.
+                mFontFileInfoMap.clear();
+                mFontFileInfoMap.putAll(backup);
+                throw new SystemFontException(
+                        FontManager.RESULT_ERROR_FAILED_UPDATE_CONFIG,
+                        "Failed to stage the config file.");
+            }
 
 
-                // Now font update is succeeded. Update config version.
-                copied.copyTo(mConfig);
-                mConfigVersion++;
+            // Now font update is succeeded. Update config version.
+            copied.copyTo(mConfig);
+            mConfigVersion++;
 
-                success = true;
-            } finally {
-                if (!success) {
-                    FileUtils.deleteContentsAndDir(newDir);
-                }
+            success = true;
+        } finally {
+            if (!success) {
+                FileUtils.deleteContentsAndDir(newDir);
             }
         }
     }
@@ -341,7 +335,7 @@
      * higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
      * #mPreinstalledFontDirs}).
      */
-    private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+    private boolean addFileToMapIfNewer(FontFileInfo fontFileInfo, boolean deleteOldFile) {
         String name = fontFileInfo.getFile().getName();
         FontFileInfo existingInfo = mFontFileInfoMap.get(name);
         final boolean shouldAddToMap;
@@ -447,27 +441,21 @@
 
     Map<String, File> getFontFileMap() {
         Map<String, File> map = new HashMap<>();
-        synchronized (UpdatableFontDir.this) {
-            for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
-                map.put(entry.getKey(), entry.getValue().getFile());
-            }
+        for (Map.Entry<String, FontFileInfo> entry : mFontFileInfoMap.entrySet()) {
+            map.put(entry.getKey(), entry.getValue().getFile());
         }
         return map;
     }
 
     /* package */ FontConfig getSystemFontConfig() {
-        synchronized (UpdatableFontDir.this) {
-            return SystemFonts.getSystemFontConfig(
-                    getFontFileMap(),
-                    mConfig.lastModifiedDate,
-                    mConfigVersion
-            );
-        }
+        return SystemFonts.getSystemFontConfig(
+                getFontFileMap(),
+                mConfig.lastModifiedDate,
+                mConfigVersion
+        );
     }
 
     /* package */ int getConfigVersion() {
-        synchronized (UpdatableFontDir.this) {
-            return mConfigVersion;
-        }
+        return mConfigVersion;
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 38ae51f..83085cc 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -977,7 +977,7 @@
             AudioPortConfig sourceConfig = mAudioSource.activeConfig();
             List<AudioPortConfig> sinkConfigs = new ArrayList<>();
             AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
-            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
+            boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated || mAudioPatch == null;
 
             for (AudioDevicePort audioSink : mAudioSink) {
                 AudioPortConfig sinkConfig = audioSink.activeConfig();
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index ed97848..1b20c44 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -168,7 +168,6 @@
 import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
 import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
-import com.android.server.policy.WindowOrientationListener;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
 import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6a86aee..48e4df7 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -69,7 +69,6 @@
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowOrientationListener;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import java.io.PrintWriter;
@@ -253,7 +252,7 @@
 
         if (isDefaultDisplay) {
             final Handler uiHandler = UiThread.getHandler();
-            mOrientationListener = new OrientationListener(mContext, uiHandler);
+            mOrientationListener = new OrientationListener(mContext, uiHandler, mService);
             mOrientationListener.setCurrentRotation(mRotation);
             mSettingsObserver = new SettingsObserver(uiHandler);
             mSettingsObserver.observe();
@@ -1474,8 +1473,8 @@
         final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5);
         boolean mEnabled;
 
-        OrientationListener(Context context, Handler handler) {
-            super(context, handler);
+        OrientationListener(Context context, Handler handler, WindowManagerService service) {
+            super(context, handler, service);
         }
 
         private class UpdateRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/wm/ImpressionAttestationController.java b/services/core/java/com/android/server/wm/ScreenshotHashController.java
similarity index 79%
rename from services/core/java/com/android/server/wm/ImpressionAttestationController.java
rename to services/core/java/com/android/server/wm/ScreenshotHashController.java
index bad6c80..03f4e28 100644
--- a/services/core/java/com/android/server/wm/ImpressionAttestationController.java
+++ b/services/core/java/com/android/server/wm/ScreenshotHashController.java
@@ -16,7 +16,9 @@
 
 package com.android.server.wm;
 
-import static android.service.attestation.ImpressionAttestationService.SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_SCREENSHOT_HASH;
+import static android.service.screenshot.ScreenshotHasherService.EXTRA_VERIFICATION_STATUS;
+import static android.service.screenshot.ScreenshotHasherService.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;
@@ -44,9 +46,9 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.attestation.IImpressionAttestationService;
-import android.service.attestation.ImpressionAttestationService;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.IScreenshotHasherService;
+import android.service.screenshot.ScreenshotHash;
+import android.service.screenshot.ScreenshotHasherService;
 import android.util.Slog;
 import android.view.MagnificationSpec;
 
@@ -59,30 +61,29 @@
 import java.util.function.BiConsumer;
 
 /**
- * Handles requests into {@link ImpressionAttestationService}
+ * Handles requests into {@link android.service.screenshot.ScreenshotHasherService}
  *
  * Do not hold the {@link WindowManagerService#mGlobalLock} when calling methods since they are
  * blocking calls into another service.
  */
-public class ImpressionAttestationController {
-    private static final String TAG =
-            TAG_WITH_CLASS_NAME ? "ImpressionAttestationController" : TAG_WM;
+public class ScreenshotHashController {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "ScreenshotHashController" : TAG_WM;
     private static final boolean DEBUG = false;
 
     private final Object mServiceConnectionLock = new Object();
 
     @GuardedBy("mServiceConnectionLock")
-    private ImpressionAttestationServiceConnection mServiceConnection;
+    private ScreenshotHasherServiceConnection mServiceConnection;
 
     private final Context mContext;
 
     /**
-     * Lock used for the cached {@link #mImpressionAlgorithms} array
+     * Lock used for the cached {@link #mHashingAlgorithms} array
      */
-    private final Object mImpressionAlgorithmsLock = new Object();
+    private final Object mHashingAlgorithmsLock = new Object();
 
-    @GuardedBy("mImpressionAlgorithmsLock")
-    private String[] mImpressionAlgorithms;
+    @GuardedBy("mHashingAlgorithmsLock")
+    private String[] mHashingAlgorithms;
 
     private final Handler mHandler;
 
@@ -93,24 +94,24 @@
     private final RectF mTmpRectF = new RectF();
 
     private interface Command {
-        void run(IImpressionAttestationService service) throws RemoteException;
+        void run(IScreenshotHasherService service) throws RemoteException;
     }
 
-    ImpressionAttestationController(Context context) {
+    ScreenshotHashController(Context context) {
         mContext = context;
         mHandler = new Handler(Looper.getMainLooper());
         mSalt = UUID.randomUUID().toString().getBytes();
     }
 
-    String[] getSupportedImpressionAlgorithms() {
-        // We have a separate lock for the impression algorithm array since it doesn't need to make
+    String[] getSupportedHashingAlgorithms() {
+        // We have a separate lock for the hashing algorithm array since it doesn't need to make
         // the request through the service connection. Instead, we have a lock to ensure we can
-        // properly cache the impression algorithms array so we don't need to call into the
+        // properly cache the hashing algorithms array so we don't need to call into the
         // ExtServices process for each request.
-        synchronized (mImpressionAlgorithmsLock) {
+        synchronized (mHashingAlgorithmsLock) {
             // Already have cached values
-            if (mImpressionAlgorithms != null) {
-                return mImpressionAlgorithms;
+            if (mHashingAlgorithms != null) {
+                return mHashingAlgorithms;
             }
 
             final ServiceInfo serviceInfo = getServiceInfo();
@@ -127,54 +128,55 @@
 
             final int resourceId = serviceInfo.metaData.getInt(
                     SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS);
-            mImpressionAlgorithms = res.getStringArray(resourceId);
+            mHashingAlgorithms = res.getStringArray(resourceId);
 
-            return mImpressionAlgorithms;
+            return mHashingAlgorithms;
         }
     }
 
-    boolean verifyImpressionToken(ImpressionToken impressionToken) {
+    boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
         final SyncCommand syncCommand = new SyncCommand();
         Bundle results = syncCommand.run((service, remoteCallback) -> {
             try {
-                service.verifyImpressionToken(mSalt, impressionToken, remoteCallback);
+                service.verifyScreenshotHash(mSalt, screenshotHash, remoteCallback);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to invoke verifyImpressionToken command");
+                Slog.e(TAG, "Failed to invoke verifyScreenshotHash command");
             }
         });
 
-        return results.getBoolean(ImpressionAttestationService.EXTRA_VERIFICATION_STATUS);
+        return results.getBoolean(EXTRA_VERIFICATION_STATUS);
     }
 
-    ImpressionToken generateImpressionToken(HardwareBuffer screenshot, Rect bounds,
+    ScreenshotHash generateScreenshotHash(HardwareBuffer screenshot, Rect bounds,
             String hashAlgorithm) {
         final SyncCommand syncCommand = new SyncCommand();
         Bundle results = syncCommand.run((service, remoteCallback) -> {
             try {
-                service.generateImpressionToken(mSalt, screenshot, bounds, hashAlgorithm,
+                service.generateScreenshotHash(mSalt, screenshot, bounds, hashAlgorithm,
                         remoteCallback);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to invoke generateImpressionToken command", e);
+                Slog.e(TAG, "Failed to invoke generateScreenshotHash command", e);
             }
         });
 
-        return results.getParcelable(ImpressionAttestationService.EXTRA_IMPRESSION_TOKEN);
+        return results.getParcelable(EXTRA_SCREENSHOT_HASH);
     }
 
     /**
-     * Calculate the bounds to take the screenshot when generating the impression token. This takes
-     * into account window transform, magnification, and display bounds.
+     * Calculate the bounds to take the screenshot when generating the ScreenshotHash. 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 win Window that the ScreenshotHash 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,
+    void calculateScreenshotHashBoundsLocked(WindowState win, Rect boundsInWindow,
             Rect outBounds) {
         if (DEBUG) {
-            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInWindow=" + boundsInWindow);
+            Slog.d(TAG,
+                    "calculateScreenshotHashBoundsLocked: boundsInWindow=" + boundsInWindow);
         }
         outBounds.set(boundsInWindow);
 
@@ -192,7 +194,8 @@
         outBounds.intersectUnchecked(windowBounds);
 
         if (DEBUG) {
-            Slog.d(TAG, "calculateImpressionTokenBounds: boundsIntersectWindow=" + outBounds);
+            Slog.d(TAG,
+                    "calculateScreenshotHashBoundsLocked: boundsIntersectWindow=" + outBounds);
         }
 
         if (outBounds.isEmpty()) {
@@ -207,7 +210,7 @@
         outBounds.set((int) mTmpRectF.left, (int) mTmpRectF.top, (int) mTmpRectF.right,
                 (int) mTmpRectF.bottom);
         if (DEBUG) {
-            Slog.d(TAG, "calculateImpressionTokenBounds: boundsInDisplay=" + outBounds);
+            Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsInDisplay=" + outBounds);
         }
 
         // Apply the magnification spec values to the bounds since the content could be magnified
@@ -218,7 +221,8 @@
         }
 
         if (DEBUG) {
-            Slog.d(TAG, "calculateImpressionTokenBounds: boundsWithMagnification=" + outBounds);
+            Slog.d(TAG, "calculateScreenshotHashBoundsLocked: boundsWithMagnification="
+                    + outBounds);
         }
 
         if (outBounds.isEmpty()) {
@@ -230,7 +234,7 @@
         final Rect displayBounds = displayContent.getBounds();
         outBounds.intersectUnchecked(displayBounds);
         if (DEBUG) {
-            Slog.d(TAG, "calculateImpressionTokenBounds: finalBounds=" + outBounds);
+            Slog.d(TAG, "calculateScreenshotHashBoundsLocked: finalBounds=" + outBounds);
         }
     }
 
@@ -244,7 +248,7 @@
                 if (DEBUG) Slog.v(TAG, "creating connection");
 
                 // Create the connection
-                mServiceConnection = new ImpressionAttestationServiceConnection();
+                mServiceConnection = new ScreenshotHasherServiceConnection();
 
                 final ComponentName component = getServiceComponentName();
                 if (DEBUG) Slog.v(TAG, "binding to: " + component);
@@ -275,7 +279,7 @@
             return null;
         }
 
-        final Intent intent = new Intent(ImpressionAttestationService.SERVICE_INTERFACE);
+        final Intent intent = new Intent(ScreenshotHasherService.SERVICE_INTERFACE);
         intent.setPackage(packageName);
         final ResolveInfo resolveInfo = mContext.getPackageManager().resolveService(intent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
@@ -292,10 +296,10 @@
         if (serviceInfo == null) return null;
 
         final ComponentName name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
-        if (!Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE
+        if (!Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE
                 .equals(serviceInfo.permission)) {
             Slog.w(TAG, name.flattenToShortString() + " requires permission "
-                    + Manifest.permission.BIND_IMPRESSION_ATTESTATION_SERVICE);
+                    + Manifest.permission.BIND_SCREENSHOT_HASHER_SERVICE);
             return null;
         }
 
@@ -308,7 +312,7 @@
         private Bundle mResult;
         private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
 
-        public Bundle run(BiConsumer<IImpressionAttestationService, RemoteCallback> func) {
+        public Bundle run(BiConsumer<IScreenshotHasherService, RemoteCallback> func) {
             connectAndRun(service -> {
                 RemoteCallback callback = new RemoteCallback(result -> {
                     mResult = result;
@@ -327,9 +331,9 @@
         }
     }
 
-    private class ImpressionAttestationServiceConnection implements ServiceConnection {
+    private class ScreenshotHasherServiceConnection implements ServiceConnection {
         @GuardedBy("mServiceConnectionLock")
-        private IImpressionAttestationService mRemoteService;
+        private IScreenshotHasherService mRemoteService;
 
         @GuardedBy("mServiceConnectionLock")
         private ArrayList<Command> mQueuedCommands;
@@ -338,7 +342,7 @@
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) Slog.v(TAG, "onServiceConnected(): " + name);
             synchronized (mServiceConnectionLock) {
-                mRemoteService = IImpressionAttestationService.Stub.asInterface(service);
+                mRemoteService = IScreenshotHasherService.Stub.asInterface(service);
                 if (mQueuedCommands != null) {
                     final int size = mQueuedCommands.size();
                     if (DEBUG) Slog.d(TAG, "running " + size + " queued commands");
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 8d8bdcb..fe7ec16 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -57,7 +57,7 @@
 import android.os.RemoteException;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
@@ -862,11 +862,11 @@
     }
 
     @Override
-    public ImpressionToken generateImpressionToken(IWindow window, Rect boundsInWindow,
+    public ScreenshotHash generateScreenshotHash(IWindow window, Rect boundsInWindow,
             String hashAlgorithm) {
         final long origId = Binder.clearCallingIdentity();
         try {
-            return mService.generateImpressionToken(this, window, boundsInWindow, hashAlgorithm);
+            return mService.generateScreenshotHash(this, window, boundsInWindow, hashAlgorithm);
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerConstants.java b/services/core/java/com/android/server/wm/WindowManagerConstants.java
index a5ebf9a..015a0fb 100644
--- a/services/core/java/com/android/server/wm/WindowManagerConstants.java
+++ b/services/core/java/com/android/server/wm/WindowManagerConstants.java
@@ -49,6 +49,10 @@
     static final String KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS =
             "system_gesture_exclusion_log_debounce_millis";
 
+    // Enable logging from the sensor which publishes accel and gyro data generating a rotation
+    // event
+    private static final String KEY_RAW_SENSOR_LOGGING_ENABLED = "raw_sensor_logging_enabled";
+
     private static final int MIN_GESTURE_EXCLUSION_LIMIT_DP = 200;
 
     /** @see #KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS */
@@ -58,6 +62,8 @@
     /** @see AndroidDeviceConfig#KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE */
     boolean mSystemGestureExcludedByPreQStickyImmersive;
 
+    boolean mRawSensorLoggingEnabled;
+
     private final WindowManagerGlobalLock mGlobalLock;
     private final Runnable mUpdateSystemGestureExclusionCallback;
     private final DeviceConfigInterface mDeviceConfig;
@@ -133,6 +139,9 @@
                     case KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS:
                         updateSystemGestureExclusionLogDebounceMillis();
                         break;
+                    case KEY_RAW_SENSOR_LOGGING_ENABLED:
+                        updateRawSensorDataLoggingEnabled();
+                        break;
                     default:
                         break;
                 }
@@ -158,6 +167,12 @@
                 KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
     }
 
+    private void updateRawSensorDataLoggingEnabled() {
+        mRawSensorLoggingEnabled = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                KEY_RAW_SENSOR_LOGGING_ENABLED, false);
+    }
+
     void dump(PrintWriter pw) {
         pw.println("WINDOW MANAGER CONSTANTS (dumpsys window constants):");
 
@@ -167,6 +182,8 @@
         pw.print("="); pw.println(mSystemGestureExclusionLimitDp);
         pw.print("  "); pw.print(KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE);
         pw.print("="); pw.println(mSystemGestureExcludedByPreQStickyImmersive);
+        pw.print("  "); pw.print(KEY_RAW_SENSOR_LOGGING_ENABLED);
+        pw.print("="); pw.println(mRawSensorLoggingEnabled);
         pw.println();
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 27b7fab..09fbce0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -202,7 +202,7 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
-import android.service.attestation.ImpressionToken;
+import android.service.screenshot.ScreenshotHash;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
 import android.sysprop.SurfaceFlingerProperties;
@@ -458,7 +458,8 @@
      */
     static final float MIN_TASK_LETTERBOX_ASPECT_RATIO = 1.0f;
 
-    final WindowManagerConstants mConstants;
+    @VisibleForTesting
+    WindowManagerConstants mConstants;
 
     final WindowTracing mWindowTracing;
 
@@ -768,7 +769,7 @@
     final EmbeddedWindowController mEmbeddedWindowController;
     final AnrController mAnrController;
 
-    private final ImpressionAttestationController mImpressionAttestationController;
+    private final ScreenshotHashController mScreenshotHashController;
     private final WindowContextListenerController mWindowContextListenerController =
             new WindowContextListenerController();
 
@@ -1418,7 +1419,7 @@
         mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
                 mContext.getResources());
 
-        mImpressionAttestationController = new ImpressionAttestationController(mContext);
+        mScreenshotHashController = new ScreenshotHashController(mContext);
         setGlobalShadowSettings();
         mAnrController = new AnrController(this);
         mStartingSurfaceController = new StartingSurfaceController(this);
@@ -8660,38 +8661,38 @@
     }
 
     @Override
-    public String[] getSupportedImpressionAlgorithms() {
-        return mImpressionAttestationController.getSupportedImpressionAlgorithms();
+    public String[] getSupportedScreenshotHashingAlgorithms() {
+        return mScreenshotHashController.getSupportedHashingAlgorithms();
     }
 
     @Override
-    public boolean verifyImpressionToken(ImpressionToken impressionToken) {
-        return mImpressionAttestationController.verifyImpressionToken(impressionToken);
+    public boolean verifyScreenshotHash(ScreenshotHash screenshotHash) {
+        return mScreenshotHashController.verifyScreenshotHash(screenshotHash);
     }
 
-    ImpressionToken generateImpressionToken(Session session, IWindow window,
+    ScreenshotHash generateScreenshotHash(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");
+                Slog.w(TAG, "Failed to generate ScreenshotHash. 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");
+                Slog.w(TAG, "Failed to generate ScreenshotHash. Window is not on a display");
                 return null;
             }
 
             displaySurfaceControl = displayContent.getSurfaceControl();
-            mImpressionAttestationController.calculateImpressionTokenBoundsLocked(win,
+            mScreenshotHashController.calculateScreenshotHashBoundsLocked(win,
                     boundsInWindow, boundsInDisplay);
 
             if (boundsInDisplay.isEmpty()) {
-                Slog.w(TAG, "Failed to generate impression token. Bounds are not on screen");
+                Slog.w(TAG, "Failed to generate ScreenshotHash. Bounds are not on screen");
                 return null;
             }
         }
@@ -8711,11 +8712,11 @@
                 SurfaceControl.captureLayers(args);
         if (screenshotHardwareBuffer == null
                 || screenshotHardwareBuffer.getHardwareBuffer() == null) {
-            Slog.w(TAG, "Failed to generate impression token. Failed to take screenshot");
+            Slog.w(TAG, "Failed to generate ScreenshotHash. Failed to take screenshot");
             return null;
         }
 
-        return mImpressionAttestationController.generateImpressionToken(
+        return mScreenshotHashController.generateScreenshotHash(
                 screenshotHardwareBuffer.getHardwareBuffer(), boundsInWindow, hashAlgorithm);
     }
 }
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java
similarity index 96%
rename from services/core/java/com/android/server/policy/WindowOrientationListener.java
rename to services/core/java/com/android/server/wm/WindowOrientationListener.java
index 17e81da..da31bb2 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.policy;
+package com.android.server.wm;
 
 import static com.android.server.wm.WindowOrientationListenerProto.ENABLED;
 import static com.android.server.wm.WindowOrientationListenerProto.ROTATION;
@@ -31,6 +31,8 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
+import com.android.internal.util.FrameworkStatsLog;
+
 import java.io.PrintWriter;
 import java.util.List;
 
@@ -63,6 +65,7 @@
     private OrientationJudge mOrientationJudge;
     private int mCurrentRotation = -1;
     private final Context mContext;
+    private final WindowManagerConstants mConstants;
 
     private final Object mLock = new Object();
 
@@ -71,9 +74,11 @@
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
+     * @param wmService WindowManagerService to read the device config from.
      */
-    public WindowOrientationListener(Context context, Handler handler) {
-        this(context, handler, SensorManager.SENSOR_DELAY_UI);
+    public WindowOrientationListener(
+            Context context, Handler handler, WindowManagerService wmService) {
+        this(context, handler, wmService, SensorManager.SENSOR_DELAY_UI);
     }
 
     /**
@@ -81,6 +86,7 @@
      *
      * @param context for the WindowOrientationListener.
      * @param handler Provides the Looper for receiving sensor updates.
+     * @param wmService WindowManagerService to read the device config from.
      * @param rate at which sensor events are processed (see also
      * {@link android.hardware.SensorManager SensorManager}). Use the default
      * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
@@ -88,10 +94,12 @@
      *
      * This constructor is private since no one uses it.
      */
-    private WindowOrientationListener(Context context, Handler handler, int rate) {
+    private WindowOrientationListener(
+            Context context, Handler handler, WindowManagerService wmService, int rate) {
         mContext = context;
         mHandler = handler;
-        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mConstants = wmService.mConstants;
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
         mRate = rate;
         List<Sensor> l = mSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION);
         Sensor wakeUpDeviceOrientationSensor = null;
@@ -497,7 +505,7 @@
         private static final float MIN_ACCELERATION_MAGNITUDE =
                 SensorManager.STANDARD_GRAVITY - ACCELERATION_TOLERANCE;
         private static final float MAX_ACCELERATION_MAGNITUDE =
-            SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
+                SensorManager.STANDARD_GRAVITY + ACCELERATION_TOLERANCE;
 
         // Maximum absolute tilt angle at which to consider orientation data.  Beyond this (i.e.
         // when screen is facing the sky or ground), we completely ignore orientation data
@@ -1072,8 +1080,14 @@
                 mDesiredRotation = reportedRotation;
                 newRotation = evaluateRotationChangeLocked();
             }
-            if (newRotation >=0) {
+            if (newRotation >= 0) {
                 onProposedRotationChanged(newRotation);
+                if (mConstants.mRawSensorLoggingEnabled) {
+                    FrameworkStatsLog.write(
+                            FrameworkStatsLog.DEVICE_ROTATED,
+                            event.timestamp,
+                            rotationToLogEnum(reportedRotation));
+                }
             }
         }
 
@@ -1180,5 +1194,20 @@
                 }
             }
         };
+
+        private int rotationToLogEnum(int rotation) {
+            switch (rotation) {
+                case 0:
+                    return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_0;
+                case 1:
+                    return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_90;
+                case 2:
+                    return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_180;
+                case 3:
+                    return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__ROTATION_270;
+                default:
+                    return FrameworkStatsLog.DEVICE_ROTATED__PROPOSED_ORIENTATION__UNKNOWN;
+            }
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d235a7f..4b1bae4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16220,12 +16220,16 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            int result = checkProvisioningPreConditionSkipPermission(
-                    ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
-            if (result != CODE_OK) {
-                throw new ServiceSpecificException(
-                        PROVISIONING_RESULT_PRE_CONDITION_FAILED,
-                        "Provisioning preconditions failed with result: " + result);
+            // TODO(b/178187130): This check fails silent provisioning, uncomment once silent
+            //  provisioning is no longer used.
+            if (false) {
+                int result = checkProvisioningPreConditionSkipPermission(
+                        ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
+                if (result != CODE_OK) {
+                    throw new ServiceSpecificException(
+                            PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+                            "Provisioning preconditions failed with result: " + result);
+                }
             }
 
             setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -16333,6 +16337,11 @@
     private boolean setActiveAdminAndDeviceOwner(
             @UserIdInt int userId, ComponentName adminComponent, String name) {
         enableAndSetActiveAdmin(userId, userId, adminComponent);
-        return setDeviceOwner(adminComponent, name, userId);
+        // TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
+        //  longer used.
+        if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
+            return setDeviceOwner(adminComponent, name, userId);
+        }
+        return true;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
index e1aca55..2321a73 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java
@@ -121,6 +121,8 @@
         sMockWm = mock(WindowManagerService.class);
         sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
         sMockWm.mPolicy = mock(WindowManagerPolicy.class);
+        sMockWm.mConstants = mock(WindowManagerConstants.class);
+        sMockWm.mConstants.mRawSensorLoggingEnabled = true;
     }
 
     @AfterClass
diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc
index eaa3e04..5cb0c17 100644
--- a/startop/view_compiler/apk_layout_compiler.cc
+++ b/startop/view_compiler/apk_layout_compiler.cc
@@ -80,7 +80,7 @@
 }
 
 namespace {
-void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
+void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
                              CompilationTarget target, std::ostream& target_out) {
   android::AssetManager2 resources;
   resources.SetApkAssets({assets.get()});
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 8507d85..706e3cb 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -344,6 +344,7 @@
             // TODO: Instead of doing this, we should create a formal way for cloning cell identity.
             // Cell identity is not an immutable object so we have to deep copy it.
             mCellIdentity = CellIdentity.CREATOR.createFromParcel(p);
+            p.recycle();
         }
 
         if (nri.mVoiceSpecificInfo != null) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e4386e75..65b8de2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -14242,7 +14242,7 @@
      * If this policy is enabled, data will be temporarily enabled on the non-default data SIM
      * during any voice calls.
      *
-     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
      * @hide
      */
     @SystemApi
@@ -14256,7 +14256,7 @@
      * will also return true for {@link ApnSetting#TYPE_MMS}.
      * When disabled, the MMS APN will be governed by the same rules as all other APNs.
      *
-     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabledStatus}.
+     * This policy can be enabled and disabled via {@link #setMobileDataPolicyEnabled}.
      * @hide
      */
     @SystemApi
@@ -14284,11 +14284,11 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) {
+    public void setMobileDataPolicyEnabled(@MobileDataPolicy int policy, boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.setMobileDataPolicyEnabledStatus(getSubId(), policy, enabled);
+                service.setMobileDataPolicyEnabled(getSubId(), policy, enabled);
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index d17415a..0cd17da3 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2172,7 +2172,7 @@
      */
     String getMmsUAProfUrl(int subId);
 
-    void setMobileDataPolicyEnabledStatus(int subscriptionId, int policy, boolean enabled);
+    void setMobileDataPolicyEnabled(int subscriptionId, int policy, boolean enabled);
 
     boolean isMobileDataPolicyEnabled(int subscriptionId, int policy);