Merge "Hide UDFPS overlay when onError during enroll"
diff --git a/.gitignore b/.gitignore
index c47cc8b..5018436 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
 .idea
 *.iml
 *.sw*
-gen/
\ No newline at end of file
+gen/
+.vscode/
+*.code-workspace
diff --git a/Android.bp b/Android.bp
index 35f97ac..5c0dd63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -532,6 +532,7 @@
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
         "android.security.apc-java",
+        "android.security.authorization-java",
         "android.system.keystore2-java",
         "android.system.suspend.control.internal-java",
         "cameraprotosnano",
diff --git a/apct-tests/perftests/OWNERS b/apct-tests/perftests/OWNERS
index a060ad9..7e7feaf 100644
--- a/apct-tests/perftests/OWNERS
+++ b/apct-tests/perftests/OWNERS
@@ -1,2 +1,11 @@
-timmurray@google.com
+balejs@google.com
+carmenjackson@google.com
+cfijalkovich@google.com
+dualli@google.com
+edgararriaga@google.com
+jpakaravoor@google.com
+kevinjeon@google.com
 philipcuadra@google.com
+shombert@google.com
+timmurray@google.com
+wessam@google.com
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 18643ed..4441643 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import android.app.BroadcastOptions;
+
 import com.android.server.deviceidle.IDeviceIdleConstraint;
 
 public interface DeviceIdleInternal {
@@ -32,8 +34,17 @@
     void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
             long duration, int userId, boolean sync, String reason);
 
-    // duration in milliseconds
-    void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
+    /**
+     * Called by ActivityManagerService to directly add UID to DeviceIdleController's temp
+     * allowlist.
+     * @param uid
+     * @param duration duration in milliseconds
+     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     * @param sync
+     * @param reason
+     */
+    void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
+            @BroadcastOptions.TempAllowListType int type, boolean sync,
             String reason);
 
     // duration in milliseconds
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 4b98c32..7aed32c 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -21,6 +21,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
+import android.app.BroadcastOptions;
+import android.app.BroadcastOptions.TempAllowListType;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -1941,9 +1943,9 @@
 
         // duration in milliseconds
         @Override
-        public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
-                String reason) {
-            addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, sync, reason);
+        public void addPowerSaveTempWhitelistAppDirect(int uid, long duration,
+                @TempAllowListType int type, boolean sync, String reason) {
+            addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, type, sync, reason);
         }
 
         // duration in milliseconds
@@ -2719,7 +2721,9 @@
             long duration, int userId, boolean sync, String reason) {
         try {
             int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
-            addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration, sync, reason);
+            addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, duration,
+                    BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED, sync,
+                    reason);
         } catch (NameNotFoundException e) {
         }
     }
@@ -2729,7 +2733,7 @@
      * app an exemption to access network and acquire wakelocks.
      */
     void addPowerSaveTempWhitelistAppDirectInternal(int callingUid, int uid,
-            long duration, boolean sync, String reason) {
+            long duration, @TempAllowListType int type, boolean sync, String reason) {
         final long timeNow = SystemClock.elapsedRealtime();
         boolean informWhitelistChanged = false;
         int appId = UserHandle.getAppId(uid);
@@ -2761,7 +2765,7 @@
                 } catch (RemoteException e) {
                 }
                 postTempActiveTimeoutMessage(uid, duration);
-                updateTempWhitelistAppIdsLocked(appId, true);
+                updateTempWhitelistAppIdsLocked(uid, true, duration, type);
                 if (sync) {
                     informWhitelistChanged = true;
                 } else {
@@ -2786,8 +2790,7 @@
         try {
             final int uid = getContext().getPackageManager().getPackageUidAsUser(
                     packageName, userId);
-            final int appId = UserHandle.getAppId(uid);
-            removePowerSaveTempWhitelistAppDirectInternal(appId);
+            removePowerSaveTempWhitelistAppDirectInternal(uid);
         } catch (NameNotFoundException e) {
         }
     }
@@ -2821,7 +2824,8 @@
             Slog.d(TAG, "checkTempAppWhitelistTimeout: uid=" + uid + ", timeNow=" + timeNow);
         }
         synchronized (this) {
-            Pair<MutableLong, String> entry = mTempWhitelistAppIdEndTimes.get(appId);
+            Pair<MutableLong, String> entry =
+                    mTempWhitelistAppIdEndTimes.get(appId);
             if (entry == null) {
                 // Nothing to do
                 return;
@@ -2832,7 +2836,7 @@
             } else {
                 // Need more time
                 if (DEBUG) {
-                    Slog.d(TAG, "Time to remove AppId " + appId + ": " + entry.first.value);
+                    Slog.d(TAG, "Time to remove uid " + uid + ": " + entry.first.value);
                 }
                 postTempActiveTimeoutMessage(uid, entry.first.value - timeNow);
             }
@@ -2841,12 +2845,12 @@
 
     @GuardedBy("this")
     private void onAppRemovedFromTempWhitelistLocked(int uid, String reason) {
-        final int appId = UserHandle.getAppId(uid);
         if (DEBUG) {
             Slog.d(TAG, "Removing uid " + uid + " from temp whitelist");
         }
-        updateTempWhitelistAppIdsLocked(appId, false);
-        mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED_TO_NPMS, appId, 0)
+        final int appId = UserHandle.getAppId(uid);
+        updateTempWhitelistAppIdsLocked(uid, false, 0, 0);
+        mHandler.obtainMessage(MSG_REPORT_TEMP_APP_WHITELIST_CHANGED, appId, 0)
                 .sendToTarget();
         reportTempWhitelistChangedLocked(uid, false);
         try {
@@ -3869,7 +3873,16 @@
         passWhiteListsToForceAppStandbyTrackerLocked();
     }
 
-    private void updateTempWhitelistAppIdsLocked(int appId, boolean adding) {
+    /**
+     * update temp allowlist.
+     * @param uid uid to add or remove from temp allowlist.
+     * @param adding true to add to temp allowlist, false to remove from temp allowlist.
+     * @param durationMs duration in milliseconds to add to temp allowlist, only valid when
+     *                   param adding is true.
+     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}
+     */
+    private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
+            @TempAllowListType int type) {
         final int size = mTempWhitelistAppIdEndTimes.size();
         if (mTempWhitelistAppIdArray.length != size) {
             mTempWhitelistAppIdArray = new int[size];
@@ -3882,8 +3895,8 @@
                 Slog.d(TAG, "Setting activity manager temp whitelist to "
                         + Arrays.toString(mTempWhitelistAppIdArray));
             }
-            mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, appId,
-                    adding);
+            mLocalActivityManager.updateDeviceIdleTempWhitelist(mTempWhitelistAppIdArray, uid,
+                    adding, durationMs, type);
         }
         if (mLocalPowerManager != null) {
             if (DEBUG) {
diff --git a/core/api/current.txt b/core/api/current.txt
index e77ada6..5e07790 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12053,7 +12053,6 @@
 
   public static class PackageInstaller.Session implements java.io.Closeable {
     method public void abandon();
-    method @Deprecated public void addChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>) throws java.io.IOException;
     method public void addChildSessionId(int);
     method public void close();
     method public void commit(@NonNull android.content.IntentSender);
@@ -12067,6 +12066,7 @@
     method @NonNull public java.io.OutputStream openWrite(@NonNull String, long, long) throws java.io.IOException;
     method public void removeChildSessionId(int);
     method public void removeSplit(@NonNull String) throws java.io.IOException;
+    method @Deprecated public void setChecksums(@NonNull String, @NonNull java.util.List<android.content.pm.Checksum>, @Nullable byte[]) throws java.io.IOException;
     method public void setStagingProgress(float);
     method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d4d05f0..275b31b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10231,6 +10231,7 @@
     method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method @Nullable public final String getTelecomCallId();
     method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
+    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
     method public final void resetConnectionTime();
     method public void setCallDirection(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
@@ -10407,6 +10408,7 @@
   }
 
   public final class RemoteConnection {
+    method @RequiresPermission(android.Manifest.permission.READ_CONTACTS) public void onCallFilteringCompleted(boolean, boolean);
     method @Deprecated public void setAudioState(android.telecom.AudioState);
   }
 
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
index c6f42f7..a31cfae 100644
--- a/core/java/android/accessibilityservice/OWNERS
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -1,4 +1,4 @@
 svetoslavganov@google.com
 pweaver@google.com
 rhedjao@google.com
-qasid@google.com
+ryanlwlin@google.com
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f7f42a6..986051c 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -98,7 +98,7 @@
     public abstract void killForegroundAppsForUser(@UserIdInt int userId);
 
     /**
-     * Sets how long a {@link PendingIntent} can be temporarily whitelist to by bypass restrictions
+     * Sets how long a {@link PendingIntent} can be temporarily allowlisted to bypass restrictions
      * such as Power Save mode.
      * @param target
      * @param whitelistToken
@@ -132,9 +132,14 @@
 
     /**
      * Update information about which app IDs are on the temp whitelist.
+     * @param appids the updated list of appIds in temp allowlist.
+     * @param changingUid uid to add or remove to temp allowlist.
+     * @param adding true to add to temp allowlist, false to remove from temp allowlist.
+     * @param durationMs when adding is true, the duration to be in temp allowlist.
+     * @param type temp allowlist type defined at {@link BroadcastOptions.TempAllowListType}.
      */
-    public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId,
-            boolean adding);
+    public abstract void updateDeviceIdleTempWhitelist(int[] appids, int changingUid,
+            boolean adding, long durationMs, @BroadcastOptions.TempAllowListType int type);
 
     /**
      * Get the procstate for the UID.  The return value will be between
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index a23dd35..161b731 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1645,7 +1645,7 @@
      */
     private static int[] sOpToSwitch = new int[] {
             OP_COARSE_LOCATION,                 // COARSE_LOCATION
-            OP_COARSE_LOCATION,                 // FINE_LOCATION
+            OP_FINE_LOCATION,                   // FINE_LOCATION
             OP_COARSE_LOCATION,                 // GPS
             OP_VIBRATE,                         // VIBRATE
             OP_READ_CONTACTS,                   // READ_CONTACTS
diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java
new file mode 100644
index 0000000..045c55f
--- /dev/null
+++ b/core/java/android/content/pm/AppSearchPerson.java
@@ -0,0 +1,159 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Person;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.net.UriCodec;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class AppSearchPerson extends GenericDocument {
+
+    /** The name of the schema type for {@link Person} documents.*/
+    public static final String SCHEMA_TYPE = "Person";
+
+    public static final String KEY_NAME = "name";
+    public static final String KEY_KEY = "key";
+    public static final String KEY_IS_BOT = "isBot";
+    public static final String KEY_IS_IMPORTANT = "isImportant";
+
+    private AppSearchPerson(@NonNull GenericDocument document) {
+        super(document);
+    }
+
+    public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_NAME)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_KEY)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_BOT)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_IS_IMPORTANT)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).build();
+
+    /** hide */
+    @NonNull
+    public static AppSearchPerson instance(@NonNull final Person person) {
+        Objects.requireNonNull(person);
+        final String id;
+        if (person.getUri() != null) {
+            id = person.getUri();
+        } else {
+            // NOTE: an identifier is required even when uri is null.
+            id = UUID.randomUUID().toString();
+        }
+        return new Builder(id).setName(person.getName())
+                .setKey(person.getKey()).setIsBot(person.isBot())
+                .setIsImportant(person.isImportant()).build();
+    }
+
+    /** hide */
+    @NonNull
+    public Person toPerson() {
+        String uri;
+        try {
+            uri = UriCodec.decode(
+                    getUri(), false /* convertPlus */, StandardCharsets.UTF_8,
+                    true /* throwOnFailure */);
+        } catch (IllegalArgumentException e) {
+            uri = null;
+        }
+        return new Person.Builder().setName(getPropertyString(KEY_NAME))
+                .setUri(uri).setKey(getPropertyString(KEY_KEY))
+                .setBot(getPropertyBoolean(KEY_IS_BOT))
+                .setImportant(getPropertyBoolean(KEY_IS_IMPORTANT)).build();
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public static class Builder extends GenericDocument.Builder<Builder> {
+
+        public Builder(@NonNull final String id) {
+            super(id, SCHEMA_TYPE);
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setName(@Nullable final CharSequence name) {
+            if (name != null) {
+                setPropertyString(KEY_NAME, name.toString());
+            }
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setKey(@Nullable final String key) {
+            if (key != null) {
+                setPropertyString(KEY_KEY, key);
+            }
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setIsBot(final boolean isBot) {
+            setPropertyBoolean(KEY_IS_BOT, isBot);
+            return this;
+        }
+
+        /** @hide */
+        @NonNull
+        public Builder setIsImportant(final boolean isImportant) {
+            setPropertyBoolean(KEY_IS_IMPORTANT, isImportant);
+            return this;
+        }
+
+        @NonNull
+        @Override
+        public AppSearchPerson build() {
+            return new AppSearchPerson(super.build());
+        }
+    }
+}
diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java
new file mode 100644
index 0000000..14b8df8
--- /dev/null
+++ b/core/java/android/content/pm/AppSearchShortcutInfo.java
@@ -0,0 +1,616 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Person;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.LocusId;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public class AppSearchShortcutInfo extends GenericDocument {
+
+    /** The name of the schema type for {@link ShortcutInfo} documents.*/
+    public static final String SCHEMA_TYPE = "Shortcut";
+
+    public static final String KEY_PACKAGE_NAME = "packageName";
+    public static final String KEY_ACTIVITY = "activity";
+    public static final String KEY_TITLE = "title";
+    public static final String KEY_TEXT = "text";
+    public static final String KEY_DISABLED_MESSAGE = "disabledMessage";
+    public static final String KEY_CATEGORIES = "categories";
+    public static final String KEY_INTENTS = "intents";
+    public static final String KEY_INTENT_PERSISTABLE_EXTRAS = "intentPersistableExtras";
+    public static final String KEY_PERSON = "person";
+    public static final String KEY_LOCUS_ID = "locusId";
+    public static final String KEY_RANK = "rank";
+    public static final String KEY_EXTRAS = "extras";
+    public static final String KEY_FLAGS = "flags";
+    public static final String KEY_ICON_RES_ID = "iconResId";
+    public static final String KEY_ICON_RES_NAME = "iconResName";
+    public static final String KEY_ICON_URI = "iconUri";
+    public static final String KEY_BITMAP_PATH = "bitmapPath";
+    public static final String KEY_DISABLED_REASON = "disabledReason";
+
+    public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+            .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PACKAGE_NAME)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ACTIVITY)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TITLE)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TEXT)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_MESSAGE)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CATEGORIES)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENTS)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_INTENT_PERSISTABLE_EXTRAS)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_PERSON)
+                    .setSchemaType(AppSearchPerson.SCHEMA_TYPE)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_LOCUS_ID)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_RANK)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_EXTRAS)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FLAGS)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_ID)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_RES_NAME)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_ICON_URI)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BITMAP_PATH)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_NONE)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE)
+                    .build()
+
+            ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_DISABLED_REASON)
+                    .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_INT64)
+                    .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
+                    .setTokenizerType(AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN)
+                    .setIndexingType(AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS)
+                    .build()
+
+            ).build();
+
+    public AppSearchShortcutInfo(@NonNull GenericDocument document) {
+        super(document);
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public static AppSearchShortcutInfo instance(@NonNull final ShortcutInfo shortcutInfo) {
+        Objects.requireNonNull(shortcutInfo);
+        return new Builder(shortcutInfo.getId())
+                .setActivity(shortcutInfo.getActivity())
+                .setPackageName(shortcutInfo.getPackage())
+                .setTitle(shortcutInfo.getShortLabel())
+                .setText(shortcutInfo.getLongLabel())
+                .setDisabledMessage(shortcutInfo.getDisabledMessage())
+                .setCategories(shortcutInfo.getCategories())
+                .setIntents(shortcutInfo.getIntents())
+                .setRank(shortcutInfo.getRank())
+                .setExtras(shortcutInfo.getExtras())
+                .setCreationTimestampMillis(shortcutInfo.getLastChangedTimestamp())
+                .setFlags(shortcutInfo.getFlags())
+                .setIconResId(shortcutInfo.getIconResourceId())
+                .setIconResName(shortcutInfo.getIconResName())
+                .setBitmapPath(shortcutInfo.getBitmapPath())
+                .setIconUri(shortcutInfo.getIconUri())
+                .setDisabledReason(shortcutInfo.getDisabledReason())
+                .setPersons(shortcutInfo.getPersons())
+                .setLocusId(shortcutInfo.getLocusId())
+                .build();
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public ShortcutInfo toShortcutInfo() {
+        return toShortcutInfo(UserHandle.myUserId());
+    }
+
+    /**
+     * @hide
+     * TODO: This should be @SystemApi when AppSearchShortcutInfo unhides.
+     */
+    @NonNull
+    public ShortcutInfo toShortcutInfo(@UserIdInt final int userId) {
+        final String packageName = getPropertyString(KEY_PACKAGE_NAME);
+        final String activityString = getPropertyString(KEY_ACTIVITY);
+        final ComponentName activity = activityString == null
+                ? null : ComponentName.unflattenFromString(activityString);
+        // TODO: proper icon handling
+        // NOTE: bitmap based icons are currently saved in side-channel (see ShortcutBitmapSaver),
+        // re-creating Icon object at creation time implies turning this function into async since
+        // loading bitmap is I/O bound. Since ShortcutInfo#getIcon is already annotated with
+        // @hide and @UnsupportedAppUsage, we could migrate existing usage in platform with
+        // LauncherApps#getShortcutIconDrawable instead.
+        final Icon icon = null;
+        final String title = getPropertyString(KEY_TITLE);
+        final String text = getPropertyString(KEY_TEXT);
+        final String disabledMessage = getPropertyString(KEY_DISABLED_MESSAGE);
+        final String[] categories = getPropertyStringArray(KEY_CATEGORIES);
+        final Set<String> categoriesSet = categories == null
+                ? new ArraySet<>() : new ArraySet<>(Arrays.asList(categories));
+        final String[] intentsStrings = getPropertyStringArray(KEY_INTENTS);
+        final Intent[] intents = intentsStrings == null
+                ? null : Arrays.stream(intentsStrings).map(uri -> {
+                    try {
+                        return Intent.parseUri(uri, /* flags =*/ 0);
+                    } catch (URISyntaxException e) {
+                        // ignore malformed entry
+                    }
+                    return null;
+                }).toArray(Intent[]::new);
+        final byte[][] intentExtrasesBytes = getPropertyBytesArray(KEY_INTENT_PERSISTABLE_EXTRAS);
+        final Bundle[] intentExtrases = intentExtrasesBytes == null
+                ? null : Arrays.stream(intentExtrasesBytes)
+                .map(this::transformToBundle).toArray(Bundle[]::new);
+        if (intents != null) {
+            for (int i = 0; i < intents.length; i++) {
+                final Intent intent = intents[i];
+                if (intent != null) {
+                    intent.replaceExtras(intentExtrases[i].size() == 0 ? null : intentExtrases[i]);
+                }
+            }
+        }
+        final Person[] persons = parsePerson(getPropertyDocumentArray(KEY_PERSON));
+        final String locusIdString = getPropertyString(KEY_LOCUS_ID);
+        final LocusId locusId = locusIdString == null ? null : new LocusId(locusIdString);
+        final int rank = (int) getPropertyLong(KEY_RANK);
+        final byte[] extrasByte = getPropertyBytes(KEY_EXTRAS);
+        final PersistableBundle extras = transformToPersistableBundle(extrasByte);
+        final int flags = parseFlags(getPropertyLongArray(KEY_FLAGS));
+        final int iconResId = (int) getPropertyLong(KEY_ICON_RES_ID);
+        final String iconResName = getPropertyString(KEY_ICON_RES_NAME);
+        final String iconUri = getPropertyString(KEY_ICON_URI);
+        final String bitmapPath = getPropertyString(KEY_BITMAP_PATH);
+        final int disabledReason = (int) getPropertyLong(KEY_DISABLED_REASON);
+        return new ShortcutInfo(
+                userId, getUri(), packageName, activity, icon, title, 0, null,
+                text, 0, null, disabledMessage, 0, null,
+                categoriesSet, intents, rank, extras,
+                getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri,
+                disabledReason, persons, locusId);
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public static class Builder extends GenericDocument.Builder<Builder> {
+
+        public Builder(String id) {
+            super(id, SCHEMA_TYPE);
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setLocusId(@Nullable final LocusId locusId) {
+            if (locusId != null) {
+                setPropertyString(KEY_LOCUS_ID, locusId.getId());
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setActivity(@Nullable final ComponentName activity) {
+            if (activity != null) {
+                setPropertyString(KEY_ACTIVITY, activity.flattenToShortString());
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setTitle(@Nullable final CharSequence shortLabel) {
+            if (!TextUtils.isEmpty(shortLabel)) {
+                setPropertyString(KEY_TITLE, Preconditions.checkStringNotEmpty(
+                        shortLabel, "shortLabel cannot be empty").toString());
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setText(@Nullable final CharSequence longLabel) {
+            if (!TextUtils.isEmpty(longLabel)) {
+                setPropertyString(KEY_TEXT, Preconditions.checkStringNotEmpty(
+                        longLabel, "longLabel cannot be empty").toString());
+            }
+            return this;
+
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setDisabledMessage(@Nullable final CharSequence disabledMessage) {
+            if (!TextUtils.isEmpty(disabledMessage)) {
+                setPropertyString(KEY_DISABLED_MESSAGE, Preconditions.checkStringNotEmpty(
+                        disabledMessage, "disabledMessage cannot be empty").toString());
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setCategories(@Nullable final Set<String> categories) {
+            if (categories != null && !categories.isEmpty()) {
+                setPropertyString(KEY_CATEGORIES, categories.stream().toArray(String[]::new));
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setIntent(@Nullable final Intent intent) {
+            if (intent == null) {
+                return this;
+            }
+            return setIntents(new Intent[]{intent});
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setIntents(@Nullable final Intent[] intents) {
+            if (intents == null || intents.length == 0) {
+                return this;
+            }
+            for (Intent intent : intents) {
+                Objects.requireNonNull(intent, "intents cannot contain null");
+                Objects.requireNonNull(intent.getAction(), "intent's action must be set");
+            }
+            final byte[][] intentExtrases = new byte[intents.length][];
+            for (int i = 0; i < intents.length; i++) {
+                final Intent intent = intents[i];
+                final Bundle extras = intent.getExtras();
+                intentExtrases[i] = extras == null
+                        ? new byte[0] : transformToByteArray(new PersistableBundle(extras));
+            }
+
+            setPropertyString(KEY_INTENTS, Arrays.stream(intents).map(it ->
+                    it.toUri(0)).toArray(String[]::new));
+            setPropertyBytes(KEY_INTENT_PERSISTABLE_EXTRAS, intentExtrases);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setPerson(@Nullable final Person person) {
+            if (person == null) {
+                return this;
+            }
+            return setPersons(new Person[]{person});
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setPersons(@Nullable final Person[] persons) {
+            if (persons == null || persons.length == 0) {
+                return this;
+            }
+            setPropertyDocument(KEY_PERSON,
+                    Arrays.stream(persons).map(person -> AppSearchPerson.instance(
+                            Objects.requireNonNull(person, "persons cannot contain null"))
+                    ).toArray(AppSearchPerson[]::new));
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setRank(final int rank) {
+            Preconditions.checkArgument((0 <= rank),
+                    "Rank cannot be negative or bigger than MAX_RANK");
+            setPropertyLong(KEY_RANK, rank);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setExtras(@Nullable final PersistableBundle extras) {
+            if (extras != null) {
+                setPropertyBytes(KEY_EXTRAS, transformToByteArray(extras));
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setPackageName(@Nullable final String packageName) {
+            if (!TextUtils.isEmpty(packageName)) {
+                setPropertyString(KEY_PACKAGE_NAME, packageName);
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setFlags(@ShortcutInfo.ShortcutFlags final int flags) {
+            setPropertyLong(KEY_FLAGS, flattenFlags(flags));
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        public Builder setIconResId(@Nullable final int iconResId) {
+            setPropertyLong(KEY_ICON_RES_ID, iconResId);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setIconResName(@Nullable final String iconResName) {
+            if (!TextUtils.isEmpty(iconResName)) {
+                setPropertyString(KEY_ICON_RES_NAME, iconResName);
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setBitmapPath(@Nullable final String bitmapPath) {
+            if (!TextUtils.isEmpty(bitmapPath)) {
+                setPropertyString(KEY_BITMAP_PATH, bitmapPath);
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setIconUri(@Nullable final String iconUri) {
+            if (!TextUtils.isEmpty(iconUri)) {
+                setPropertyString(KEY_ICON_URI, iconUri);
+            }
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setDisabledReason(@ShortcutInfo.DisabledReason final int disabledReason) {
+            setPropertyLong(KEY_DISABLED_REASON, disabledReason);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
+        @NonNull
+        @Override
+        public AppSearchShortcutInfo build() {
+            return new AppSearchShortcutInfo(super.build());
+        }
+    }
+
+    /**
+     * Convert PersistableBundle into byte[] for persistence.
+     */
+    @Nullable
+    private static byte[] transformToByteArray(@NonNull final PersistableBundle extras) {
+        Objects.requireNonNull(extras);
+        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+            new PersistableBundle(extras).writeToStream(baos);
+            return baos.toByteArray();
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Convert byte[] into Bundle.
+     */
+    @Nullable
+    private Bundle transformToBundle(@Nullable final byte[] extras) {
+        if (extras == null) {
+            return null;
+        }
+        Objects.requireNonNull(extras);
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) {
+            final Bundle ret = new Bundle();
+            ret.putAll(PersistableBundle.readFromStream(bais));
+            return ret;
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Convert byte[] into PersistableBundle.
+     */
+    @Nullable
+    private PersistableBundle transformToPersistableBundle(@Nullable final byte[] extras) {
+        if (extras == null) {
+            return null;
+        }
+        try (ByteArrayInputStream bais = new ByteArrayInputStream(extras)) {
+            return PersistableBundle.readFromStream(bais);
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    private static long[] flattenFlags(@ShortcutInfo.ShortcutFlags final int flags) {
+        final List<Integer> flattenedFlags = new ArrayList<>();
+        flattenedFlags.add(0);
+        for (int i = 0; i < 31; i++) {
+            final int mask = 1 << i;
+            if ((flags & mask) != 0) {
+                flattenedFlags.add(mask);
+            }
+        }
+        return flattenedFlags.stream().mapToLong(i -> i).toArray();
+    }
+
+    private static int parseFlags(final long[] flags) {
+        return (int) Arrays.stream(flags).reduce((p, v) -> p | v).getAsLong();
+    }
+
+    @NonNull
+    private static Person[] parsePerson(@Nullable final GenericDocument[] persons) {
+        return persons == null ? new Person[0] : Arrays.stream(persons).map(it ->
+                ((AppSearchPerson) it).toPerson()).toArray(Person[]::new);
+    }
+}
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 9a73be9..f72288c 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -35,7 +35,7 @@
     void write(String name, long offsetBytes, long lengthBytes, in ParcelFileDescriptor fd);
     void stageViaHardLink(String target);
 
-    void addChecksums(String name, in Checksum[] checksums);
+    void setChecksums(String name, in Checksum[] checksums, in byte[] signature);
 
     void removeSplit(String splitName);
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 01d4c280..248be0f 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1247,12 +1247,15 @@
         }
 
         /**
-         * Adds installer-provided checksums for the APK file in session.
+         * Sets installer-provided checksums for the APK file in session.
          *
          * @param name      previously written as part of this session.
          *                  {@link #openWrite}
          * @param checksums installer intends to make available via
          *                  {@link PackageManager#requestChecksums}.
+         * @param signature PKCS#7 detached signature bytes over serialized checksums to enable
+         *                  fs-verity for the checksums or null if fs-verity should not be enabled.
+         *                  @see <a href="https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification">fs-verity</a>
          * @throws SecurityException if called after the session has been
          *                           committed or abandoned.
          * @throws IllegalStateException if checksums for this file have already been added.
@@ -1262,13 +1265,14 @@
          *              in {@link PackageManager#requestChecksums}.
          */
         @Deprecated
-        public void addChecksums(@NonNull String name, @NonNull List<Checksum> checksums)
-                throws IOException {
+        public void setChecksums(@NonNull String name, @NonNull List<Checksum> checksums,
+                @Nullable byte[] signature) throws IOException {
             Objects.requireNonNull(name);
             Objects.requireNonNull(checksums);
 
             try {
-                mSession.addChecksums(name, checksums.toArray(new Checksum[checksums.size()]));
+                mSession.setChecksums(name, checksums.toArray(new Checksum[checksums.size()]),
+                        signature);
             } catch (RuntimeException e) {
                 ExceptionUtils.maybeUnwrapIOException(e);
                 throw e;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index da75fba..522f4ca 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -2191,7 +2191,7 @@
         dest.writeString8(mIconUri);
     }
 
-    public static final @android.annotation.NonNull Creator<ShortcutInfo> CREATOR =
+    public static final @NonNull Creator<ShortcutInfo> CREATOR =
             new Creator<ShortcutInfo>() {
                 public ShortcutInfo createFromParcel(Parcel source) {
                     return new ShortcutInfo(source);
diff --git a/core/java/android/net/vcn/IVcnManagementService.aidl b/core/java/android/net/vcn/IVcnManagementService.aidl
index 04b585c..80ac64b 100644
--- a/core/java/android/net/vcn/IVcnManagementService.aidl
+++ b/core/java/android/net/vcn/IVcnManagementService.aidl
@@ -16,6 +16,7 @@
 
 package android.net.vcn;
 
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
 import android.os.ParcelUuid;
 
@@ -25,4 +26,7 @@
 interface IVcnManagementService {
     void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
     void clearVcnConfig(in ParcelUuid subscriptionGroup);
+
+    void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
+    void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener);
 }
diff --git a/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
new file mode 100644
index 0000000..f8ae492
--- /dev/null
+++ b/core/java/android/net/vcn/IVcnUnderlyingNetworkPolicyListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.net.vcn;
+
+/** @hide */
+interface IVcnUnderlyingNetworkPolicyListener {
+    void onPolicyChanged();
+}
\ No newline at end of file
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index b881a339..2ccdc26 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -25,7 +25,12 @@
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
 
 /**
  * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks.
@@ -60,6 +65,11 @@
 public final class VcnManager {
     @NonNull private static final String TAG = VcnManager.class.getSimpleName();
 
+    @VisibleForTesting
+    public static final Map<
+                    VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
+            REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
+
     @NonNull private final Context mContext;
     @NonNull private final IVcnManagementService mService;
 
@@ -136,4 +146,101 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    // TODO: make VcnUnderlyingNetworkPolicyListener @SystemApi
+    /**
+     * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components
+     * can register to receive updates for VCN-underlying Network policies from the System Server.
+     *
+     * @hide
+     */
+    public interface VcnUnderlyingNetworkPolicyListener {
+        /**
+         * Notifies the implementation that the VCN's underlying Network policy has changed.
+         *
+         * <p>After receiving this callback, implementations MUST poll VcnManager for the updated
+         * VcnUnderlyingNetworkPolicy via VcnManager#getUnderlyingNetworkPolicy.
+         */
+        void onPolicyChanged();
+    }
+
+    /**
+     * Add a listener for VCN-underlying network policy updates.
+     *
+     * @param executor the Executor that will be used for invoking all calls to the specified
+     *     Listener
+     * @param listener the VcnUnderlyingNetworkPolicyListener to be added
+     * @throws SecurityException if the caller does not have permission NETWORK_FACTORY
+     * @throws IllegalArgumentException if the specified VcnUnderlyingNetworkPolicyListener is
+     *     already registered
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    public void addVcnUnderlyingNetworkPolicyListener(
+            @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) {
+        requireNonNull(executor, "executor must not be null");
+        requireNonNull(listener, "listener must not be null");
+
+        VcnUnderlyingNetworkPolicyListenerBinder binder =
+                new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener);
+        if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) {
+            throw new IllegalArgumentException(
+                    "Attempting to add a listener that is already in use");
+        }
+
+        try {
+            mService.addVcnUnderlyingNetworkPolicyListener(binder);
+        } catch (RemoteException e) {
+            REGISTERED_POLICY_LISTENERS.remove(listener);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager.
+     *
+     * <p>If the specified listener is not currently registered, this is a no-op.
+     *
+     * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed
+     * @hide
+     */
+    public void removeVcnUnderlyingNetworkPolicyListener(
+            @NonNull VcnUnderlyingNetworkPolicyListener listener) {
+        requireNonNull(listener, "listener must not be null");
+
+        VcnUnderlyingNetworkPolicyListenerBinder binder =
+                REGISTERED_POLICY_LISTENERS.remove(listener);
+        if (binder == null) {
+            return;
+        }
+
+        try {
+            mService.removeVcnUnderlyingNetworkPolicyListener(binder);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System
+     * Server.
+     *
+     * @hide
+     */
+    private static class VcnUnderlyingNetworkPolicyListenerBinder
+            extends IVcnUnderlyingNetworkPolicyListener.Stub {
+        @NonNull private final Executor mExecutor;
+        @NonNull private final VcnUnderlyingNetworkPolicyListener mListener;
+
+        private VcnUnderlyingNetworkPolicyListenerBinder(
+                Executor executor, VcnUnderlyingNetworkPolicyListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onPolicyChanged() {
+            mExecutor.execute(() -> mListener.onPolicyChanged());
+        }
+    }
 }
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
new file mode 100644
index 0000000..6cb6ee6
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.net.vcn;
+
+/** @hide */
+parcelable VcnUnderlyingNetworkPolicy;
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
new file mode 100644
index 0000000..dd7c86d8
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.vcn;
+
+import android.annotation.NonNull;
+import android.net.NetworkCapabilities;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * VcnUnderlyingNetworkPolicy represents the Network policy for a VCN-managed Network.
+ *
+ * <p>Transports that are bringing up networks capable of acting as a VCN's underlying network
+ * should query for policy state upon major capability changes (e.g. changing of TRUSTED bit), and
+ * when prompted by VcnManagementService via VcnUnderlyingNetworkPolicyListener.
+ *
+ * @hide
+ */
+public final class VcnUnderlyingNetworkPolicy implements Parcelable {
+    private final boolean mIsTearDownRequested;
+    private final NetworkCapabilities mMergedNetworkCapabilities;
+
+    /**
+     * Constructs a VcnUnderlyingNetworkPolicy with the specified parameters.
+     *
+     * @hide
+     */
+    public VcnUnderlyingNetworkPolicy(
+            boolean isTearDownRequested, @NonNull NetworkCapabilities mergedNetworkCapabilities) {
+        Objects.requireNonNull(
+                mergedNetworkCapabilities, "mergedNetworkCapabilities must be nonnull");
+
+        mIsTearDownRequested = isTearDownRequested;
+        mMergedNetworkCapabilities = mergedNetworkCapabilities;
+    }
+
+    /**
+     * Returns whether this Carrier VCN policy policy indicates that the underlying Network should
+     * be torn down.
+     */
+    public boolean isTeardownRequested() {
+        return mIsTearDownRequested;
+    }
+
+    /**
+     * Returns the NetworkCapabilities with Carrier VCN policy bits merged into the provided
+     * capabilities.
+     */
+    @NonNull
+    public NetworkCapabilities getMergedNetworkCapabilities() {
+        return mMergedNetworkCapabilities;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mIsTearDownRequested, mMergedNetworkCapabilities);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof VcnUnderlyingNetworkPolicy)) return false;
+        final VcnUnderlyingNetworkPolicy that = (VcnUnderlyingNetworkPolicy) o;
+
+        return mIsTearDownRequested == that.mIsTearDownRequested
+                && mMergedNetworkCapabilities.equals(that.mMergedNetworkCapabilities);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeBoolean(mIsTearDownRequested);
+        dest.writeParcelable(mMergedNetworkCapabilities, flags);
+    }
+
+    /** Implement the Parcelable interface */
+    public static final @NonNull Creator<VcnUnderlyingNetworkPolicy> CREATOR =
+            new Creator<VcnUnderlyingNetworkPolicy>() {
+                public VcnUnderlyingNetworkPolicy createFromParcel(Parcel in) {
+                    return new VcnUnderlyingNetworkPolicy(
+                            in.readBoolean(), in.readParcelable(null));
+                }
+
+                public VcnUnderlyingNetworkPolicy[] newArray(int size) {
+                    return new VcnUnderlyingNetworkPolicy[size];
+                }
+            };
+}
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 59292baa..9fdc72b 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,6 +36,7 @@
 import android.content.Context;
 import android.content.pm.DataLoaderParams;
 import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.IPackageLoadingProgressCallback;
 import android.content.pm.InstallationFileParcel;
 import android.text.TextUtils;
 
@@ -70,7 +71,8 @@
             @Nullable StorageHealthCheckParams healthCheckParams,
             @Nullable IStorageHealthListener healthListener,
             @NonNull List<InstallationFileParcel> addedFiles,
-            @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
+            @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
+            IPackageLoadingProgressCallback progressCallback) throws IOException {
         // TODO(b/136132412): validity check if session should not be incremental
         IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
                 Context.INCREMENTAL_SERVICE);
@@ -95,7 +97,11 @@
                 throw new IOException("Unknown file location: " + file.location);
             }
         }
-
+        // Register progress loading callback after files have been added
+        if (progressCallback != null) {
+            incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
+                    progressCallback);
+        }
         result.startLoading();
 
         return result;
@@ -180,6 +186,7 @@
 
         try {
             mDefaultStorage.unBind(mStageDir.getAbsolutePath());
+            mDefaultStorage.unregisterLoadingProgressListener();
         } catch (IOException ignored) {
         }
         mDefaultStorage = null;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7e0ebbc..f24fbab 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -9154,14 +9154,14 @@
      * Handles an inbound request for scroll capture from the system. If a client is not already
      * active, a search will be dispatched through the view tree to locate scrolling content.
      * <p>
-     * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect,
+     * Either {@link IScrollCaptureCallbacks#onConnected(IScrollCaptureConnection, Rect,
      * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned
      * depending on the results of the search.
      *
      * @param callbacks to receive responses
      * @see ScrollCaptureTargetResolver
      */
-    private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+    public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
         LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>();
 
         // Window (root) level callbacks
@@ -9169,10 +9169,12 @@
 
         // Search through View-tree
         View rootView = getView();
-        Point point = new Point();
-        Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
-        getChildVisibleRect(rootView, rect, point);
-        rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+        if (rootView != null) {
+            Point point = new Point();
+            Rect rect = new Rect(0, 0, rootView.getWidth(), rootView.getHeight());
+            getChildVisibleRect(rootView, rect, point);
+            rootView.dispatchScrollCaptureSearch(rect, point, targetList);
+        }
 
         // No-op path. Scroll capture not offered for this window.
         if (targetList.isEmpty()) {
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
index 93b5a2e..b1d3967 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -9,3 +9,4 @@
 ogunwale@google.com
 jjaggi@google.com
 pweaver@google.com
+ryanlwlin@google.com
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index b9ff26b..6281ee9 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -39,6 +39,7 @@
 import android.view.inspector.InspectableProperty;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
@@ -776,17 +777,23 @@
 
     /**
      * Grows {@code r} from its center such that each dimension is at least {@code minimumSize}.
+     *
+     * The result will still have the same {@link Rect#centerX()} and {@link Rect#centerY()} as the
+     * input.
+     *
+     * @hide
      */
-    private void growRectTo(Rect r, int minimumSize) {
-        int dy = (minimumSize - r.height()) / 2;
+    @VisibleForTesting
+    public void growRectTo(Rect r, int minimumSize) {
+        int dy = minimumSize - r.height();
         if (dy > 0) {
-            r.top -= dy;
-            r.bottom += dy;
+            r.top -= (dy + 1) / 2;
+            r.bottom += dy / 2;
         }
-        int dx = (minimumSize - r.width()) / 2;
+        int dx = minimumSize - r.width();
         if (dx > 0) {
-            r.left -= dx;
-            r.right += dx;
+            r.left -= (dx + 1) / 2;
+            r.right += dx / 2;
         }
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 33aa190..f105320 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,6 +19,8 @@
 import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
 import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
 
+import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -103,7 +105,6 @@
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
 import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader;
-import com.android.internal.power.MeasuredEnergyArray;
 import com.android.internal.power.MeasuredEnergyStats;
 import com.android.internal.power.MeasuredEnergyStats.EnergyBucket;
 import com.android.internal.util.ArrayUtils;
@@ -370,14 +371,6 @@
          * @param railStats
          */
         void fillRailDataStats(RailStats railStats);
-        /**
-         * Function to get energy consumption data
-         *
-         * @return an array of measured energy (in microjoules) since boot, will be null if
-         * measured energy data is unavailable
-         */
-        @Nullable
-        MeasuredEnergyArray getEnergyConsumptionData();
     }
 
     public static abstract class UserInfoProvider {
@@ -10704,15 +10697,13 @@
     }
 
     public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb,
-            MeasuredEnergyRetriever energyStatsCb, boolean[] supportedEnergyBuckets,
-            UserInfoProvider userInfoProvider) {
-        this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, supportedEnergyBuckets,
-                userInfoProvider);
+            MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) {
+        this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider);
     }
 
     private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler,
             PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb,
-            boolean[] supportedEnergyBuckets, UserInfoProvider userInfoProvider) {
+            UserInfoProvider userInfoProvider) {
         init(clocks);
 
         if (systemDir == null) {
@@ -10818,10 +10809,6 @@
         // Notify statsd that the system is initially not in doze.
         mDeviceIdleMode = DEVICE_IDLE_MODE_OFF;
         FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode);
-
-        mGlobalMeasuredEnergyStats = supportedEnergyBuckets == null ? null :
-                new MeasuredEnergyStats(supportedEnergyBuckets);
-        mScreenStateAtLastEnergyMeasurement = mScreenState;
     }
 
     @UnsupportedAppUsage
@@ -12503,21 +12490,6 @@
     }
 
     /**
-     * Get energy consumed (in microjoules) by a set of subsystems from the {@link
-     * MeasuredEnergyRetriever}, if available.
-     *
-     * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is
-     * unavailable.
-     */
-    @Nullable
-    public MeasuredEnergyArray getEnergyConsumptionDataLocked() {
-        if (mMeasuredEnergyRetriever == null) {
-            return null;
-        }
-        return mMeasuredEnergyRetriever.getEnergyConsumptionData();
-    }
-
-    /**
      * Read and distribute kernel wake lock use across apps.
      */
     public void updateKernelWakelocksLocked() {
@@ -14240,6 +14212,40 @@
         mConstants.startObserving(context.getContentResolver());
         registerUsbStateReceiver(context);
     }
+    /**
+     * Initialize the measured energy stats data structures.
+     *
+     * @param supportedEnergyBuckets boolean array indicating which buckets are currently supported
+     */
+    @GuardedBy("this")
+    public void initMeasuredEnergyStatsLocked(boolean[] supportedEnergyBuckets) {
+        boolean supportedBucketMismatch = false;
+        mScreenStateAtLastEnergyMeasurement = mScreenState;
+
+        if (supportedEnergyBuckets == null) {
+            if (mGlobalMeasuredEnergyStats != null) {
+                // Measured energy buckets no longer supported, wipe out the existing data.
+                supportedBucketMismatch = true;
+            }
+        } else if (mGlobalMeasuredEnergyStats == null) {
+            mGlobalMeasuredEnergyStats = new MeasuredEnergyStats(supportedEnergyBuckets);
+            return;
+        } else {
+            for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+                if (mGlobalMeasuredEnergyStats.isEnergyBucketSupported(i)
+                        != supportedEnergyBuckets[i]) {
+                    supportedBucketMismatch = true;
+                    break;
+                }
+            }
+        }
+
+        if (supportedBucketMismatch) {
+            // Supported energy buckets changed since last boot.
+            // Existing data is no longer reliable.
+            resetAllStatsLocked(SystemClock.uptimeMillis(), SystemClock.elapsedRealtime());
+        }
+    }
 
     @VisibleForTesting
     public final class Constants extends ContentObserver {
@@ -14919,7 +14925,11 @@
         mNextMaxDailyDeadlineMs = in.readLong();
         mBatteryTimeToFullSeconds = in.readLong();
 
-        MeasuredEnergyStats.readSummaryFromParcel(mGlobalMeasuredEnergyStats, in);
+        /**
+         * WARNING: Supported buckets may have changed across boots. Bucket mismatch is handled
+         *          later when {@link #initMeasuredEnergyStatsLocked} is called.
+         */
+        mGlobalMeasuredEnergyStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(in);
 
         mStartCount++;
 
@@ -15417,7 +15427,7 @@
         out.writeLong(mNextMaxDailyDeadlineMs);
         out.writeLong(mBatteryTimeToFullSeconds);
 
-        MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out);
+        MeasuredEnergyStats.writeSummaryToParcel(mGlobalMeasuredEnergyStats, out, false);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -15742,7 +15752,7 @@
                 out.writeInt(0);
             }
 
-            MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out);
+            MeasuredEnergyStats.writeSummaryToParcel(u.mUidMeasuredEnergyStats, out, true);
 
             final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap();
             int NW = wakeStats.size();
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index b644df4..b744a5d 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -130,12 +130,16 @@
      * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary
      *       parceling changes.
      */
-    private void readSummaryFromParcel(Parcel in) {
+    private void readSummaryFromParcel(Parcel in, boolean overwriteAvailability) {
         final int size = in.readInt();
         for (int i = 0; i < size; i++) {
             final int bucket = in.readInt();
             final long energyUJ = in.readLong();
-            setValueIfSupported(bucket, energyUJ);
+            if (overwriteAvailability) {
+                mAccumulatedEnergiesMicroJoules[bucket] = energyUJ;
+            } else {
+                setValueIfSupported(bucket, energyUJ);
+            }
         }
     }
 
@@ -143,14 +147,15 @@
      * Write to summary parcel.
      * Note: Measured subsystem availability may be different when the summary parcel is read.
      */
-    private void writeSummaryToParcel(Parcel out) {
+    private void writeSummaryToParcel(Parcel out, boolean skipZero) {
         final int sizePos = out.dataPosition();
         out.writeInt(0);
         int size = 0;
         // Write only the supported buckets with non-zero energy.
         for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
             final long energy = mAccumulatedEnergiesMicroJoules[i];
-            if (energy <= 0) continue;
+            if (energy < 0) continue;
+            if (energy == 0 && skipZero) continue;
 
             out.writeInt(i);
             out.writeLong(mAccumulatedEnergiesMicroJoules[i]);
@@ -197,16 +202,19 @@
     }
 
     /**
-     * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and
-     * ignore the parcelled data.
+     * Create a MeasuredEnergyStats object from a summary parcel.
+     *
+     * @return a new MeasuredEnergyStats object as described.
+     *         Returns null if the parcel indicates there is no data to populate.
      */
-    public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) {
+    public static @Nullable MeasuredEnergyStats createAndReadSummaryFromParcel(Parcel in) {
         // Check if any MeasuredEnergyStats exists on the parcel
-        if (in.readInt() == 0) return;
+        if (in.readInt() == 0) return null;
 
-        // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data
-        final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats();
-        mes.readSummaryFromParcel(in);
+        final MeasuredEnergyStats stats =
+                new MeasuredEnergyStats(new boolean[NUMBER_ENERGY_BUCKETS]);
+        stats.readSummaryFromParcel(in, true);
+        return stats;
     }
 
     /**
@@ -227,12 +235,12 @@
         if (template == null) {
             // Nothing supported now. Create placeholder object just to consume the parcel data.
             final MeasuredEnergyStats mes = new MeasuredEnergyStats();
-            mes.readSummaryFromParcel(in);
+            mes.readSummaryFromParcel(in, false);
             return null;
         }
 
         final MeasuredEnergyStats stats = createFromTemplate(template);
-        stats.readSummaryFromParcel(in);
+        stats.readSummaryFromParcel(in, false);
         if (stats.containsInterestingData()) {
             return stats;
         } else {
@@ -253,13 +261,13 @@
      * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0.
      */
     public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats,
-            Parcel dest) {
+            Parcel dest, boolean skipZero) {
         if (stats == null) {
             dest.writeInt(0);
             return;
         }
         dest.writeInt(1);
-        stats.writeSummaryToParcel(dest);
+        stats.writeSummaryToParcel(dest, skipZero);
     }
 
     /** Reset accumulated energy. */
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index fa046c6..d3c2d31 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -626,6 +626,7 @@
         optional int32 target_uid = 1;
         optional int64 duration_ms = 2;
         optional string tag = 3;
+        optional int32 type = 4;
     }
     repeated PendingTempWhitelist pending_temp_whitelist = 26;
 
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
new file mode 100644
index 0000000..1ff88f7
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AppSearchPersonTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+
+import org.junit.Test;
+
+public class AppSearchPersonTest {
+
+    @Test
+    public void testBuildPersonAndGetValue() {
+        final String name = "name";
+        final String key = "key";
+        final String uri = "name:name";
+
+        final Person person = new AppSearchPerson.Builder(uri)
+                .setName(name)
+                .setKey(key)
+                .setIsBot(true)
+                .setIsImportant(false)
+                .build()
+                .toPerson();
+
+        assertThat(person.getName()).isEqualTo(name);
+        assertThat(person.getKey()).isEqualTo(key);
+        assertThat(person.getUri()).isEqualTo(uri);
+        assertThat(person.isBot()).isTrue();
+        assertThat(person.isImportant()).isFalse();
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
new file mode 100644
index 0000000..da92e69
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AppSearchShortcutInfoTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Person;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.util.ArraySet;
+
+import org.junit.Test;
+
+import java.util.Set;
+
+public class AppSearchShortcutInfoTest {
+
+    @Test
+    public void testBuildShortcutAndGetValue() {
+        final String category =
+                "android.app.stubs.SHARE_SHORTCUT_CATEGORY";
+        final String id = "shareShortcut";
+        final String shortcutIconResName = "shortcut";
+        final ComponentName activity = new ComponentName("xxx", "s");
+        final Person person = new Person.Builder()
+                .setBot(false)
+                .setName("BubbleBot")
+                .setImportant(true)
+                .build();
+
+        final Set<String> categorySet = new ArraySet<>();
+        categorySet.add(category);
+        final Intent shortcutIntent = new Intent(Intent.ACTION_VIEW);
+        final ShortcutInfo shortcut = new AppSearchShortcutInfo.Builder(id)
+                .setActivity(activity)
+                .setText(id)
+                .setIconResName(shortcutIconResName)
+                .setIntent(shortcutIntent)
+                .setPerson(person)
+                .setCategories(categorySet)
+                .setFlags(ShortcutInfo.FLAG_LONG_LIVED)
+                .build()
+                .toShortcutInfo();
+
+        assertThat(shortcut.getId()).isEqualTo(id);
+        assertThat(shortcut.getShortLabel()).isEqualTo(id);
+        assertThat(shortcut.getIconResName()).isEqualTo(shortcutIconResName);
+        assertThat(shortcut.getIntent().toString()).isEqualTo(shortcut.toString());
+        assertThat(shortcut.getPersons().length).isEqualTo(1);
+        assertThat(shortcut.getPersons()[0]).isEqualTo(person);
+        assertThat(shortcut.getCategories()).isEqualTo(categorySet);
+        assertThat(shortcut.getFlags()).isEqualTo(ShortcutInfo.FLAG_LONG_LIVED);
+        assertThat(shortcut.getActivity()).isEqualTo(activity);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f371a7f..f171ab4 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -35,6 +35,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.content.Context;
 import android.os.Binder;
@@ -50,6 +51,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for {@link ViewRootImpl}
  *
@@ -196,6 +200,26 @@
     }
 
     /**
+     * Ensure scroll capture request handles a ViewRootImpl with no view tree.
+     */
+    @Test
+    public void requestScrollCapture_withoutContentRoot() {
+        final CountDownLatch latch = new CountDownLatch(1);
+        mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+            @Override
+            public void onUnavailable() {
+                latch.countDown();
+            }
+        });
+        try {
+            if (latch.await(100, TimeUnit.MILLISECONDS)) {
+                return; // pass
+            }
+        } catch (InterruptedException e) { /* ignore */ }
+        fail("requestScrollCapture did not respond");
+    }
+
+    /**
      * When window doesn't have focus, keys should be dropped.
      */
     @Test
diff --git a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
index 5371a0f..ccd873d 100644
--- a/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
+++ b/core/tests/coretests/src/android/widget/AbsSeekBarTest.java
@@ -30,7 +30,6 @@
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RectShape;
 import android.platform.test.annotations.Presubmit;
-import android.view.View;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.platform.app.InstrumentationRegistry;
@@ -48,6 +47,7 @@
 @Presubmit
 public class AbsSeekBarTest {
 
+    public static final int PADDING = 10;
     private Context mContext;
     private AbsSeekBar mBar;
 
@@ -59,34 +59,42 @@
 
     @Test
     public void testExclusionForThumb_limitedTo48dp() {
-        mBar.setPadding(10, 10, 10, 10);
+        mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
         mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
         mBar.setProgress(50);
+
+        final int thumbOffset = mBar.getThumbOffset();
+
         measureAndLayout(dpToPxSize(200), dpToPxSize(100));
         List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
 
         assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
         assertEquals("exclusion should be centered on thumb",
-                center(mBar), center(exclusions.get(0)));
+                center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)),
+                center(exclusions.get(0)));
         assertEquals("exclusion should be 48dp high", dpToPxSize(48), exclusions.get(0).height());
         assertEquals("exclusion should be 48dp wide", dpToPxSize(48), exclusions.get(0).width());
     }
 
     @Test
     public void testExclusionForThumb_limitedToHeight() {
-        mBar.setPadding(10, 10, 10, 10);
+        mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
         mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
         mBar.setProgress(50);
+
+        final int thumbOffset = mBar.getThumbOffset();
+
         measureAndLayout(dpToPxSize(200), dpToPxSize(32));
         List<Rect> exclusions = mBar.getSystemGestureExclusionRects();
 
         assertEquals("exclusions should be size 1, but was " + exclusions, 1, exclusions.size());
         assertEquals("exclusion should be centered on thumb",
-                center(mBar), center(exclusions.get(0)));
+                center(offset(mBar.getThumb().getBounds(), PADDING - thumbOffset, PADDING)),
+                center(exclusions.get(0)));
         assertEquals("exclusion should be 32dp high", dpToPxSize(32), exclusions.get(0).height());
         assertEquals("exclusion should be 32dp wide", dpToPxSize(32), exclusions.get(0).width());
     }
@@ -95,7 +103,7 @@
     public void testExclusionForThumb_passesThroughUserExclusions() {
         mBar.setSystemGestureExclusionRects(Arrays.asList(new Rect(1, 2, 3, 4)));
 
-        mBar.setPadding(10, 10, 10, 10);
+        mBar.setPadding(PADDING, PADDING, PADDING, PADDING);
         mBar.setThumb(newThumb(dpToPxSize(20)));
         mBar.setMin(0);
         mBar.setMax(100);
@@ -110,12 +118,37 @@
         assertThat(mBar.getSystemGestureExclusionRects(), hasSize(2));
     }
 
+    @Test
+    public void testGrowRectTo_evenInitialDifference() {
+        doGrowRectTest(new Rect(0, 0, 0, 0), 10, new Rect(-5, -5, 5, 5));
+    }
+
+    @Test
+    public void testGrowRectTo_unevenInitialDifference() {
+        doGrowRectTest(new Rect(0, 0, 1, 1), 10, new Rect(-5, -5, 5, 5));
+    }
+
+    @Test
+    public void testGrowRectTo_unevenInitialDifference_unevenSize() {
+        doGrowRectTest(new Rect(0, 0, 0, 0), 9, new Rect(-5, -5, 4, 4));
+    }
+
+    public void doGrowRectTest(Rect in, int minimumSize, Rect expected) {
+        Rect result = new Rect(in);
+        mBar.growRectTo(result, minimumSize);
+
+        assertEquals("grown rect", expected, result);
+        assertEquals("grown rect center point", center(expected), center(result));
+    }
+
     private Point center(Rect rect) {
         return new Point(rect.centerX(), rect.centerY());
     }
 
-    private Point center(View view) {
-        return center(new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
+    private Rect offset(Rect rect, int dx, int dy) {
+        Rect result = new Rect(rect);
+        result.offset(dx, dy);
+        return result;
     }
 
     private ShapeDrawable newThumb(int size) {
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index 2ede751..c9c81ac 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -34,6 +34,8 @@
 
 import org.junit.Test;
 
+import java.util.Arrays;
+
 /**
  * Test class for {@link MeasuredEnergyStats}.
  *
@@ -114,7 +116,7 @@
     }
 
     @Test
-    public void testReadWriteSummaryParcel() {
+    public void testCreateAndReadSummaryFromParcel() {
         final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
         supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
         supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
@@ -126,35 +128,21 @@
         stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 40, true);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
-
-
-        final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = true; // switched from false to true
-        newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_OTHER] = false; // switched true to false
-        MeasuredEnergyStats newStats = new MeasuredEnergyStats(newSupportedEnergyBuckets);
+        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
         parcel.setDataPosition(0);
-        MeasuredEnergyStats.readSummaryFromParcel(newStats, parcel);
+        MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(parcel);
 
         for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
-            if (!newSupportedEnergyBuckets[i]) {
-                assertFalse(newStats.isEnergyBucketSupported(i));
-                assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
-            } else if (!supportedEnergyBuckets[i]) {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
-            } else {
-                assertTrue(newStats.isEnergyBucketSupported(i));
-                assertEquals(stats.getAccumulatedBucketEnergy(i),
-                        newStats.getAccumulatedBucketEnergy(i));
-            }
+            assertEquals(stats.isEnergyBucketSupported(i),
+                    newStats.isEnergyBucketSupported(i));
+            assertEquals(stats.getAccumulatedBucketEnergy(i),
+                    newStats.getAccumulatedBucketEnergy(i));
         }
         parcel.recycle();
     }
 
     @Test
-    public void testCreateAndReadSummaryFromParcel() {
+    public void testCreateAndReadSummaryFromParcel_existingTemplate() {
         final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
         supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
         supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_DOZE] = false;
@@ -171,7 +159,7 @@
         stats.updateBucket(ENERGY_BUCKET_SCREEN_OTHER, 63, true);
 
         final Parcel parcel = Parcel.obtain();
-        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel);
+        MeasuredEnergyStats.writeSummaryToParcel(stats, parcel, false);
 
         final boolean[] newSupportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
         newSupportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
@@ -200,6 +188,55 @@
     }
 
     @Test
+    public void testCreateAndReadSummaryFromParcel_skipZero() {
+        final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
+        Arrays.fill(supportedEnergyBuckets, true);
+
+        final MeasuredEnergyStats stats = new MeasuredEnergyStats(supportedEnergyBuckets);
+        // Accumulate energy in one bucket, the rest should be zero
+        stats.updateBucket(ENERGY_BUCKET_SCREEN_ON, 200, true);
+
+        final Parcel includeZerosParcel = Parcel.obtain();
+        MeasuredEnergyStats.writeSummaryToParcel(stats, includeZerosParcel, false);
+        includeZerosParcel.setDataPosition(0);
+
+        MeasuredEnergyStats newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(
+                includeZerosParcel);
+
+        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+            if (i == ENERGY_BUCKET_SCREEN_ON) {
+                assertEquals(stats.isEnergyBucketSupported(i),
+                        newStats.isEnergyBucketSupported(i));
+                assertEquals(stats.getAccumulatedBucketEnergy(i),
+                        newStats.getAccumulatedBucketEnergy(i));
+            } else {
+                assertTrue(newStats.isEnergyBucketSupported(i));
+                assertEquals(0L, newStats.getAccumulatedBucketEnergy(i));
+            }
+        }
+        includeZerosParcel.recycle();
+
+        final Parcel skipZerosParcel = Parcel.obtain();
+        MeasuredEnergyStats.writeSummaryToParcel(stats, skipZerosParcel, true);
+        skipZerosParcel.setDataPosition(0);
+
+        newStats = MeasuredEnergyStats.createAndReadSummaryFromParcel(skipZerosParcel);
+
+        for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) {
+            if (i == ENERGY_BUCKET_SCREEN_ON) {
+                assertEquals(stats.isEnergyBucketSupported(i),
+                        newStats.isEnergyBucketSupported(i));
+                assertEquals(stats.getAccumulatedBucketEnergy(i),
+                        newStats.getAccumulatedBucketEnergy(i));
+            } else {
+                assertFalse(newStats.isEnergyBucketSupported(i));
+                assertEquals(ENERGY_DATA_UNAVAILABLE, newStats.getAccumulatedBucketEnergy(i));
+            }
+        }
+        skipZerosParcel.recycle();
+    }
+
+    @Test
     public void testUpdateBucket() {
         final boolean[] supportedEnergyBuckets = new boolean[NUMBER_ENERGY_BUCKETS];
         supportedEnergyBuckets[ENERGY_BUCKET_SCREEN_ON] = true;
diff --git a/keystore/java/android/security/AuthTokenUtils.java b/keystore/java/android/security/AuthTokenUtils.java
new file mode 100644
index 0000000..14d6626
--- /dev/null
+++ b/keystore/java/android/security/AuthTokenUtils.java
@@ -0,0 +1,75 @@
+/*
+ * 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.security;
+
+import android.annotation.NonNull;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.Timestamp;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * @hide This Utils class provides method(s) for AuthToken conversion.
+ */
+public class AuthTokenUtils {
+
+    private AuthTokenUtils(){
+    }
+
+    /**
+     * Build a HardwareAuthToken from a byte array
+     * @param array byte array representing an auth token
+     * @return HardwareAuthToken representation of an auth token
+     */
+    public static @NonNull HardwareAuthToken toHardwareAuthToken(@NonNull byte[] array) {
+        final HardwareAuthToken hardwareAuthToken = new HardwareAuthToken();
+
+        // First byte is version, which does not exist in HardwareAuthToken anymore
+        // Next 8 bytes is the challenge.
+        hardwareAuthToken.challenge =
+                ByteBuffer.wrap(array, 1, 8).order(ByteOrder.nativeOrder()).getLong();
+
+        // Next 8 bytes is the userId
+        hardwareAuthToken.userId =
+                ByteBuffer.wrap(array, 9, 8).order(ByteOrder.nativeOrder()).getLong();
+
+        // Next 8 bytes is the authenticatorId.
+        hardwareAuthToken.authenticatorId =
+                ByteBuffer.wrap(array, 17, 8).order(ByteOrder.nativeOrder()).getLong();
+
+        // while the other fields are in machine byte order, authenticatorType and timestamp
+        // are in network byte order.
+        // Next 4 bytes is the authenticatorType.
+        hardwareAuthToken.authenticatorType =
+                ByteBuffer.wrap(array, 25, 4).order(ByteOrder.BIG_ENDIAN).getInt();
+        // Next 8 bytes is the timestamp.
+        final Timestamp timestamp = new Timestamp();
+        timestamp.milliSeconds =
+                ByteBuffer.wrap(array, 29, 8).order(ByteOrder.BIG_ENDIAN).getLong();
+        hardwareAuthToken.timestamp = timestamp;
+
+        // Last 32 bytes is the mac, 37:69
+        hardwareAuthToken.mac = new byte[32];
+        System.arraycopy(array, 37 /* srcPos */,
+                hardwareAuthToken.mac,
+                0 /* destPos */,
+                32 /* length */);
+
+        return hardwareAuthToken;
+    }
+}
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
new file mode 100644
index 0000000..1fde2b5
--- /dev/null
+++ b/keystore/java/android/security/Authorization.java
@@ -0,0 +1,78 @@
+/*
+ * 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.security;
+
+import android.annotation.NonNull;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.authorization.IKeystoreAuthorization;
+import android.system.keystore2.ResponseCode;
+import android.util.Log;
+
+/**
+ * @hide This is the client side for IKeystoreAuthorization AIDL.
+ * It shall only be used by biometric authentication providers and Gatekeeper.
+ */
+public class Authorization {
+    private static final String TAG = "KeystoreAuthorization";
+    private static IKeystoreAuthorization sIKeystoreAuthorization;
+
+    public static final int SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
+
+    public Authorization() {
+        sIKeystoreAuthorization = null;
+    }
+
+    private static synchronized IKeystoreAuthorization getService() {
+        if (sIKeystoreAuthorization == null) {
+            sIKeystoreAuthorization = IKeystoreAuthorization.Stub.asInterface(
+                    ServiceManager.checkService("android.security.authorization"));
+        }
+        return sIKeystoreAuthorization;
+    }
+
+    /**
+     * Adds an auth token to keystore2.
+     *
+     * @param authToken created by Android authenticators.
+     * @return 0 if successful or {@code ResponseCode.SYSTEM_ERROR}.
+     */
+    public int addAuthToken(@NonNull HardwareAuthToken authToken) {
+        if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+        try {
+            getService().addAuthToken(authToken);
+            return 0;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Can not connect to keystore", e);
+            return SYSTEM_ERROR;
+        } catch (ServiceSpecificException e) {
+            return e.errorCode;
+        }
+    }
+
+    /**
+     * Add an auth token to Keystore 2.0 in the legacy serialized auth token format.
+     * @param authToken
+     * @return 0 if successful or a {@code ResponseCode}.
+     */
+    public int addAuthToken(@NonNull byte[] authToken) {
+        return addAuthToken(AuthTokenUtils.toHardwareAuthToken(authToken));
+    }
+
+}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index c70c986..4a67135 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -996,6 +996,7 @@
      */
     public int addAuthToken(byte[] authToken) {
         try {
+            new Authorization().addAuthToken(authToken);
             return mBinder.addAuthToken(authToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 8d0e965..dd43139 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -252,7 +252,9 @@
         mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, true);
         // TODO: Synchronize show with the resize
         onLocationChanged();
-        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        if (taskInfo.taskDescription != null) {
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        }
 
         if (mListener != null) {
             mListenerExecutor.execute(() -> {
@@ -279,8 +281,9 @@
 
     @Override
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-        mTaskInfo.taskDescription = taskInfo.taskDescription;
-        setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        if (taskInfo.taskDescription != null) {
+            setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor());
+        }
     }
 
     @Override
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 6bf2e99..11a9086 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -16,12 +16,14 @@
 
 #include "RecordingCanvas.h"
 
-#include "pipeline/skia/FunctorDrawable.h"
-#include "VectorDrawable.h"
+#include <GrRecordingContext.h>
+
+#include <experimental/type_traits>
 
 #include "SkAndroidFrameworkUtils.h"
 #include "SkCanvas.h"
 #include "SkCanvasPriv.h"
+#include "SkColor.h"
 #include "SkData.h"
 #include "SkDrawShadowInfo.h"
 #include "SkImage.h"
@@ -33,8 +35,8 @@
 #include "SkRegion.h"
 #include "SkTextBlob.h"
 #include "SkVertices.h"
-
-#include <experimental/type_traits>
+#include "VectorDrawable.h"
+#include "pipeline/skia/FunctorDrawable.h"
 
 namespace android {
 namespace uirenderer {
@@ -500,7 +502,68 @@
     // SkDrawable::onSnapGpuDrawHandler callback instead of SkDrawable::onDraw.
     // SkCanvas::drawDrawable/SkGpuDevice::drawDrawable has the logic to invoke
     // onSnapGpuDrawHandler.
-    void draw(SkCanvas* c, const SkMatrix&) const { c->drawDrawable(drawable.get()); }
+private:
+    // Unfortunately WebView does not have complex clip information serialized, and we only perform
+    // best-effort stencil fill for GLES. So for Vulkan we create an intermediate layer if the
+    // canvas clip is complex.
+    static bool needsCompositedLayer(SkCanvas* c) {
+        if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+            return false;
+        }
+        SkRegion clipRegion;
+        // WebView's rasterizer has access to simple clips, so for Vulkan we only need to check if
+        // the clip is more complex than a rectangle.
+        c->temporary_internal_getRgnClip(&clipRegion);
+        return clipRegion.isComplex();
+    }
+
+    mutable SkImageInfo mLayerImageInfo;
+    mutable sk_sp<SkSurface> mLayerSurface = nullptr;
+
+public:
+    void draw(SkCanvas* c, const SkMatrix&) const {
+        if (needsCompositedLayer(c)) {
+            // What we do now is create an offscreen surface, sized by the clip bounds.
+            // We won't apply a clip while drawing - clipping will be performed when compositing the
+            // surface back onto the original canvas. Note also that we're not using saveLayer
+            // because the webview functor still doesn't respect the canvas clip stack.
+            const SkIRect deviceBounds = c->getDeviceClipBounds();
+            if (mLayerSurface == nullptr || c->imageInfo() != mLayerImageInfo) {
+                GrRecordingContext* directContext = c->recordingContext();
+                mLayerImageInfo =
+                        c->imageInfo().makeWH(deviceBounds.width(), deviceBounds.height());
+                mLayerSurface = SkSurface::MakeRenderTarget(directContext, SkBudgeted::kYes,
+                                                            mLayerImageInfo, 0,
+                                                            kTopLeft_GrSurfaceOrigin, nullptr);
+            }
+
+            SkCanvas* layerCanvas = mLayerSurface->getCanvas();
+
+            SkAutoCanvasRestore(layerCanvas, true);
+            layerCanvas->clear(SK_ColorTRANSPARENT);
+
+            // Preserve the transform from the original canvas, but now the clip rectangle is
+            // anchored at the origin so we need to transform the clipped content to the origin.
+            SkM44 mat4(c->getLocalToDevice());
+            mat4.postTranslate(-deviceBounds.fLeft, -deviceBounds.fTop);
+            layerCanvas->concat(mat4);
+            layerCanvas->drawDrawable(drawable.get());
+
+            SkAutoCanvasRestore acr(c, true);
+
+            // Temporarily use an identity transform, because this is just blitting to the parent
+            // canvas with an offset.
+            SkMatrix invertedMatrix;
+            if (!c->getTotalMatrix().invert(&invertedMatrix)) {
+                ALOGW("Unable to extract invert canvas matrix; aborting VkFunctor draw");
+                return;
+            }
+            c->concat(invertedMatrix);
+            mLayerSurface->draw(c, deviceBounds.fLeft, deviceBounds.fTop, nullptr);
+        } else {
+            c->drawDrawable(drawable.get());
+        }
+    }
 };
 }
 
diff --git a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
index 52d952c..e55678d9 100644
--- a/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
+++ b/media/java/android/media/metrics/IPlaybackMetricsManager.aidl
@@ -20,6 +20,7 @@
 import android.media.metrics.PlaybackErrorEvent;
 import android.media.metrics.PlaybackMetrics;
 import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
 
 /**
  * Interface to the playback manager service.
@@ -31,4 +32,5 @@
     void reportNetworkEvent(in String sessionId, in NetworkEvent event, int userId);
     void reportPlaybackErrorEvent(in String sessionId, in PlaybackErrorEvent event, int userId);
     void reportPlaybackStateEvent(in String sessionId, in PlaybackStateEvent event, int userId);
+    void reportTrackChangeEvent(in String sessionId, in TrackChangeEvent event, int userId);
 }
\ No newline at end of file
diff --git a/media/java/android/media/metrics/PlaybackMetricsManager.java b/media/java/android/media/metrics/PlaybackMetricsManager.java
index 63a50ae..f48ffe7 100644
--- a/media/java/android/media/metrics/PlaybackMetricsManager.java
+++ b/media/java/android/media/metrics/PlaybackMetricsManager.java
@@ -73,6 +73,18 @@
     }
 
     /**
+     * Reports track change event.
+     * @hide
+     */
+    public void reportTrackChangeEvent(@NonNull String sessionId, TrackChangeEvent event) {
+        try {
+            mService.reportTrackChangeEvent(sessionId, event, mUserId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a playback session.
      */
     public PlaybackSession createSession() {
diff --git a/media/java/android/media/metrics/PlaybackSession.java b/media/java/android/media/metrics/PlaybackSession.java
index 061e665..0a77516 100644
--- a/media/java/android/media/metrics/PlaybackSession.java
+++ b/media/java/android/media/metrics/PlaybackSession.java
@@ -71,6 +71,13 @@
         mManager.reportPlaybackStateEvent(mId, event);
     }
 
+    /**
+     * Reports track change event.
+     */
+    public void reportTrackChangeEvent(TrackChangeEvent event) {
+        mManager.reportTrackChangeEvent(mId, event);
+    }
+
     public @NonNull String getId() {
         return mId;
     }
diff --git a/media/java/android/media/metrics/TrackChangeEvent.aidl b/media/java/android/media/metrics/TrackChangeEvent.aidl
new file mode 100644
index 0000000..8fbe4a9
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.media.metrics;
+
+parcelable TrackChangeEvent;
diff --git a/media/java/android/media/metrics/TrackChangeEvent.java b/media/java/android/media/metrics/TrackChangeEvent.java
new file mode 100644
index 0000000..fff0e36
--- /dev/null
+++ b/media/java/android/media/metrics/TrackChangeEvent.java
@@ -0,0 +1,480 @@
+/*
+ * 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.media.metrics;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Playback track change event.
+ * @hide
+ */
+public final class TrackChangeEvent implements Parcelable {
+    public static final int TRACK_STATE_OFF = 0;
+    public static final int TRACK_STATE_ON = 1;
+
+    public static final int TRACK_CHANGE_REASON_UNKNOWN = 0;
+    public static final int TRACK_CHANGE_REASON_OTHER = 1;
+    public static final int TRACK_CHANGE_REASON_INITIAL = 2;
+    public static final int TRACK_CHANGE_REASON_MANUAL = 3;
+    public static final int TRACK_CHANGE_REASON_ADAPTIVE = 4;
+
+    public static final int TRACK_TYPE_AUDIO = 0;
+    public static final int TRACK_TYPE_VIDEO = 1;
+    public static final int TRACK_TYPE_TEXT = 2;
+
+    private final int mState;
+    private final int mReason;
+    private final @Nullable String mContainerMimeType;
+    private final @Nullable String mSampleMimeType;
+    private final @Nullable String mCodecName;
+    private final int mBitrate;
+    private final long mTimeSincePlaybackCreatedMillis;
+    private final int mType;
+    private final @Nullable String mLanguage;
+    private final @Nullable String mLanguageRegion;
+    private final int mChannelCount;
+    private final int mSampleRate;
+    private final int mWidth;
+    private final int mHeight;
+
+
+
+    /** @hide */
+    @IntDef(prefix = "TRACK_STATE_", value = {
+        TRACK_STATE_OFF,
+        TRACK_STATE_ON
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TrackState {}
+
+    /** @hide */
+    @IntDef(prefix = "TRACK_CHANGE_REASON_", value = {
+        TRACK_CHANGE_REASON_UNKNOWN,
+        TRACK_CHANGE_REASON_OTHER,
+        TRACK_CHANGE_REASON_INITIAL,
+        TRACK_CHANGE_REASON_MANUAL,
+        TRACK_CHANGE_REASON_ADAPTIVE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TrackChangeReason {}
+
+    /** @hide */
+    @IntDef(prefix = "TRACK_TYPE_", value = {
+        TRACK_TYPE_AUDIO,
+        TRACK_TYPE_VIDEO,
+        TRACK_TYPE_TEXT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TrackType {}
+
+    public TrackChangeEvent(
+            int state,
+            int reason,
+            @Nullable String containerMimeType,
+            @Nullable String sampleMimeType,
+            @Nullable String codecName,
+            int bitrate,
+            long timeSincePlaybackCreatedMillis,
+            int type,
+            @Nullable String language,
+            @Nullable String languageRegion,
+            int channelCount,
+            int sampleRate,
+            int width,
+            int height) {
+        this.mState = state;
+        this.mReason = reason;
+        this.mContainerMimeType = containerMimeType;
+        this.mSampleMimeType = sampleMimeType;
+        this.mCodecName = codecName;
+        this.mBitrate = bitrate;
+        this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+        this.mType = type;
+        this.mLanguage = language;
+        this.mLanguageRegion = languageRegion;
+        this.mChannelCount = channelCount;
+        this.mSampleRate = sampleRate;
+        this.mWidth = width;
+        this.mHeight = height;
+    }
+
+    @TrackState
+    public int getTrackState() {
+        return mState;
+    }
+
+    @TrackChangeReason
+    public int getTrackChangeReason() {
+        return mReason;
+    }
+
+    public @Nullable String getContainerMimeType() {
+        return mContainerMimeType;
+    }
+
+    public @Nullable String getSampleMimeType() {
+        return mSampleMimeType;
+    }
+
+    public @Nullable String getCodecName() {
+        return mCodecName;
+    }
+
+    public int getBitrate() {
+        return mBitrate;
+    }
+
+    public long getTimeSincePlaybackCreatedMillis() {
+        return mTimeSincePlaybackCreatedMillis;
+    }
+
+    @TrackType
+    public int getTrackType() {
+        return mType;
+    }
+
+    public @Nullable String getLanguage() {
+        return mLanguage;
+    }
+
+    public @Nullable String getLanguageRegion() {
+        return mLanguageRegion;
+    }
+
+    public int getChannelCount() {
+        return mChannelCount;
+    }
+
+    public int getSampleRate() {
+        return mSampleRate;
+    }
+
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        int flg = 0;
+        if (mContainerMimeType != null) flg |= 0x4;
+        if (mSampleMimeType != null) flg |= 0x8;
+        if (mCodecName != null) flg |= 0x10;
+        if (mLanguage != null) flg |= 0x100;
+        if (mLanguageRegion != null) flg |= 0x200;
+        dest.writeInt(flg);
+        dest.writeInt(mState);
+        dest.writeInt(mReason);
+        if (mContainerMimeType != null) dest.writeString(mContainerMimeType);
+        if (mSampleMimeType != null) dest.writeString(mSampleMimeType);
+        if (mCodecName != null) dest.writeString(mCodecName);
+        dest.writeInt(mBitrate);
+        dest.writeLong(mTimeSincePlaybackCreatedMillis);
+        dest.writeInt(mType);
+        if (mLanguage != null) dest.writeString(mLanguage);
+        if (mLanguageRegion != null) dest.writeString(mLanguageRegion);
+        dest.writeInt(mChannelCount);
+        dest.writeInt(mSampleRate);
+        dest.writeInt(mWidth);
+        dest.writeInt(mHeight);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    /* package-private */ TrackChangeEvent(@NonNull Parcel in) {
+        int flg = in.readInt();
+        int state = in.readInt();
+        int reason = in.readInt();
+        String containerMimeType = (flg & 0x4) == 0 ? null : in.readString();
+        String sampleMimeType = (flg & 0x8) == 0 ? null : in.readString();
+        String codecName = (flg & 0x10) == 0 ? null : in.readString();
+        int bitrate = in.readInt();
+        long timeSincePlaybackCreatedMillis = in.readLong();
+        int type = in.readInt();
+        String language = (flg & 0x100) == 0 ? null : in.readString();
+        String languageRegion = (flg & 0x200) == 0 ? null : in.readString();
+        int channelCount = in.readInt();
+        int sampleRate = in.readInt();
+        int width = in.readInt();
+        int height = in.readInt();
+
+        this.mState = state;
+        this.mReason = reason;
+        this.mContainerMimeType = containerMimeType;
+        this.mSampleMimeType = sampleMimeType;
+        this.mCodecName = codecName;
+        this.mBitrate = bitrate;
+        this.mTimeSincePlaybackCreatedMillis = timeSincePlaybackCreatedMillis;
+        this.mType = type;
+        this.mLanguage = language;
+        this.mLanguageRegion = languageRegion;
+        this.mChannelCount = channelCount;
+        this.mSampleRate = sampleRate;
+        this.mWidth = width;
+        this.mHeight = height;
+    }
+
+    public static final @NonNull Parcelable.Creator<TrackChangeEvent> CREATOR =
+            new Parcelable.Creator<TrackChangeEvent>() {
+        @Override
+        public TrackChangeEvent[] newArray(int size) {
+            return new TrackChangeEvent[size];
+        }
+
+        @Override
+        public TrackChangeEvent createFromParcel(@NonNull Parcel in) {
+            return new TrackChangeEvent(in);
+        }
+    };
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/media/java/android/media/metrics/TrackChangeEvent.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+    @Override
+    public String toString() {
+        return "TrackChangeEvent { " +
+                "state = " + mState + ", " +
+                "reason = " + mReason + ", " +
+                "containerMimeType = " + mContainerMimeType + ", " +
+                "sampleMimeType = " + mSampleMimeType + ", " +
+                "codecName = " + mCodecName + ", " +
+                "bitrate = " + mBitrate + ", " +
+                "timeSincePlaybackCreatedMillis = " + mTimeSincePlaybackCreatedMillis + ", " +
+                "type = " + mType + ", " +
+                "language = " + mLanguage + ", " +
+                "languageRegion = " + mLanguageRegion + ", " +
+                "channelCount = " + mChannelCount + ", " +
+                "sampleRate = " + mSampleRate + ", " +
+                "width = " + mWidth + ", " +
+                "height = " + mHeight +
+        " }";
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TrackChangeEvent that = (TrackChangeEvent) o;
+        return mState == that.mState
+                && mReason == that.mReason
+                && Objects.equals(mContainerMimeType, that.mContainerMimeType)
+                && Objects.equals(mSampleMimeType, that.mSampleMimeType)
+                && Objects.equals(mCodecName, that.mCodecName)
+                && mBitrate == that.mBitrate
+                && mTimeSincePlaybackCreatedMillis == that.mTimeSincePlaybackCreatedMillis
+                && mType == that.mType
+                && Objects.equals(mLanguage, that.mLanguage)
+                && Objects.equals(mLanguageRegion, that.mLanguageRegion)
+                && mChannelCount == that.mChannelCount
+                && mSampleRate == that.mSampleRate
+                && mWidth == that.mWidth
+                && mHeight == that.mHeight;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mState, mReason, mContainerMimeType, mSampleMimeType, mCodecName,
+                mBitrate, mTimeSincePlaybackCreatedMillis, mType, mLanguage, mLanguageRegion,
+                mChannelCount, mSampleRate, mWidth, mHeight);
+    }
+
+    /**
+     * A builder for {@link TrackChangeEvent}
+     */
+    public static final class Builder {
+        // TODO: check track type for the setters.
+        private int mState;
+        private int mReason;
+        private @Nullable String mContainerMimeType;
+        private @Nullable String mSampleMimeType;
+        private @Nullable String mCodecName;
+        private int mBitrate;
+        private long mTimeSincePlaybackCreatedMillis;
+        private int mType;
+        private @Nullable String mLanguage;
+        private @Nullable String mLanguageRegion;
+        private int mChannelCount;
+        private int mSampleRate;
+        private int mWidth;
+        private int mHeight;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @hide
+         */
+        public Builder(int type) {
+            mType = type;
+        }
+
+        public @NonNull Builder setTrackState(@TrackState int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mState = value;
+            return this;
+        }
+
+        public @NonNull Builder setTrackChangeReason(@TrackChangeReason int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2;
+            mReason = value;
+            return this;
+        }
+
+        public @NonNull Builder setContainerMimeType(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mContainerMimeType = value;
+            return this;
+        }
+
+        public @NonNull Builder setSampleMimeType(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mSampleMimeType = value;
+            return this;
+        }
+
+        public @NonNull Builder setCodecName(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10;
+            mCodecName = value;
+            return this;
+        }
+
+        public @NonNull Builder setBitrate(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x20;
+            mBitrate = value;
+            return this;
+        }
+
+        public @NonNull Builder setTimeSincePlaybackCreatedMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x40;
+            mTimeSincePlaybackCreatedMillis = value;
+            return this;
+        }
+
+        public @NonNull Builder setTrackType(@TrackType int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mType = value;
+            return this;
+        }
+
+        public @NonNull Builder setLanguage(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x100;
+            mLanguage = value;
+            return this;
+        }
+
+        public @NonNull Builder setLanguageRegion(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x200;
+            mLanguageRegion = value;
+            return this;
+        }
+
+        public @NonNull Builder setChannelCount(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x400;
+            mChannelCount = value;
+            return this;
+        }
+
+        public @NonNull Builder setSampleRate(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x800;
+            mSampleRate = value;
+            return this;
+        }
+
+        public @NonNull Builder setWidth(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1000;
+            mWidth = value;
+            return this;
+        }
+
+        public @NonNull Builder setHeight(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x2000;
+            mHeight = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull TrackChangeEvent build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4000; // Mark builder used
+
+            TrackChangeEvent o = new TrackChangeEvent(
+                    mState,
+                    mReason,
+                    mContainerMimeType,
+                    mSampleMimeType,
+                    mCodecName,
+                    mBitrate,
+                    mTimeSincePlaybackCreatedMillis,
+                    mType,
+                    mLanguage,
+                    mLanguageRegion,
+                    mChannelCount,
+                    mSampleRate,
+                    mWidth,
+                    mHeight);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x4000) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index d278c59..86aa214 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -83,7 +83,7 @@
                         return value == null || value.length() < MAX_LENGTH;
                     }
                 });
-        VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.85f, 1.3f));
+        VALIDATORS.put(System.FONT_SCALE, new InclusiveFloatRangeValidator(0.25f, 5.0f));
         VALIDATORS.put(System.DIM_SCREEN, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 System.DISPLAY_COLOR_MODE,
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 672d2f6..c4cf440 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -73,7 +73,7 @@
     <color name="media_divider">#85ffffff</color>
 
     <!-- Biometric dialog colors -->
-    <color name="biometric_dialog_gray">#ff888888</color>
+    <color name="biometric_dialog_gray">#ffcccccc</color>
     <color name="biometric_dialog_accent">#ff80cbc4</color> <!-- light teal -->
     <color name="biometric_dialog_error">#fff28b82</color> <!-- red 300 -->
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index e4f6d6c..9b09cfd 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -160,12 +160,12 @@
 
     @Override
     protected void handleResetAfterError() {
-        resetErrorView(mContext, mIndicatorView);
+        resetErrorView();
     }
 
     @Override
     protected void handleResetAfterHelp() {
-        resetErrorView(mContext, mIndicatorView);
+        resetErrorView();
     }
 
     @Override
@@ -185,7 +185,7 @@
 
         if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                 (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {
-            resetErrorView(mContext, mIndicatorView);
+            resetErrorView();
         }
 
         // Do this last since the state variable gets updated.
@@ -204,9 +204,8 @@
         super.onAuthenticationFailed(failureReason);
     }
 
-    static void resetErrorView(Context context, TextView textView) {
-        textView.setTextColor(context.getResources().getColor(
-                R.color.biometric_dialog_gray, context.getTheme()));
-        textView.setVisibility(View.INVISIBLE);
+    private void resetErrorView() {
+        mIndicatorView.setTextColor(mTextColorHint);
+        mIndicatorView.setVisibility(View.INVISIBLE);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
index 176e9e6..45ee4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.java
@@ -78,7 +78,7 @@
 
     private void showTouchSensorString() {
         mIndicatorView.setText(R.string.fingerprint_dialog_touch_sensor);
-        mIndicatorView.setTextColor(R.color.biometric_dialog_gray);
+        mIndicatorView.setTextColor(mTextColorHint);
     }
 
     private void updateIcon(int lastState, int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index c748ab2..18206ef 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -83,7 +83,7 @@
      * Authenticated, dialog animating away soon.
      */
     protected static final int STATE_AUTHENTICATED = 6;
-    
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({STATE_IDLE, STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_HELP,
             STATE_ERROR, STATE_PENDING_CONFIRMATION, STATE_AUTHENTICATED})
@@ -168,8 +168,8 @@
     private final Injector mInjector;
     private final Handler mHandler;
     private final AccessibilityManager mAccessibilityManager;
-    private final int mTextColorError;
-    private final int mTextColorHint;
+    protected final int mTextColorError;
+    protected final int mTextColorHint;
 
     private AuthPanelController mPanelController;
     private PromptInfo mPromptInfo;
@@ -183,7 +183,7 @@
     private TextView mDescriptionView;
     private View mIconHolderView;
     protected ImageView mIconView;
-    @VisibleForTesting protected TextView mIndicatorView;
+    protected TextView mIndicatorView;
 
     // Negative button position, exclusively for the app-specified behavior
     @VisibleForTesting Button mNegativeButton;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index bba8579..e9207f1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -29,7 +29,6 @@
 import com.android.systemui.qs.TouchAnimator.Builder;
 import com.android.systemui.qs.TouchAnimator.Listener;
 import com.android.systemui.qs.dagger.QSScope;
-import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
@@ -50,9 +49,7 @@
     private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows";
 
     public static final float EXPANDED_TILE_DELAY = .86f;
-    private static final long QQS_FADE_IN_DURATION = 200L;
-    // Fade out faster than fade in to finish before QQS hides.
-    private static final long QQS_FADE_OUT_DURATION = 50L;
+
 
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
@@ -80,7 +77,6 @@
     private TouchAnimator mBrightnessAnimator;
     private boolean mNeedsAnimatorUpdate = false;
 
-    private boolean mToShowing;
     private boolean mOnKeyguard;
 
     private boolean mAllowFancy;
@@ -137,18 +133,6 @@
         }
     }
 
-    void startAlphaAnimation(boolean show) {
-        if (show == mToShowing) {
-            return;
-        }
-        mToShowing = show;
-        if (show) {
-            CrossFadeHelper.fadeIn(mQs.getView(), QQS_FADE_IN_DURATION, 0 /* delay */);
-        } else {
-            CrossFadeHelper.fadeOut(mQs.getView(), QQS_FADE_OUT_DURATION, 0 /* delay */,
-                    null /* endRunnable */);
-        }
-    }
 
     /**
      * Sets whether or not the keyguard is currently being shown with a collapsed header.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 562ac64..16e9590 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -91,11 +91,6 @@
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
     }
 
-    @Override
-    public boolean hasOverlappingRendering() {
-        return false;
-    }
-
     void onMediaVisibilityChanged(boolean qsVisible) {
         mAnimateBottomOnNextLayout = qsVisible;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 9a0827d..dbdd04a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -186,7 +186,7 @@
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     boolean sizeChanged = (oldTop - oldBottom) != (top - bottom);
                     if (sizeChanged) {
-                        setQsExpansion(mLastQSExpansion, mLastHeaderTranslation);
+                        setQsExpansion(mLastQSExpansion, mLastQSExpansion);
                     }
                 });
     }
@@ -391,9 +391,6 @@
     @Override
     public void setQsExpansion(float expansion, float headerTranslation) {
         if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation);
-        if (mQSAnimator != null) {
-            mQSAnimator.startAlphaAnimation(headerTranslation == 0 /* show */);
-        }
         mContainer.setExpansion(expansion);
         final float translationScaleY = expansion - 1;
         boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
index 3357be8..fe01f84 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.os.Build;
 import android.os.PowerManager;
 
 import androidx.test.filters.SmallTest;
@@ -85,4 +86,14 @@
         assertTrue(ran[0]);
         assertFalse(mInner.isHeld());
     }
+
+    @Test
+    public void prodBuild_wakeLock_releaseWithoutAcquire_noThrow() {
+        if (Build.IS_ENG) {
+            return;
+        }
+
+        // shouldn't throw an exception on production builds
+        mWakeLock.release(WHY);
+    }
 }
\ No newline at end of file
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index c6f42f7..a31cfae 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -1,4 +1,4 @@
 svetoslavganov@google.com
 pweaver@google.com
 rhedjao@google.com
-qasid@google.com
+ryanlwlin@google.com
diff --git a/services/core/java/android/power/PowerStatsInternal.java b/services/core/java/android/power/PowerStatsInternal.java
new file mode 100644
index 0000000..9c908c3
--- /dev/null
+++ b/services/core/java/android/power/PowerStatsInternal.java
@@ -0,0 +1,42 @@
+/*
+ * 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.power;
+
+import android.hardware.power.stats.EnergyConsumerId;
+import android.hardware.power.stats.EnergyConsumerResult;
+
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Power stats local system service interface.
+ *
+ * @hide Only for use within Android OS.
+ */
+public abstract class PowerStatsInternal {
+    /**
+     * Returns a CompletableFuture that will get an {@link EnergyConsumerResult} array for the
+     * available requested energy consumers (power models).
+     *
+     * @param energyConsumerIds Array of {@link EnergyConsumerId} for which energy consumed is being
+     *                          requested.
+     *
+     * @return A Future containing a list of {@link EnergyConsumerResult} objects containing energy
+     *         consumer results for all listed {@link EnergyConsumerId}.
+     */
+    public abstract CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+            @EnergyConsumerId int[] energyConsumerIds);
+}
diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java
index a6d9bf8..f04af8b 100644
--- a/services/core/java/com/android/server/DropBoxManagerService.java
+++ b/services/core/java/com/android/server/DropBoxManagerService.java
@@ -73,7 +73,6 @@
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.SortedSet;
@@ -446,7 +445,10 @@
             // from an in-memory buffer, or another file on disk; if we buffered
             // we'd lose out on sendfile() optimizations
             if (forceCompress) {
-                FileUtils.copy(in, new GZIPOutputStream(new FileOutputStream(fd)));
+                final GZIPOutputStream gzipOutputStream =
+                        new GZIPOutputStream(new FileOutputStream(fd));
+                FileUtils.copy(in, gzipOutputStream);
+                gzipOutputStream.finish();
             } else {
                 FileUtils.copy(in, new FileOutputStream(fd));
             }
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index dfcc325..b14ce1c 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -5,7 +5,7 @@
 per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
 
 # Zram writeback
-per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com
+per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
 
 # Userspace reboot
 per-file UserspaceRebootLogger.java = ioffe@google.com, tomcherry@google.com
@@ -30,6 +30,7 @@
 per-file MmsServiceBroker.java = file:/telephony/OWNERS
 per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS
 per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS
+per-file PinnerService.java = file:/apct-tests/perftests/OWNERS
 per-file TelephonyRegistry.java = file:/telephony/OWNERS
 per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS
 per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index a3bcbbe..871de0d 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -43,6 +43,8 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -61,7 +63,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import dalvik.system.DexFile;
@@ -70,6 +71,7 @@
 import java.io.Closeable;
 import java.io.DataInputStream;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
@@ -100,12 +102,6 @@
     private static final int KEY_HOME = 1;
     private static final int KEY_ASSISTANT = 2;
 
-    // Pin the camera application. Default to the system property only if the experiment phenotype
-    // property is not set.
-    private static boolean PROP_PIN_CAMERA =
-            DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
-                                    "pin_camera",
-                                    SystemProperties.getBoolean("pinner.pin_camera", false));
     // Pin using pinlist.meta when pinning apps.
     private static boolean PROP_PIN_PINLIST = SystemProperties.getBoolean(
             "pinner.use_pinlist", true);
@@ -150,7 +146,13 @@
     /**
      * A set of {@link AppKey} that are configured to be pinned.
      */
-    private final ArraySet<Integer> mPinKeys = new ArraySet<>();
+    @GuardedBy("this")
+    private ArraySet<Integer> mPinKeys;
+
+    // Resource-configured pinner flags;
+    private final boolean mConfiguredToPinCamera;
+    private final boolean mConfiguredToPinHome;
+    private final boolean mConfiguredToPinAssistant;
 
     private BinderService mBinderService;
     private PinnerHandler mPinnerHandler = null;
@@ -173,25 +175,13 @@
         super(context);
 
         mContext = context;
-        boolean shouldPinCamera = context.getResources().getBoolean(
+        mConfiguredToPinCamera = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerCameraApp);
-        boolean shouldPinHome = context.getResources().getBoolean(
+        mConfiguredToPinHome = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerHomeApp);
-        boolean shouldPinAssistant = context.getResources().getBoolean(
+        mConfiguredToPinAssistant = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_pinnerAssistantApp);
-        if (shouldPinCamera) {
-            if (PROP_PIN_CAMERA) {
-                mPinKeys.add(KEY_CAMERA);
-            } else if (DEBUG) {
-                Slog.i(TAG, "Pinner - skip pinning camera app");
-            }
-        }
-        if (shouldPinHome) {
-            mPinKeys.add(KEY_HOME);
-        }
-        if (shouldPinAssistant) {
-            mPinKeys.add(KEY_ASSISTANT);
-        }
+        mPinKeys = createPinKeys();
         mPinnerHandler = new PinnerHandler(BackgroundThread.get().getLooper());
 
         mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
@@ -259,9 +249,10 @@
      * The other files pinned in onStart will not need to be updated.
      */
     public void update(ArraySet<String> updatedPackages, boolean force) {
+        ArraySet<Integer> pinKeys = getPinKeys();
         int currentUser = ActivityManager.getCurrentUser();
-        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
-            int key = mPinKeys.valueAt(i);
+        for (int i = pinKeys.size() - 1; i >= 0; i--) {
+            int key = pinKeys.valueAt(i);
             ApplicationInfo info = getInfoForKey(key, currentUser);
             if (info != null && updatedPackages.contains(info.packageName)) {
                 Slog.i(TAG, "Updating pinned files for " + info.packageName + " force=" + force);
@@ -385,6 +376,14 @@
         }
     }
 
+    private void unpinApps() {
+        ArraySet<Integer> pinKeys = getPinKeys();
+        for (int i = pinKeys.size() - 1; i >= 0; i--) {
+            int key = pinKeys.valueAt(i);
+            unpinApp(key);
+        }
+    }
+
     private void unpinApp(@AppKey int key) {
         ArrayList<PinnedFile> pinnedAppFiles;
         synchronized (this) {
@@ -490,9 +489,79 @@
                 userHandle));
     }
 
+    private void sendPinAppsWithUpdatedKeysMessage(int userHandle) {
+        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::pinAppsWithUpdatedKeys,
+                this, userHandle));
+    }
+    private void sendUnpinAppsMessage() {
+        mPinnerHandler.sendMessage(PooledLambda.obtainMessage(PinnerService::unpinApps, this));
+    }
+
+    private ArraySet<Integer> createPinKeys() {
+        ArraySet<Integer> pinKeys = new ArraySet<>();
+        // Pin the camera application. Default to the system property only if the experiment
+        // phenotype property is not set.
+        boolean shouldPinCamera = mConfiguredToPinCamera
+                && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                        "pin_camera",
+                        SystemProperties.getBoolean("pinner.pin_camera", false));
+        if (shouldPinCamera) {
+            pinKeys.add(KEY_CAMERA);
+        } else if (DEBUG) {
+            Slog.i(TAG, "Pinner - skip pinning camera app");
+        }
+
+        if (mConfiguredToPinHome) {
+            pinKeys.add(KEY_HOME);
+        }
+        if (mConfiguredToPinAssistant) {
+            pinKeys.add(KEY_ASSISTANT);
+        }
+
+        return pinKeys;
+    }
+
+    private static boolean shouldPinSplitApks() {
+        // For now this is disabled by default bcause the pinlist support for split APKs are
+        // missing in the toolchain. This flag should be removed once it is ready. b/174697187.
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                "pin_split_apks", false);
+    }
+
+    private synchronized ArraySet<Integer> getPinKeys() {
+        return mPinKeys;
+    }
+
     private void pinApps(int userHandle) {
-        for (int i = mPinKeys.size() - 1; i >= 0; i--) {
-            int key = mPinKeys.valueAt(i);
+        pinAppsInternal(userHandle, false);
+    }
+
+    private void pinAppsWithUpdatedKeys(int userHandle) {
+        pinAppsInternal(userHandle, true);
+    }
+
+    /**
+     * @param updateKeys True if the pinned app list has to be updated. This is true only when
+     *                   "pinner repin" shell command is requested.
+     */
+    private void pinAppsInternal(int userHandle, boolean updateKeys) {
+        if (updateKeys) {
+            ArraySet<Integer> newKeys = createPinKeys();
+            synchronized (this) {
+                // This code path demands preceding unpinApps() call.
+                if (!mPinnedApps.isEmpty()) {
+                    Slog.e(TAG, "Attempted to update a list of apps, "
+                            + "but apps were already pinned. Skipping.");
+                    return;
+                }
+
+                mPinKeys = newKeys;
+            }
+        }
+
+        ArraySet<Integer> currentPinKeys = getPinKeys();
+        for (int i = currentPinKeys.size() - 1; i >= 0; i--) {
+            int key = currentPinKeys.valueAt(i);
             pinApp(key, userHandle, true /* force */);
         }
     }
@@ -610,19 +679,40 @@
             mPinnedApps.put(key, pinnedApp);
         }
 
+
         // pin APK
-        int pinSizeLimit = getSizeLimitForKey(key);
-        String apk = appInfo.sourceDir;
-        PinnedFile pf = pinFile(apk, pinSizeLimit, /*attemptPinIntrospection=*/true);
-        if (pf == null) {
-            Slog.e(TAG, "Failed to pin " + apk);
-            return;
+        final int pinSizeLimit = getSizeLimitForKey(key);
+        List<String> apks = new ArrayList<>();
+        apks.add(appInfo.sourceDir);
+
+        if (shouldPinSplitApks() && appInfo.splitSourceDirs != null) {
+            for (String splitApk : appInfo.splitSourceDirs) {
+                apks.add(splitApk);
+            }
         }
-        if (DEBUG) {
-            Slog.i(TAG, "Pinned " + pf.fileName);
-        }
-        synchronized (this) {
-            pinnedApp.mFiles.add(pf);
+
+        int apkPinSizeLimit = pinSizeLimit;
+        for (String apk: apks) {
+            if (apkPinSizeLimit <= 0) {
+                Slog.w(TAG, "Reached to the pin size limit. Skipping: " + apk);
+                // Continue instead of break to print all skipped APK names.
+                continue;
+            }
+
+            PinnedFile pf = pinFile(apk, apkPinSizeLimit, /*attemptPinIntrospection=*/true);
+            if (pf == null) {
+                Slog.e(TAG, "Failed to pin " + apk);
+                continue;
+            }
+
+            if (DEBUG) {
+                Slog.i(TAG, "Pinned " + pf.fileName);
+            }
+            synchronized (this) {
+                pinnedApp.mFiles.add(pf);
+            }
+
+            apkPinSizeLimit -= pf.bytesPinned;
         }
 
         // determine the ABI from either ApplicationInfo or Build
@@ -641,7 +731,7 @@
 
         //not pinning the oat/odex is not a fatal error
         for (String file : files) {
-            pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
+            PinnedFile pf = pinFile(file, pinSizeLimit, /*attemptPinIntrospection=*/false);
             if (pf != null) {
                 synchronized (this) {
                     if (PROP_PIN_ODEX) {
@@ -981,6 +1071,42 @@
                 }
             }
         }
+
+        private void repin() {
+            sendUnpinAppsMessage();
+            // TODO(morrita): Consider supporting non-system user.
+            sendPinAppsWithUpdatedKeysMessage(UserHandle.USER_SYSTEM);
+        }
+
+        private void printError(FileDescriptor out, String message) {
+            PrintWriter writer = new PrintWriter(new FileOutputStream(out));
+            writer.println(message);
+            writer.flush();
+        }
+
+        @Override
+        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+            if (args.length < 1) {
+                printError(out, "Command is not given.");
+                resultReceiver.send(-1, null);
+                return;
+            }
+
+            String command = args[0];
+            switch (command) {
+                case "repin":
+                    repin();
+                    break;
+                default:
+                    printError(out, String.format(
+                            "Unknown pinner command: %s. Supported commands: repin", command));
+                    resultReceiver.send(-1, null);
+                    return;
+            }
+
+            resultReceiver.send(0, null);
+        }
     }
 
     private static final class PinnedFile implements AutoCloseable {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index c191a78..2fdc796 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.vcn.IVcnManagementService;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
 import android.net.vcn.VcnConfig;
 import android.os.Binder;
 import android.os.Handler;
@@ -495,4 +496,20 @@
             return Collections.unmodifiableMap(mVcns);
         }
     }
+
+    /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
+    @Override
+    public void addVcnUnderlyingNetworkPolicyListener(
+            IVcnUnderlyingNetworkPolicyListener listener) {
+        // TODO(b/175739863): implement policy listener registration
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
+    @Override
+    public void removeVcnUnderlyingNetworkPolicyListener(
+            IVcnUnderlyingNetworkPolicyListener listener) {
+        // TODO(b/175739863): implement policy listener unregistration
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0291ce4c..1f48aeb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -32,6 +32,7 @@
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
 import static android.content.pm.PackageManager.MATCH_ALL;
@@ -1085,11 +1086,13 @@
         final int targetUid;
         final long duration;
         final String tag;
+        final int type;
 
-        PendingTempWhitelist(int _targetUid, long _duration, String _tag) {
+        PendingTempWhitelist(int _targetUid, long _duration, String _tag, int _type) {
             targetUid = _targetUid;
             duration = _duration;
             tag = _tag;
+            type = _type;
         }
 
         void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -1097,6 +1100,7 @@
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TARGET_UID, targetUid);
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.DURATION_MS, duration);
             proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TAG, tag);
+            proto.write(ActivityManagerServiceDumpProcessesProto.PendingTempWhitelist.TYPE, type);
             proto.end(token);
         }
     }
@@ -5526,8 +5530,7 @@
     }
 
     boolean isWhitelistedForFgsStartLocked(int uid) {
-        final int appId = UserHandle.getAppId(uid);
-        return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, appId) >= 0
+        return Arrays.binarySearch(mDeviceIdleExceptIdleWhitelist, UserHandle.getAppId(uid)) >= 0
                 || mFgsStartTempAllowList.isAllowed(uid);
     }
 
@@ -9298,6 +9301,8 @@
                     TimeUtils.formatDuration(ptw.duration, pw);
                     pw.print(" ");
                     pw.println(ptw.tag);
+                    pw.print(" ");
+                    pw.print(ptw.type);
                 }
             }
         }
@@ -15359,11 +15364,12 @@
      */
     @GuardedBy("this")
     void tempWhitelistUidLocked(int targetUid, long duration, String tag, int type) {
-        mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
+        mPendingTempWhitelist.put(targetUid,
+                new PendingTempWhitelist(targetUid, duration, tag, type));
         setUidTempWhitelistStateLocked(targetUid, true);
         mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
 
-        if (type == BroadcastOptions.TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+        if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
             mFgsStartTempAllowList.add(targetUid, duration);
         }
     }
@@ -15389,7 +15395,7 @@
             for (int i = 0; i < N; i++) {
                 PendingTempWhitelist ptw = list[i];
                 mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
-                        ptw.duration, true, ptw.tag);
+                        ptw.duration, ptw.type, true, ptw.tag);
             }
         }
 
@@ -15406,8 +15412,8 @@
     }
 
     @GuardedBy("this")
-    final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
-        mOomAdjuster.setAppIdTempWhitelistStateLocked(appId, onWhitelist);
+    final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+        mOomAdjuster.setAppIdTempWhitelistStateLocked(uid, onWhitelist);
     }
 
     @GuardedBy("this")
@@ -16009,10 +16015,16 @@
         }
 
         @Override
-        public void updateDeviceIdleTempWhitelist(int[] appids, int changingAppId, boolean adding) {
+        public void updateDeviceIdleTempWhitelist(int[] appids, int changingUid, boolean adding,
+                long durationMs, @BroadcastOptions.TempAllowListType int type) {
             synchronized (ActivityManagerService.this) {
                 mDeviceIdleTempWhitelist = appids;
-                setAppIdTempWhitelistStateLocked(changingAppId, adding);
+                if (adding) {
+                    if (type == TEMPORARY_WHITELIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                        mFgsStartTempAllowList.add(changingUid, durationMs);
+                    }
+                }
+                setAppIdTempWhitelistStateLocked(changingUid, adding);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 6fe934e..ada7eea 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -17,21 +17,24 @@
 
 import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
+import android.hardware.power.stats.EnergyConsumerId;
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.Bundle;
 import android.os.OutcomeReceiver;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.ServiceManager;
 import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
 import android.os.ThreadLocalWorkSource;
 import android.os.connectivity.WifiActivityEnergyInfo;
+import android.power.PowerStatsInternal;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.IntArray;
@@ -41,8 +44,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.power.MeasuredEnergyArray;
+import com.android.internal.power.MeasuredEnergyStats;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
 
 import libcore.util.EmptyArray;
 
@@ -91,6 +96,8 @@
                     });
 
     private final Context mContext;
+
+    @GuardedBy("mStats")
     private final BatteryStatsImpl mStats;
 
     @GuardedBy("this")
@@ -123,6 +130,7 @@
     @GuardedBy("this")
     private Future<?> mBatteryLevelSync;
 
+    // If both mStats and mWorkerLock need to be synchronized, mWorkerLock must be acquired first.
     private final Object mWorkerLock = new Object();
 
     @GuardedBy("mWorkerLock")
@@ -131,6 +139,9 @@
     @GuardedBy("mWorkerLock")
     private TelephonyManager mTelephony = null;
 
+    @GuardedBy("mWorkerLock")
+    private PowerStatsInternal mPowerStatsInternal = null;
+
     // WiFi keeps an accumulated total of stats, unlike Bluetooth.
     // Keep the last WiFi stats so we can compute a delta.
     @GuardedBy("mWorkerLock")
@@ -139,7 +150,7 @@
 
     /** Snapshot of measured energies, or null if no measured energies are supported. */
     @GuardedBy("mWorkerLock")
-    private final @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot;
+    private @Nullable MeasuredEnergySnapshot mMeasuredEnergySnapshot = null;
 
     /**
      * Timestamp at which all external stats were last collected in
@@ -148,13 +159,27 @@
     @GuardedBy("this")
     private long mLastCollectionTimeStamp;
 
-    BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats,
-            @Nullable MeasuredEnergyArray initialMeasuredEnergies) {
+    BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
         mContext = context;
         mStats = stats;
+    }
 
-        mMeasuredEnergySnapshot = initialMeasuredEnergies == null ?
-                null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+    public void systemServicesReady() {
+        final WifiManager wm = mContext.getSystemService(WifiManager.class);
+        final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+        final PowerStatsInternal psi = LocalServices.getService(PowerStatsInternal.class);
+        final MeasuredEnergyArray initialMeasuredEnergies = getEnergyConsumptionData(psi);
+        final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialMeasuredEnergies);
+        synchronized (mWorkerLock) {
+            mWifiManager = wm;
+            mTelephony = tm;
+            mPowerStatsInternal = psi;
+            mMeasuredEnergySnapshot = initialMeasuredEnergies == null
+                    ? null : new MeasuredEnergySnapshot(initialMeasuredEnergies);
+            synchronized (mStats) {
+                mStats.initMeasuredEnergyStatsLocked(supportedBuckets);
+            }
+        }
     }
 
     @Override
@@ -433,14 +458,6 @@
 
         if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
             // We were asked to fetch WiFi data.
-            if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) {
-                // this code is reached very early in the boot process, before Wifi Service has
-                // been registered. Check that ServiceManager.getService() returns a non null
-                // value before calling mContext.getSystemService(), since otherwise
-                // getSystemService() will throw a ServiceNotFoundException.
-                mWifiManager = mContext.getSystemService(WifiManager.class);
-            }
-
             // Only fetch WiFi power data if it is supported.
             if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) {
                 SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi");
@@ -478,10 +495,6 @@
 
         if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_RADIO) != 0) {
             // We were asked to fetch Telephony data.
-            if (mTelephony == null) {
-                mTelephony = mContext.getSystemService(TelephonyManager.class);
-            }
-
             if (mTelephony != null) {
                 CompletableFuture<ModemActivityInfo> temp = new CompletableFuture<>();
                 mTelephony.requestModemActivityInfo(Runnable::run,
@@ -689,17 +702,91 @@
         return delta;
     }
 
-    // TODO(b/172934873): Evaluate a safe way to query the HAL without holding mStats
+    /**
+     * Map the {@link MeasuredEnergyArray.MeasuredEnergySubsystem}s in the given energyArray to
+     * their corresponding {@link MeasuredEnergyStats.EnergyBucket}s.
+     *
+     * @return array with true for index i if energy bucket i is supported.
+     */
+    private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
+        if (energyArray == null) {
+            return null;
+        }
+        final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
+        final int size = energyArray.size();
+        for (int energyIdx = 0; energyIdx < size; energyIdx++) {
+            switch (energyArray.getSubsystem(energyIdx)) {
+                case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
+                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
+                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
+                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
+                    break;
+            }
+        }
+        return buckets;
+    }
+
+    /**
+     * Get a {@link MeasuredEnergyArray} with the latest
+     * {@link MeasuredEnergyArray.MeasuredEnergySubsystem} energy usage since boot.
+     *
+     * TODO(b/176988041): Replace {@link MeasuredEnergyArray} usage with {@link
+     * EnergyConsumerResult}[]
+     */
+    private static @Nullable
+            MeasuredEnergyArray getEnergyConsumptionData(@NonNull PowerStatsInternal psi) {
+        final EnergyConsumerResult[] results;
+        try {
+            results = psi.getEnergyConsumedAsync(new int[0])
+                    .get(EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to getEnergyConsumedAsync", e);
+            return null;
+        }
+        if (results == null) return null;
+        final int size = results.length;
+        final int[] subsystems = new int[size];
+        final long[] energyUJ = new long[size];
+
+        for (int i = 0; i < size; i++) {
+            final EnergyConsumerResult consumer = results[i];
+            final int subsystem;
+            switch (consumer.energyConsumerId) {
+                case EnergyConsumerId.DISPLAY:
+                    subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
+                    break;
+                default:
+                    continue;
+            }
+            subsystems[i] = subsystem;
+            energyUJ[i] = consumer.energyUWs;
+        }
+        return new MeasuredEnergyArray() {
+            @Override
+            public int getSubsystem(int index) {
+                return subsystems[index];
+            }
+
+            @Override
+            public long getEnergy(int index) {
+                return energyUJ[index];
+            }
+
+            @Override
+            public int size() {
+                return size;
+            }
+        };
+    }
+
     /** Fetch MeasuredEnergyArray for supported subsystems based on the given updateFlags. */
     @GuardedBy("mWorkerLock")
     private @Nullable MeasuredEnergyArray getMeasuredEnergyLocked(@ExternalUpdateFlag int flags) {
-        if (mMeasuredEnergySnapshot == null) return null;
+        if (mMeasuredEnergySnapshot == null || mPowerStatsInternal == null) return null;
 
         if (flags == UPDATE_ALL) {
             // Gotta catch 'em all... including custom (non-specific) subsystems
-            synchronized (mStats) {
-                return mStats.getEnergyConsumptionDataLocked();
-            }
+            return getEnergyConsumptionData(mPowerStatsInternal);
         }
 
         final List<Integer> energyConsumerIds = new ArrayList<>();
@@ -710,10 +797,8 @@
         if (energyConsumerIds.isEmpty()) {
             return null;
         }
-        synchronized (mStats) {
-            // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
-            return mStats.getEnergyConsumptionDataLocked();
-        }
+        // TODO: Query *specific* subsystems from HAL based on energyConsumerIds.toArray()
+        return getEnergyConsumptionData(mPowerStatsInternal);
     }
 
     @GuardedBy("mWorkerLock")
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2f7c523..405ee72 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,14 +16,11 @@
 
 package com.android.server.am;
 
-import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.hardware.power.stats.EnergyConsumerId;
-import android.hardware.power.stats.EnergyConsumerResult;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.BatteryUsageStats;
@@ -65,9 +62,6 @@
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.RailStats;
 import com.android.internal.os.RpmStats;
-import com.android.internal.power.MeasuredEnergyArray;
-import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem;
-import com.android.internal.power.MeasuredEnergyStats;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.ParseUtils;
@@ -75,7 +69,6 @@
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
 import com.android.server.pm.UserManagerInternal;
-import com.android.server.powerstats.PowerStatsHALWrapper;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -126,8 +119,6 @@
     private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
     private static final int MAX_LOW_POWER_STATS_SIZE = 4096;
 
-    private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper;
-
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
     private final Object mLock = new Object();
@@ -199,43 +190,6 @@
         }
     }
 
-    @Override
-    public @Nullable MeasuredEnergyArray getEnergyConsumptionData() {
-        final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]);
-        if (results == null) return null;
-        final int size = results.length;
-        final int[] subsystems = new int[size];
-        final long[] energyUJ = new long[size];
-
-        for (int i = 0; i < size; i++) {
-            final EnergyConsumerResult consumer = results[i];
-            final int subsystem;
-            switch (consumer.energyConsumerId) {
-                case EnergyConsumerId.DISPLAY:
-                    subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY;
-                    break;
-                default:
-                    continue;
-            }
-            subsystems[i] = subsystem;
-            energyUJ[i] = consumer.energyUWs;
-        }
-        return new MeasuredEnergyArray() {
-            @Override
-            public int getSubsystem(int index) {
-                return subsystems[index];
-            }
-            @Override
-            public long getEnergy(int index) {
-                return energyUJ[index];
-            }
-            @Override
-            public int size() {
-                return size;
-            }
-        };
-    }
-
     BatteryStatsService(Context context, File systemDir, Handler handler) {
         // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
         mContext = context;
@@ -253,14 +207,9 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
 
-        // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries
-        mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
-
-        final MeasuredEnergyArray initialEnergies = getEnergyConsumptionData();
-        final boolean[] supportedBuckets = getSupportedEnergyBuckets(initialEnergies);
         mStats = new BatteryStatsImpl(systemDir, handler, this,
-                this, supportedBuckets, mUserManagerUserInfoProvider);
-        mWorker = new BatteryExternalStatsWorker(context, mStats, initialEnergies);
+                this, mUserManagerUserInfoProvider);
+        mWorker = new BatteryExternalStatsWorker(context, mStats);
         mStats.setExternalStatsSyncLocked(mWorker);
         mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
@@ -268,30 +217,6 @@
         mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
     }
 
-    /**
-     * Map the {@link MeasuredEnergySubsystem}s in the given energyArray to their corresponding
-     * {@link MeasuredEnergyStats.EnergyBucket}s.
-     *
-     * @return array with true for index i if energy bucket i is supported.
-     */
-    private static @Nullable boolean[] getSupportedEnergyBuckets(MeasuredEnergyArray energyArray) {
-        if (energyArray == null) {
-            return null;
-        }
-        final boolean[] buckets = new boolean[MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS];
-        final int size = energyArray.size();
-        for (int energyIdx = 0; energyIdx < size; energyIdx++) {
-            switch (energyArray.getSubsystem(energyIdx)) {
-                case MeasuredEnergyArray.SUBSYSTEM_DISPLAY:
-                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON] = true;
-                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE] = true;
-                    buckets[MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_OTHER] = true;
-                    break;
-            }
-        }
-        return buckets;
-    }
-
     public void publish() {
         LocalServices.addService(BatteryStatsInternal.class, new LocalService());
         ServiceManager.addService(BatteryStats.SERVICE_NAME, asBinder());
@@ -299,6 +224,7 @@
 
     public void systemServicesReady() {
         mStats.systemServicesReady(mContext);
+        mWorker.systemServicesReady();
         Watchdog.getInstance().addMonitor(this);
     }
 
@@ -2270,7 +2196,6 @@
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
                                         null, mStats.mHandler, null, null,
-                                        null /* energy buckets not currently in checkin anyway */,
                                         mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
@@ -2311,7 +2236,6 @@
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
                                         null, mStats.mHandler, null, null,
-                                        null /* energy buckets not currently in checkin anyway */,
                                         mUserManagerUserInfoProvider);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
diff --git a/services/core/java/com/android/server/am/FgsStartTempAllowList.java b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
index 1f90393..3aca4cf 100644
--- a/services/core/java/com/android/server/am/FgsStartTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsStartTempAllowList.java
@@ -28,7 +28,7 @@
 final class FgsStartTempAllowList {
     private static final int MAX_SIZE = 100;
     /**
-     * The key is the UID, the value is expiration elapse time in ms of this temp-allowed UID.
+     * The key is the uid, the value is expiration elapse time in ms of this temp-allowed uid.
      */
     private final SparseLongArray mTempAllowListFgs = new SparseLongArray();
 
@@ -37,7 +37,8 @@
 
     void add(int uid, long duration) {
         if (duration <= 0) {
-            Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: " + uid);
+            Slog.e(TAG_AM, "FgsStartTempAllowList bad duration:" + duration + " uid: "
+                    + uid);
             return;
         }
         // The temp allowlist should be a short list with only a few entries in it.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index d69bf2d..de79315 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -92,7 +92,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LongSparseArray;
@@ -2731,11 +2730,11 @@
     }
 
     @GuardedBy("mService")
-    final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
+    final void setAppIdTempWhitelistStateLocked(int uid, boolean onWhitelist) {
         boolean changed = false;
         for (int i = mActiveUids.size() - 1; i >= 0; i--) {
             final UidRecord uidRec = mActiveUids.valueAt(i);
-            if (UserHandle.getAppId(uidRec.uid) == appId && uidRec.curWhitelist != onWhitelist) {
+            if (uidRec.uid == uid && uidRec.curWhitelist != onWhitelist) {
                 uidRec.curWhitelist = onWhitelist;
                 changed = true;
             }
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 676fcd0..17fd32c 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -1488,7 +1488,7 @@
         private void writeHistoricalPackageOpsDLocked(@NonNull HistoricalPackageOps packageOps,
                 @NonNull TypedXmlSerializer serializer) throws IOException {
             serializer.startTag(null, TAG_PACKAGE);
-            serializer.attribute(null, ATTR_NAME, packageOps.getPackageName());
+            serializer.attributeInterned(null, ATTR_NAME, packageOps.getPackageName());
             final int numAttributions = packageOps.getAttributedOpsCount();
             for (int i = 0; i < numAttributions; i++) {
                 final AppOpsManager.AttributedHistoricalOps op = packageOps.getAttributedOpsAt(i);
diff --git a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
index 2a3cc90..5fa7998 100644
--- a/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
+++ b/services/core/java/com/android/server/media/metrics/PlaybackMetricsManagerService.java
@@ -22,6 +22,7 @@
 import android.media.metrics.PlaybackErrorEvent;
 import android.media.metrics.PlaybackMetrics;
 import android.media.metrics.PlaybackStateEvent;
+import android.media.metrics.TrackChangeEvent;
 import android.os.Binder;
 import android.util.Base64;
 import android.util.StatsEvent;
@@ -120,5 +121,30 @@
                     .build();
             StatsLog.write(statsEvent);
         }
+
+        @Override
+        public void reportTrackChangeEvent(
+                String sessionId, TrackChangeEvent event, int userId) {
+            StatsEvent statsEvent = StatsEvent.newBuilder()
+                    .setAtomId(321)
+                    .writeString(sessionId)
+                    .writeInt(event.getTrackState())
+                    .writeInt(event.getTrackChangeReason())
+                    .writeString(event.getContainerMimeType())
+                    .writeString(event.getSampleMimeType())
+                    .writeString(event.getCodecName())
+                    .writeInt(event.getBitrate())
+                    .writeLong(event.getTimeSincePlaybackCreatedMillis())
+                    .writeInt(event.getTrackType())
+                    .writeString(event.getLanguage())
+                    .writeString(event.getLanguageRegion())
+                    .writeInt(event.getChannelCount())
+                    .writeInt(event.getSampleRate())
+                    .writeInt(event.getWidth())
+                    .writeInt(event.getHeight())
+                    .usePooledBuffer()
+                    .build();
+            StatsLog.write(statsEvent);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index b65fc73..2b5c393 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -71,6 +71,7 @@
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstallerSession;
 import android.content.pm.IPackageInstallerSessionFileSystemConnector;
+import android.content.pm.IPackageLoadingProgressCallback;
 import android.content.pm.InstallationFile;
 import android.content.pm.InstallationFileParcel;
 import android.content.pm.PackageInfo;
@@ -186,6 +187,7 @@
     static final String TAG_CHILD_SESSION = "childSession";
     static final String TAG_SESSION_FILE = "sessionFile";
     static final String TAG_SESSION_CHECKSUM = "sessionChecksum";
+    static final String TAG_SESSION_CHECKSUM_SIGNATURE = "sessionChecksumSignature";
     private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
     private static final String TAG_WHITELISTED_RESTRICTED_PERMISSION =
             "whitelisted-restricted-permission";
@@ -319,6 +321,8 @@
     private float mProgress = 0;
     @GuardedBy("mLock")
     private float mReportedProgress = -1;
+    @GuardedBy("mLock")
+    private float mIncrementalProgress = 0;
 
     /** State of the session. */
     @GuardedBy("mLock")
@@ -397,8 +401,26 @@
     @GuardedBy("mLock")
     private ArraySet<FileEntry> mFiles = new ArraySet<>();
 
+    static class PerFileChecksum {
+        private final Checksum[] mChecksums;
+        private final byte[] mSignature;
+
+        PerFileChecksum(Checksum[] checksums, byte[] signature) {
+            mChecksums = checksums;
+            mSignature = signature;
+        }
+
+        Checksum[] getChecksums() {
+            return this.mChecksums;
+        }
+
+        byte[] getSignature() {
+            return this.mSignature;
+        }
+    }
+
     @GuardedBy("mLock")
-    private ArrayMap<String, Checksum[]> mChecksums = new ArrayMap<>();
+    private ArrayMap<String, PerFileChecksum> mChecksums = new ArrayMap<>();
 
     @Nullable
     final StagedSession mStagedSession;
@@ -921,7 +943,7 @@
             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
             SessionParams params, long createdMillis, long committedMillis,
             File stageDir, String stageCid, InstallationFile[] files,
-            ArrayMap<String, List<Checksum>> checksums,
+            ArrayMap<String, PerFileChecksum> checksums,
             boolean prepared, boolean committed, boolean destroyed, boolean sealed,
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -967,11 +989,7 @@
         }
 
         if (checksums != null) {
-            for (int i = 0, isize = checksums.size(); i < isize; ++i) {
-                final String fileName = checksums.keyAt(i);
-                final List<Checksum> fileChecksums = checksums.valueAt(i);
-                mChecksums.put(fileName, fileChecksums.toArray(new Checksum[fileChecksums.size()]));
-            }
+            mChecksums.putAll(checksums);
         }
 
         if (!params.isMultiPackage && (stageDir == null) == (stageCid == null)) {
@@ -1182,7 +1200,12 @@
 
     @GuardedBy("mLock")
     private void computeProgressLocked(boolean forcePublish) {
-        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+        // This method is triggered when the client progress is updated or the incremental progress
+        // is updated. For incremental installs, ignore the progress values reported from client.
+        // Instead, only use the progress reported by IncFs as the percentage of loading completion.
+        final float loadingProgress =
+                isIncrementalInstallation() ? mIncrementalProgress : mClientProgress;
+        mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f)
                 + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
 
         // Only publish when meaningful change
@@ -1253,7 +1276,8 @@
     }
 
     @Override
-    public void addChecksums(String name, @NonNull Checksum[] checksums) {
+    public void setChecksums(String name, @NonNull Checksum[] checksums,
+            @Nullable byte[] signature) {
         if (checksums.length == 0) {
             return;
         }
@@ -1269,6 +1293,17 @@
             throw new IllegalStateException("Can't obtain calling installer's package.");
         }
 
+        if (signature != null && signature.length != 0) {
+            final boolean standardMode = PackageManagerServiceUtils.isApkVerityEnabled();
+            final boolean legacyMode = PackageManagerServiceUtils.isLegacyApkVerityEnabled();
+            if (!standardMode || legacyMode) {
+                Slog.e(TAG,
+                        "Can't enforce checksum's signature: Apk-Verity is disabled or in legacy "
+                                + "mode.");
+                signature = null;
+            }
+        }
+
         synchronized (mLock) {
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
@@ -1277,7 +1312,7 @@
                 throw new IllegalStateException("Duplicate checksums.");
             }
 
-            mChecksums.put(name, checksums);
+            mChecksums.put(name, new PerFileChecksum(checksums, signature));
         }
     }
 
@@ -3032,15 +3067,25 @@
         maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile);
     }
 
+    private void storeBytesToInstallationFile(final String localPath, final String absolutePath,
+            final byte[] bytes) throws IOException {
+        if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
+            FileUtils.bytesToFile(absolutePath, bytes);
+        } else {
+            mIncrementalFileStorages.makeFile(localPath, bytes);
+        }
+    }
+
     @GuardedBy("mLock")
     private void maybeStageDigestsLocked(File origFile, File targetFile, String splitName)
             throws PackageManagerException {
-        final Checksum[] checksums = mChecksums.get(origFile.getName());
-        if (checksums == null) {
+        final PerFileChecksum perFileChecksum = mChecksums.get(origFile.getName());
+        if (perFileChecksum == null) {
             return;
         }
         mChecksums.remove(origFile.getName());
 
+        final Checksum[] checksums = perFileChecksum.getChecksums();
         if (checksums.length == 0) {
             return;
         }
@@ -3048,14 +3093,24 @@
         final String targetDigestsPath = ApkChecksums.buildDigestsPathForApk(targetFile.getName());
         final File targetDigestsFile = new File(stageDir, targetDigestsPath);
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+            // Storing and staging checksums.
             ApkChecksums.writeChecksums(os, checksums);
-            final byte[] checksumsBytes = os.toByteArray();
+            storeBytesToInstallationFile(targetDigestsPath, targetDigestsFile.getAbsolutePath(),
+                    os.toByteArray());
+            stageFileLocked(targetDigestsFile, targetDigestsFile);
 
-            if (!isIncrementalInstallation() || mIncrementalFileStorages == null) {
-                FileUtils.bytesToFile(targetDigestsFile.getAbsolutePath(), checksumsBytes);
-            } else {
-                mIncrementalFileStorages.makeFile(targetDigestsPath, checksumsBytes);
+            final byte[] signature = perFileChecksum.getSignature();
+            if (signature == null || signature.length == 0) {
+                return;
             }
+
+            // Storing and staging signature.
+            final String targetDigestsSignaturePath = VerityUtils.getFsveritySignatureFilePath(
+                    targetDigestsPath);
+            final File targetDigestsSignatureFile = new File(stageDir, targetDigestsSignaturePath);
+            storeBytesToInstallationFile(targetDigestsSignaturePath,
+                    targetDigestsSignatureFile.getAbsolutePath(), signature);
+            stageFileLocked(targetDigestsSignatureFile, targetDigestsSignatureFile);
         } catch (CertificateException e) {
             throw new PackageManagerException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to encode certificate for " + mPackageName, e);
@@ -3063,8 +3118,6 @@
             throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
                     "Failed to store digests for " + mPackageName, e);
         }
-
-        stageFileLocked(targetDigestsFile, targetDigestsFile);
     }
 
     @GuardedBy("mLock")
@@ -3704,7 +3757,16 @@
             try {
                 mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
                         params, statusListener, healthCheckParams, healthListener, addedFiles,
-                        perUidReadTimeouts);
+                        perUidReadTimeouts,
+                        new IPackageLoadingProgressCallback.Stub() {
+                            @Override
+                            public void onPackageLoadingProgressChanged(float progress) {
+                                synchronized (mLock) {
+                                    mIncrementalProgress = progress;
+                                    computeProgressLocked(true);
+                                }
+                            }
+                        });
                 return false;
             } catch (IOException e) {
                 throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
@@ -4277,7 +4339,8 @@
 
             for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
                 final String fileName = mChecksums.keyAt(i);
-                final Checksum[] checksums = mChecksums.valueAt(i);
+                final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
+                final Checksum[] checksums = perFileChecksum.getChecksums();
                 for (Checksum checksum : checksums) {
                     out.startTag(null, TAG_SESSION_CHECKSUM);
                     writeStringAttribute(out, ATTR_NAME, fileName);
@@ -4286,6 +4349,19 @@
                     out.endTag(null, TAG_SESSION_CHECKSUM);
                 }
             }
+            for (int i = 0, isize = mChecksums.size(); i < isize; ++i) {
+                final String fileName = mChecksums.keyAt(i);
+                final PerFileChecksum perFileChecksum = mChecksums.valueAt(i);
+                final byte[] signature = perFileChecksum.getSignature();
+                if (signature == null || signature.length == 0) {
+                    continue;
+                }
+                out.startTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
+                writeStringAttribute(out, ATTR_NAME, fileName);
+                writeByteArrayAttribute(out, ATTR_SIGNATURE, signature);
+                out.endTag(null, TAG_SESSION_CHECKSUM_SIGNATURE);
+            }
+
         }
 
         out.endTag(null, TAG_SESSION);
@@ -4401,6 +4477,7 @@
         List<Integer> childSessionIds = new ArrayList<>();
         List<InstallationFile> files = new ArrayList<>();
         ArrayMap<String, List<Checksum>> checksums = new ArrayMap<>();
+        ArrayMap<String, byte[]> signatures = new ArrayMap<>();
         int outerDepth = in.getDepth();
         int type;
         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -4443,6 +4520,11 @@
                 }
                 fileChecksums.add(checksum);
             }
+            if (TAG_SESSION_CHECKSUM_SIGNATURE.equals(in.getName())) {
+                final String fileName = readStringAttribute(in, ATTR_NAME);
+                final byte[] signature = readByteArrayAttribute(in, ATTR_SIGNATURE);
+                signatures.put(fileName, signature);
+            }
         }
 
         if (grantedRuntimePermissions.size() > 0) {
@@ -4471,13 +4553,25 @@
             fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
         }
 
+        ArrayMap<String, PerFileChecksum> checksumsMap = null;
+        if (!checksums.isEmpty()) {
+            checksumsMap = new ArrayMap<>(checksums.size());
+            for (int i = 0, isize = checksums.size(); i < isize; ++i) {
+                final String fileName = checksums.keyAt(i);
+                final List<Checksum> perFileChecksum = checksums.valueAt(i);
+                final byte[] perFileSignature = signatures.get(fileName);
+                checksumsMap.put(fileName, new PerFileChecksum(
+                        perFileChecksum.toArray(new Checksum[perFileChecksum.size()]),
+                        perFileSignature));
+            }
+        }
+
         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
                 installOriginatingPackageName, installerPackageName, installerAttributionTag);
-        return new PackageInstallerSession(callback, context, pm, sessionProvider,
-                installerThread, stagingManager, sessionId, userId, installerUid,
-                installSource, params, createdMillis, committedMillis, stageDir, stageCid,
-                fileArray, checksums, prepared, committed, destroyed, sealed, childSessionIdsArray,
-                parentSessionId, isReady, isFailed, isApplied, stagedSessionErrorCode,
-                stagedSessionErrorMessage);
+        return new PackageInstallerSession(callback, context, pm, sessionProvider, installerThread,
+                stagingManager, sessionId, userId, installerUid, installSource, params,
+                createdMillis, committedMillis, stageDir, stageCid, fileArray, checksumsMap,
+                prepared, committed, destroyed, sealed, childSessionIdsArray, parentSessionId,
+                isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index de7338f..c93127d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -464,7 +464,7 @@
 /**
  * Keep track of all those APKs everywhere.
  * <p>
- * Internally there are two important locks:
+ * Internally there are three important locks:
  * <ul>
  * <li>{@link #mLock} is used to guard all in-memory parsed package details
  * and other related state. It is a fine-grained lock that should only be held
@@ -475,6 +475,10 @@
  * this lock should never be acquired while already holding {@link #mLock}.
  * Conversely, it's safe to acquire {@link #mLock} momentarily while already
  * holding {@link #mInstallLock}.
+ * <li>{@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot
+ * itself and the snapshot invalidation flag.  This lock should never be acquired while
+ * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock}
+ * momentarily while already holding {@link #mSnapshotLock}.
  * </ul>
  * Many internal methods rely on the caller to hold the appropriate locks, and
  * this contract is expressed through method name suffixes:
@@ -485,6 +489,8 @@
  * <li>fooLPr(): the caller must hold {@link #mLock} for reading
  * <li>fooLPw(): the caller must hold {@link #mLock} for writing
  * </ul>
+ * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}.  It
+ * should not be taken anywhere else or used for any other purpose.
  * <p>
  * Because this class is very central to the platform's security; please run all
  * CTS and unit tests whenever making modifications:
@@ -4727,11 +4733,19 @@
     // If true, the cached computer object is invalid (the cache is stale).
     // The attribute is static since it may be set from outside classes.
     private static volatile boolean sSnapshotInvalid = true;
-     // If true, the cache is corked.  Do not create a new cache but continue to use the
+    // If true, the cache is corked.  Do not create a new cache but continue to use the
     // existing one.  This throttles cache creation during periods of churn in Package
     // Manager.
     private static volatile boolean sSnapshotCorked = false;
 
+    /**
+     * This lock is used to make reads from {@link #sSnapshotInvalid} and
+     * {@link #mSnapshotComputer} atomic inside {@code snapshotComputer()}.  This lock is
+     * not meant to be used outside that method.  This lock must be taken before
+     * {@link #mLock} is taken.
+     */
+    private final Object mSnapshotLock = new Object();
+
     // A counter of all queries that hit the cache.
     private AtomicInteger mSnapshotHits = new AtomicInteger(0);
 
@@ -4759,35 +4773,42 @@
         if (!SNAPSHOT_ENABLED) {
             return mLiveComputer;
         }
+        if (Thread.holdsLock(mLock)) {
+            // If the current thread holds mLock then it may have modified state but not
+            // yet invalidated the snapshot.  Always give the thread the live computer.
+            return mLiveComputer;
+        }
         int hits = 0;
         if (TRACE_CACHES) {
             hits = mSnapshotHits.incrementAndGet();
         }
-        Computer c = mSnapshotComputer;
-        if (sSnapshotCorked && (c != null)) {
-            // Snapshots are corked, which means new ones should not be built right now.
+        synchronized (mSnapshotLock) {
+            Computer c = mSnapshotComputer;
+            if (sSnapshotCorked && (c != null)) {
+                // Snapshots are corked, which means new ones should not be built right now.
+                return c;
+            }
+            if (sSnapshotInvalid || (c == null)) {
+                // The snapshot is invalid if it is marked as invalid or if it is null.  If it
+                // is null, then it is currently being rebuilt by rebuildSnapshot().
+                synchronized (mLock) {
+                    // Rebuild the snapshot if it is invalid.  Note that the snapshot might be
+                    // invalidated as it is rebuilt.  However, the snapshot is still
+                    // self-consistent (the lock is being held)and is current as of the time
+                    // this function is entered.
+                    if (sSnapshotInvalid) {
+                        rebuildSnapshot(hits);
+                    }
+
+                    // Guaranteed to be non-null.  mSnapshotComputer is only be set to null
+                    // temporarily in rebuildSnapshot(), which is guarded by mLock().  Since
+                    // the mLock is held in this block and since rebuildSnapshot() is
+                    // complete, the attribute can not now be null.
+                    c = mSnapshotComputer;
+                }
+            }
             return c;
         }
-        if (sSnapshotInvalid || (c == null)) {
-            // The snapshot is invalid if it is marked as invalid or if it is null.  If it
-            // is null, then it is currently being rebuilt by rebuildSnapshot().
-            synchronized (mLock) {
-                // Rebuild the snapshot if it is invalid.  Note that the snapshot might be
-                // invalidated as it is rebuilt.  However, the snapshot is still
-                // self-consistent (the lock is being held)and is current as of the time
-                // this function is entered.
-                if (sSnapshotInvalid) {
-                    rebuildSnapshot(hits);
-                }
-
-                // Guaranteed to be non-null.  mSnapshotComputer is only be set to null
-                // temporarily in rebuildSnapshot(), which is guarded by mLock().  Since
-                // the mLock is held in this block and since rebuildSnapshot() is
-                // complete, the attribute can not now be null.
-                c = mSnapshotComputer;
-            }
-        }
-        return c;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 9f07695..89729b5 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -1049,6 +1049,7 @@
                             }
                         }
 
+                        // TODO: update resource strings in AppSearch
                         // If this shortcut is not from a manifest, then update all resource IDs
                         // from resource names.  (We don't allow resource strings for
                         // non-manifest at the moment, but icons can still be resources.)
@@ -1340,6 +1341,7 @@
      * For all the text fields, refresh the string values if they're from resources.
      */
     public void resolveResourceStrings() {
+        // TODO: update resource strings in AppSearch
         final ShortcutService s = mShortcutUser.mService;
 
         List<ShortcutInfo> changedShortcuts = null;
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 95ce140..3c4457d 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1565,6 +1565,7 @@
      * resource-based strings.
      */
     void fixUpShortcutResourceNamesAndValues(ShortcutInfo si) {
+        // TODO: update resource names in AppSearch
         final Resources publisherRes = injectGetResourcesForApplicationAsUser(
                 si.getPackage(), si.getUserId());
         if (publisherRes != null) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 7778572..64bddcd 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -19,14 +19,19 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.power.stats.ChannelInfo;
+import android.hardware.power.stats.EnergyConsumerResult;
 import android.hardware.power.stats.PowerEntityInfo;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.UserHandle;
+import android.power.PowerStatsInternal;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.SystemService;
 import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper;
 import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils;
@@ -36,6 +41,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * This class provides a system service that estimates system power usage
@@ -55,7 +61,6 @@
     private final Injector mInjector;
 
     private Context mContext;
-    private IPowerStatsHALWrapper mPowerStatsHALWrapper;
     @Nullable
     private PowerStatsLogger mPowerStatsLogger;
     @Nullable
@@ -65,6 +70,8 @@
 
     @VisibleForTesting
     static class Injector {
+        private IPowerStatsHALWrapper mPowerStatsHALWrapper;
+
         File createDataStoragePath() {
             return new File(Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
                 DATA_STORAGE_SUBDIR);
@@ -86,6 +93,13 @@
             return PowerStatsHALWrapper.getPowerStatsHalImpl();
         }
 
+        IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() {
+            if (mPowerStatsHALWrapper == null) {
+                mPowerStatsHALWrapper = PowerStatsHALWrapper.getPowerStatsHalImpl();
+            }
+            return mPowerStatsHALWrapper;
+        }
+
         PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
                 String meterFilename, String modelFilename, String residencyFilename,
                 IPowerStatsHALWrapper powerStatsHALWrapper) {
@@ -120,15 +134,15 @@
                     }
                 } else if (args.length == 0) {
                     pw.println("PowerStatsService dumpsys: available PowerEntityInfos");
-                    PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo();
+                    PowerEntityInfo[] powerEntityInfo = getPowerStatsHal().getPowerEntityInfo();
                     PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw);
 
                     pw.println("PowerStatsService dumpsys: available ChannelInfos");
-                    ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo();
+                    ChannelInfo[] channelInfo = getPowerStatsHal().getEnergyMeterInfo();
                     ChannelInfoUtils.dumpsys(channelInfo, pw);
 
                     pw.println("PowerStatsService dumpsys: available EnergyConsumerIds");
-                    int[] energyConsumerId = mPowerStatsHALWrapper.getEnergyConsumerInfo();
+                    int[] energyConsumerId = getPowerStatsHal().getEnergyConsumerInfo();
                     EnergyConsumerIdUtils.dumpsys(energyConsumerId, pw);
                 }
             }
@@ -144,27 +158,33 @@
 
     @Override
     public void onStart() {
+        if (getPowerStatsHal().isInitialized()) {
+            // Only create internal service if PowerStatsHal is available.
+            publishLocalService(PowerStatsInternal.class, new LocalService());
+        }
         publishBinderService(Context.POWER_STATS_SERVICE, new BinderService());
     }
 
     private void onSystemServiceReady() {
-        mPowerStatsHALWrapper = mInjector.createPowerStatsHALWrapperImpl();
-
-        if (mPowerStatsHALWrapper.isInitialized()) {
-            if (DEBUG) Slog.d(TAG, "Starting PowerStatsService");
+        if (getPowerStatsHal().isInitialized()) {
+            if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
 
             // Only start logger and triggers if initialization is successful.
             mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
                 mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
                 mInjector.createModelFilename(), mInjector.createResidencyFilename(),
-                mPowerStatsHALWrapper);
+                getPowerStatsHal());
             mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
             mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
         } else {
-            Slog.e(TAG, "Initialization of PowerStatsHAL wrapper failed");
+            Slog.e(TAG, "Failed to start PowerStatsService loggers");
         }
     }
 
+    private IPowerStatsHALWrapper getPowerStatsHal() {
+        return mInjector.getPowerStatsHALWrapperImpl();
+    }
+
     public PowerStatsService(Context context) {
         this(context, new Injector());
     }
@@ -175,4 +195,29 @@
         mContext = context;
         mInjector = injector;
     }
+
+    private final class LocalService extends PowerStatsInternal {
+        private final Handler mHandler;
+
+        LocalService() {
+            HandlerThread thread = new HandlerThread(TAG);
+            thread.start();
+            mHandler = new Handler(thread.getLooper());
+        }
+
+        @Override
+        public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+                int[] energyConsumerIds) {
+            final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture<>();
+            mHandler.sendMessage(
+                    PooledLambda.obtainMessage(PowerStatsService.this::getEnergyConsumedAsync,
+                            future, energyConsumerIds));
+            return future;
+        }
+    }
+
+    private void getEnergyConsumedAsync(CompletableFuture<EnergyConsumerResult[]> future,
+            int[] energyConsumerIds) {
+        future.complete(getPowerStatsHal().getEnergyConsumed(energyConsumerIds));
+    }
 }
diff --git a/services/core/java/com/android/server/utils/quota/OWNERS b/services/core/java/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..a2943f3
--- /dev/null
+++ b/services/core/java/com/android/server/utils/quota/OWNERS
@@ -0,0 +1,4 @@
+dplotnikov@google.com
+kwekua@google.com
+omakoto@google.com
+yamasani@google.com
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS
new file mode 100644
index 0000000..2e9e625
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/quota/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/services/core/java/com/android/server/utils/quota/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index 5f65440..a5039fb 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -82,6 +82,7 @@
     private PowerStatsLogger mPowerStatsLogger;
 
     private final PowerStatsService.Injector mInjector = new PowerStatsService.Injector() {
+        private TestPowerStatsHALWrapper mTestPowerStatsHALWrapper = new TestPowerStatsHALWrapper();
         @Override
         File createDataStoragePath() {
             mDataStorageDir = null;
@@ -111,8 +112,8 @@
         }
 
         @Override
-        IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
-            return new TestPowerStatsHALWrapper();
+        IPowerStatsHALWrapper getPowerStatsHALWrapperImpl() {
+            return mTestPowerStatsHALWrapper;
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 04b365f..717040a 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.MODIFY_PHONE_STATE;
 
+import android.Manifest;
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
@@ -3357,6 +3358,24 @@
      */
     public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
 
+    /**
+     * Indicates that call filtering in Telecom is complete
+     *
+     * This method is called for a connection created via
+     * {@link ConnectionService#onCreateIncomingConnection} when call filtering completes in
+     * Telecom, including checking the blocked number db, per-contact settings, and custom call
+     * filtering apps.
+     *
+     * @param isBlocked {@code true} if the call was blocked, {@code false} otherwise. If this is
+     *                  {@code true}, {@link #onDisconnect()} will be called soon after
+     *                  this is called.
+     * @param isInContacts Indicates whether the caller is in the user's contacts list.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_CONTACTS)
+    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) { }
+
     static String toLogSafePhoneNumber(String number) {
         // For unknown number, log empty string.
         if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b1ccb53..f86f9d5 100755
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -147,6 +147,7 @@
     private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
     private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
     private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
+    private static final String SESSION_CALL_FILTERING_COMPLETED = "CS.oCFC";
     private static final String SESSION_HANDOVER_COMPLETE = "CS.hC";
     private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
     private static final String SESSION_START_RTT = "CS.+RTT";
@@ -200,6 +201,7 @@
     private static final int MSG_ADD_PARTICIPANT = 39;
     private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
     private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
+    private static final int MSG_ON_CALL_FILTERING_COMPLETED = 42;
 
     private static Connection sNullConnection;
 
@@ -722,6 +724,22 @@
         }
 
         @Override
+        public void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+                Session.Info sessionInfo) {
+            Log.startSession(sessionInfo, SESSION_CALL_FILTERING_COMPLETED);
+            try {
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = callId;
+                args.arg2 = isBlocked;
+                args.arg3 = isInContacts;
+                args.arg4 = Log.createSubsession();
+                mHandler.obtainMessage(MSG_ON_CALL_FILTERING_COMPLETED, args).sendToTarget();
+            } finally {
+                Log.endSession();
+            }
+        }
+
+        @Override
         public void onExtrasChanged(String callId, Bundle extras, Session.Info sessionInfo) {
             Log.startSession(sessionInfo, SESSION_EXTRAS_CHANGED);
             try {
@@ -1354,6 +1372,21 @@
                     }
                     break;
                 }
+                case MSG_ON_CALL_FILTERING_COMPLETED: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Log.continueSession((Session) args.arg4,
+                                SESSION_HANDLER + SESSION_CALL_FILTERING_COMPLETED);
+                        String callId = (String) args.arg1;
+                        boolean isBlocked = (boolean) args.arg2;
+                        boolean isInContacts = (boolean) args.arg3;
+                        onCallFilteringCompleted(callId, isBlocked, isInContacts);
+                    } finally {
+                        args.recycle();
+                        Log.endSession();
+                    }
+                    break;
+                }
                 case MSG_HANDOVER_COMPLETE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
@@ -2345,6 +2378,14 @@
         }
     }
 
+    private void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts) {
+        Log.i(this, "onCallFilteringCompleted(%s, %b, %b)", isBlocked, isInContacts);
+        Connection connection = findConnectionForAction(callId, "onCallFilteringCompleted");
+        if (connection != null) {
+            connection.onCallFilteringCompleted(isBlocked, isInContacts);
+        }
+    }
+
     /**
      * Notifies a {@link Connection} that a handover has completed.
      *
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 52210a5..feb2ca5 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -16,8 +16,10 @@
 
 package android.telecom;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.hardware.camera2.CameraManager;
 import android.net.Uri;
@@ -1198,6 +1200,28 @@
     }
 
     /**
+     * Notifies this {@link RemoteConnection} that call filtering has completed, as well as
+     * the results of a contacts lookup for the remote party.
+     * @param isBlocked Whether call filtering indicates that the call should be blocked
+     * @param isInContacts Whether the remote party is in the user's contacts
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.READ_CONTACTS)
+    public void onCallFilteringCompleted(boolean isBlocked, boolean isInContacts) {
+        Log.startSession("RC.oCFC", getActiveOwnerInfo());
+        try {
+            if (mConnected) {
+                mConnectionService.onCallFilteringCompleted(mConnectionId, isBlocked, isInContacts,
+                        null /*Session.Info*/);
+            }
+        } catch (RemoteException ignored) {
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    /**
      * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
      * upgrade request sent via {@link Connection#sendRemoteRttRequest}.
      * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index fb54179..92264be 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -118,6 +118,9 @@
 
     void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
 
+    void onCallFilteringCompleted(String callId, boolean isBlocked, boolean isInContacts,
+            in Session.Info sessionInfo);
+
     void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
 
     void startRtt(String callId, in ParcelFileDescriptor fromInCall,
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 3c08d34..c04ddd7 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -16,6 +16,7 @@
         "frameworks-base-testutils",
         "framework-protos",
         "mockito-target-minus-junit4",
+        "net-tests-utils",
         "platform-test-annotations",
         "services.core",
     ],
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
new file mode 100644
index 0000000..9c6b719
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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.net.vcn;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.util.concurrent.Executor;
+
+public class VcnManagerTest {
+    private static final Executor INLINE_EXECUTOR = Runnable::run;
+
+    private IVcnManagementService mMockVcnManagementService;
+    private VcnUnderlyingNetworkPolicyListener mMockPolicyListener;
+
+    private Context mContext;
+    private VcnManager mVcnManager;
+
+    @Before
+    public void setUp() {
+        mMockVcnManagementService = mock(IVcnManagementService.class);
+        mMockPolicyListener = mock(VcnUnderlyingNetworkPolicyListener.class);
+
+        mContext = getContext();
+        mVcnManager = new VcnManager(mContext, mMockVcnManagementService);
+    }
+
+    @Test
+    public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+        mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+        ArgumentCaptor<IVcnUnderlyingNetworkPolicyListener> captor =
+                ArgumentCaptor.forClass(IVcnUnderlyingNetworkPolicyListener.class);
+        verify(mMockVcnManagementService).addVcnUnderlyingNetworkPolicyListener(captor.capture());
+
+        assertTrue(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+
+        IVcnUnderlyingNetworkPolicyListener listenerWrapper = captor.getValue();
+        listenerWrapper.onPolicyChanged();
+        verify(mMockPolicyListener).onPolicyChanged();
+    }
+
+    @Test
+    public void testRemoveVcnUnderlyingNetworkPolicyListener() throws Exception {
+        mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, mMockPolicyListener);
+
+        mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+        verify(mMockVcnManagementService)
+                .addVcnUnderlyingNetworkPolicyListener(
+                        any(IVcnUnderlyingNetworkPolicyListener.class));
+    }
+
+    @Test
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerUnknownListener() throws Exception {
+        mVcnManager.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+        assertFalse(VcnManager.REGISTERED_POLICY_LISTENERS.containsKey(mMockPolicyListener));
+        verify(mMockVcnManagementService, never())
+                .addVcnUnderlyingNetworkPolicyListener(
+                        any(IVcnUnderlyingNetworkPolicyListener.class));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testAddVcnUnderlyingNetworkPolicyListenerNullExecutor() throws Exception {
+        mVcnManager.addVcnUnderlyingNetworkPolicyListener(null, mMockPolicyListener);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testAddVcnUnderlyingNetworkPolicyListenerNullListener() throws Exception {
+        mVcnManager.addVcnUnderlyingNetworkPolicyListener(INLINE_EXECUTOR, null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testRemoveVcnUnderlyingNetworkPolicyListenerNullListener() {
+        mVcnManager.removeVcnUnderlyingNetworkPolicyListener(null);
+    }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
new file mode 100644
index 0000000..3ba0a1f
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.net.vcn;
+
+import static com.android.testutils.ParcelUtils.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.net.NetworkCapabilities;
+
+import org.junit.Test;
+
+public class VcnUnderlyingNetworkPolicyTest {
+    private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY =
+            new VcnUnderlyingNetworkPolicy(
+                    false /* isTearDownRequested */, new NetworkCapabilities());
+    private static final VcnUnderlyingNetworkPolicy SAMPLE_NETWORK_POLICY =
+            new VcnUnderlyingNetworkPolicy(
+                    true /* isTearDownRequested */,
+                    new NetworkCapabilities.Builder()
+                            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                            .build());
+
+    @Test
+    public void testEquals() {
+        assertEquals(DEFAULT_NETWORK_POLICY, DEFAULT_NETWORK_POLICY);
+        assertEquals(SAMPLE_NETWORK_POLICY, SAMPLE_NETWORK_POLICY);
+
+        assertNotEquals(DEFAULT_NETWORK_POLICY, SAMPLE_NETWORK_POLICY);
+    }
+
+    @Test
+    public void testParcelUnparcel() {
+        assertParcelSane(SAMPLE_NETWORK_POLICY, 2);
+    }
+}