Merge "Use CS identity to update setting while performing factory reset"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 626f977..e194c47 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -251,7 +251,7 @@
 
 modules_system_stubs = [
     "android.net.ipsec.ike.stubs.system",
-    "art.module.public.api.stubs", // Only has public stubs
+    "art.module.public.api.stubs.system",
     "conscrypt.module.public.api.stubs", // Only has public stubs
     "framework-connectivity.stubs.system",
     "framework-media.stubs.system",
diff --git a/api/Android.bp b/api/Android.bp
index 5b2d97f..438e7dc 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -149,6 +149,7 @@
 genrule {
     name: "frameworks-base-api-system-current.txt",
     srcs: [
+        ":art.module.public.api{.system.api.txt}",
         ":android.net.ipsec.ike{.system.api.txt}",
         ":framework-connectivity{.system.api.txt}",
         ":framework-media{.system.api.txt}",
@@ -199,6 +200,7 @@
 genrule {
     name: "frameworks-base-api-system-removed.txt",
     srcs: [
+        ":art.module.public.api{.system.removed-api.txt}",
         ":android.net.ipsec.ike{.system.removed-api.txt}",
         ":framework-connectivity{.system.removed-api.txt}",
         ":framework-media{.system.removed-api.txt}",
@@ -231,6 +233,7 @@
 genrule {
     name: "frameworks-base-api-module-lib-current.txt",
     srcs: [
+        ":art.module.public.api{.module-lib.api.txt}",
         ":android.net.ipsec.ike{.module-lib.api.txt}",
         ":framework-connectivity{.module-lib.api.txt}",
         ":framework-media{.module-lib.api.txt}",
@@ -283,6 +286,7 @@
 genrule {
     name: "frameworks-base-api-module-lib-removed.txt",
     srcs: [
+        ":art.module.public.api{.module-lib.removed-api.txt}",
         ":android.net.ipsec.ike{.module-lib.removed-api.txt}",
         ":framework-connectivity{.module-lib.removed-api.txt}",
         ":framework-media{.module-lib.removed-api.txt}",
diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS
index f1903a5..69dfcc9 100644
--- a/cmds/idmap2/OWNERS
+++ b/cmds/idmap2/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 toddke@google.com
-rtmitchell@google.com
\ No newline at end of file
+rtmitchell@google.com
+patb@google.com
\ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index cd4e6f9..e32f07b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -25711,7 +25711,7 @@
   public final class VcnGatewayConnectionConfig {
     method @NonNull public int[] getExposedCapabilities();
     method @NonNull public String getGatewayConnectionName();
-    method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu();
+    method @IntRange(from=0x500) public int getMaxMtu();
     method @NonNull public long[] getRetryIntervalsMillis();
   }
 
@@ -25720,7 +25720,7 @@
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
-    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int);
+    method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]);
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index bb42ddc..1aa8aed 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1124,10 +1124,10 @@
 package android.app.compat {
 
   public final class CompatChanges {
-    method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void addPackageOverrides(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
     method public static boolean isChangeEnabled(long);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle);
     method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int);
+    method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void putPackageOverrides(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>);
     method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void removePackageOverrides(@NonNull String, @NonNull java.util.Set<java.lang.Long>);
   }
 
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 5bc9992..a172c05 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -138,23 +138,25 @@
         // NOTE: Keep this in sync with installd expectations.
         File dexPathFile = new File(dexPath);
         File secondaryProfileDir = new File(dexPathFile.getParent(), "oat");
-        File secondaryProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof");
+        File secondaryCurProfile =
+                new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof");
+        File secondaryRefProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".prof");
 
         // Create the profile if not already there.
         // Returns true if the file was created, false if the file already exists.
         // or throws exceptions in case of errors.
         if (!secondaryProfileDir.exists()) {
             if (!secondaryProfileDir.mkdir()) {
-                Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile);
+                Slog.e(TAG, "Could not create the profile directory: " + secondaryCurProfile);
                 // Do not continue with registration if we could not create the oat dir.
                 return;
             }
         }
 
         try {
-            boolean created = secondaryProfile.createNewFile();
+            boolean created = secondaryCurProfile.createNewFile();
             if (DEBUG && created) {
-                Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
+                Slog.i(TAG, "Created profile for secondary dex: " + secondaryCurProfile);
             }
         } catch (IOException ex) {
             Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath
@@ -165,7 +167,12 @@
 
         // If we got here, the dex paths is a secondary dex and we were able to create the profile.
         // Register the path to the runtime.
-        VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
+        VMRuntime.registerAppInfo(
+                ActivityThread.currentPackageName(),
+                secondaryCurProfile.getPath(),
+                secondaryRefProfile.getPath(),
+                new String[] { dexPath },
+                VMRuntime.CODE_PATH_TYPE_SECONDARY_DEX);
     }
 
     // A dex file is a secondary dex file if it is in any of the registered app
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b45f389..5d2370d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -884,7 +884,7 @@
         if (DEBUG) Slog.v(ActivityThread.TAG, "Class path: " + zip +
                     ", JNI path: " + librarySearchPath);
 
-        boolean needToSetupJitProfiles = false;
+        boolean registerAppInfoToArt = false;
         if (mDefaultClassLoader == null) {
             // Temporarily disable logging of disk reads on the Looper thread
             // as this is early and necessary.
@@ -902,7 +902,7 @@
 
             setThreadPolicy(oldPolicy);
             // Setup the class loader paths for profiling.
-            needToSetupJitProfiles = true;
+            registerAppInfoToArt = true;
         }
 
         if (!libPaths.isEmpty()) {
@@ -919,7 +919,7 @@
             final String add = TextUtils.join(File.pathSeparator, addedPaths);
             ApplicationLoaders.getDefault().addPath(mDefaultClassLoader, add);
             // Setup the new code paths for profiling.
-            needToSetupJitProfiles = true;
+            registerAppInfoToArt = true;
         }
 
         // Setup jit profile support.
@@ -933,8 +933,8 @@
         // loads code from) so we explicitly disallow it there.
         //
         // It is not ok to call this in a zygote context where mActivityThread is null.
-        if (needToSetupJitProfiles && !ActivityThread.isSystem() && mActivityThread != null) {
-            setupJitProfileSupport();
+        if (registerAppInfoToArt && !ActivityThread.isSystem() && mActivityThread != null) {
+            registerAppInfoToArt();
         }
 
         // Call AppComponentFactory to select/create the main class loader of this app.
@@ -984,12 +984,8 @@
         }
     }
 
-    private void setupJitProfileSupport() {
-        if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
-            return;
-        }
-
-        // If we use profiles, setup the dex reporter to notify package manager
+    private void registerAppInfoToArt() {
+        // Setup the dex reporter to notify package manager
         // of any relevant dex loads. The idle maintenance job will use the information
         // reported to optimize the loaded dex files.
         // Note that we only need one global reporter per app.
@@ -1022,9 +1018,19 @@
 
         for (int i = codePaths.size() - 1; i >= 0; i--) {
             String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1];
-            String profileFile = ArtManager.getCurrentProfilePath(
+            String curProfileFile = ArtManager.getCurrentProfilePath(
                     mPackageName, UserHandle.myUserId(), splitName);
-            VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)});
+            String refProfileFile = ArtManager.getReferenceProfilePath(
+                    mPackageName, UserHandle.myUserId(), splitName);
+            int codePathType = codePaths.get(i).equals(mApplicationInfo.sourceDir)
+                    ? VMRuntime.CODE_PATH_TYPE_PRIMARY_APK
+                    : VMRuntime.CODE_PATH_TYPE_SPLIT_APK;
+            VMRuntime.registerAppInfo(
+                    mPackageName,
+                    curProfileFile,
+                    refProfileFile,
+                    new String[] {codePaths.get(i)},
+                    codePathType);
         }
 
         // Register the app data directory with the reporter. It will
diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS
index 21c39a8..5582803 100644
--- a/core/java/android/app/RESOURCES_OWNERS
+++ b/core/java/android/app/RESOURCES_OWNERS
@@ -1,2 +1,3 @@
 rtmitchell@google.com
 toddke@google.com
+patb@google.com
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 8ca43c4..24ca72c 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -100,9 +100,10 @@
     }
 
     /**
-     * Adds app compat overrides for a given package. This will check whether the caller is allowed
-     * to perform this operation on the given apk and build. Only the installer package is allowed
-     * to set overrides on a non-debuggable final build and a non-test apk.
+     * Associates app compat overrides with the given package and their respective change IDs.
+     * This will check whether the caller is allowed to perform this operation on the given apk and
+     * build. Only the installer package is allowed to set overrides on a non-debuggable final
+     * build and a non-test apk.
      *
      * <p>Note that calling this method doesn't remove previously added overrides for the given
      * package if their change ID isn't in the given map, only replaces those that have the same
@@ -112,7 +113,7 @@
      * @param overrides A map from change ID to the override applied for this change ID.
      */
     @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD)
-    public static void addPackageOverrides(@NonNull String packageName,
+    public static void putPackageOverrides(@NonNull String packageName,
             @NonNull Map<Long, PackageOverride> overrides) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
@@ -125,7 +126,7 @@
     }
 
     /**
-     * Removes app compat overrides for a given package. This will check whether the caller is
+     * Removes app compat overrides for the given package. This will check whether the caller is
      * allowed to perform this operation on the given apk and build. Only the installer package is
      * allowed to clear overrides on a non-debuggable final build and a non-test apk.
      *
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 6cb4b5e..fe99f85 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -644,7 +644,10 @@
                         : NetworkTemplate.buildTemplateMobileAll(subscriberId);
                 break;
             case ConnectivityManager.TYPE_WIFI:
-                template = NetworkTemplate.buildTemplateWifiWildcard();
+                template = subscriberId == null
+                        ? NetworkTemplate.buildTemplateWifiWildcard()
+                        : NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL,
+                                subscriberId);
                 break;
             default:
                 throw new IllegalArgumentException("Cannot create template for network type "
diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java
index b0970f4..009ebb9 100644
--- a/core/java/android/content/pm/dex/ArtManager.java
+++ b/core/java/android/content/pm/dex/ArtManager.java
@@ -201,6 +201,16 @@
     }
 
     /**
+     * Return the path to the current profile corresponding to given package and split.
+     *
+     * @hide
+     */
+    public static String getReferenceProfilePath(String packageName, int userId, String splitName) {
+        File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName);
+        return new File(profileDir, getProfileName(splitName)).getAbsolutePath();
+    }
+
+    /**
      * Return the snapshot profile file for the given package and profile name.
      *
      * KEEP in sync with installd dexopt.cpp.
diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
index 14992fb..3740ef7 100644
--- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
+++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java
@@ -119,6 +119,7 @@
             // how many APKs they're going through.
             mDeferredErrors.erase();
         }
+        mTargetSdkVersion = null;
         return this;
     }
 
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 01d1aa5..c106807 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -810,7 +810,9 @@
          *
          * @param underlyingNetwork the new {@link Network} that will carry traffic for this tunnel.
          *     This network MUST never be the network exposing this IpSecTunnelInterface, otherwise
-         *     this method will throw an {@link IllegalArgumentException}.
+         *     this method will throw an {@link IllegalArgumentException}. If the
+         *     IpSecTunnelInterface is later added to this network, all outbound traffic will be
+         *     blackholed.
          */
         // TODO: b/169171001 Update the documentation when transform migration is supported.
         // The purpose of making updating network and applying transforms separate is to leave open
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 3bde6fa..1d07a03 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -26,8 +26,10 @@
 import android.telephony.Annotation.NetworkType;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.net.module.util.NetworkCapabilitiesUtils;
 import com.android.net.module.util.NetworkIdentityUtils;
 
+import java.util.ArrayList;
 import java.util.Objects;
 
 /**
@@ -121,11 +123,37 @@
         }
         builder.append(", metered=").append(mMetered);
         builder.append(", defaultNetwork=").append(mDefaultNetwork);
-        // TODO(180557699): Print a human readable string for OEM managed state.
-        builder.append(", oemManaged=").append(mOemManaged);
+        builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
         return builder.append("}").toString();
     }
 
+    /**
+     * Get the human readable representation of a bitfield representing the OEM managed state of a
+     * network.
+     */
+    static String getOemManagedNames(int oemManaged) {
+        if (oemManaged == OEM_NONE) {
+            return "OEM_NONE";
+        }
+        final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged);
+        final ArrayList<String> oemManagedNames = new ArrayList<String>();
+        for (int position : bitPositions) {
+            oemManagedNames.add(nameOfOemManaged(1 << position));
+        }
+        return String.join(",", oemManagedNames);
+    }
+
+    private static String nameOfOemManaged(int oemManagedBit) {
+        switch (oemManagedBit) {
+            case OEM_PAID:
+                return "OEM_PAID";
+            case OEM_PRIVATE:
+                return "OEM_PRIVATE";
+            default:
+                return "Invalid(" + oemManagedBit + ")";
+        }
+    }
+
     public void dumpDebug(ProtoOutputStream proto, long tag) {
         final long start = proto.start(tag);
 
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index d3c8957..352f2e9 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -274,11 +274,14 @@
     }
 
     /**
-     * Template to match all carrier networks with the given IMSI.
+     * Template to match all metered carrier networks with the given IMSI.
      */
-    public static NetworkTemplate buildTemplateCarrier(@NonNull String subscriberId) {
+    public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) {
         Objects.requireNonNull(subscriberId);
-        return new NetworkTemplate(MATCH_CARRIER, subscriberId, null);
+        return new NetworkTemplate(MATCH_CARRIER, subscriberId,
+                new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL,
+                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
+                SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     private final int mMatchRule;
@@ -424,7 +427,7 @@
             builder.append(", subType=").append(mSubType);
         }
         if (mOemManaged != OEM_MANAGED_ALL) {
-            builder.append(", oemManaged=").append(mOemManaged);
+            builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged));
         }
         builder.append(", subscriberIdMatchRule=")
                 .append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule));
@@ -774,6 +777,19 @@
         }
     }
 
+    private static String getOemManagedNames(int oemManaged) {
+        switch (oemManaged) {
+            case OEM_MANAGED_ALL:
+                return "OEM_MANAGED_ALL";
+            case OEM_MANAGED_NO:
+                return "OEM_MANAGED_NO";
+            case OEM_MANAGED_YES:
+                return "OEM_MANAGED_YES";
+            default:
+                return NetworkIdentity.getOemManagedNames(oemManaged);
+        }
+    }
+
     /**
      * Examine the given template and normalize if it refers to a "merged"
      * mobile subscriber. We pick the "lowest" merged subscriber as the primary
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 7a837e1..339371b 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -302,7 +302,7 @@
                         new MyReadMapCallback()));
             }
         }
-        return EMPTY;
+        return new PersistableBundle();  // An empty mutable PersistableBundle
     }
 
     @Override
diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS
index 3795493..47eee64 100644
--- a/core/java/android/os/incremental/OWNERS
+++ b/core/java/android/os/incremental/OWNERS
@@ -3,3 +3,4 @@
 schfan@google.com
 toddke@google.com
 zyy@google.com
+patb@google.com
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c34b9f0..dd2940f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -40,6 +40,7 @@
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBatteryPropertiesRegistrar;
@@ -71,6 +72,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.IntArray;
 import android.util.KeyValueListParser;
@@ -122,6 +124,7 @@
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -6126,6 +6129,18 @@
         }
     }
 
+    /**
+     * Records timing data related to an incoming Binder call in order to attribute
+     * the power consumption to the calling app.
+     */
+    public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
+            Collection<BinderCallsStats.CallStat> callStats) {
+        synchronized (this) {
+            getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
+                    callStats);
+        }
+    }
+
     public String[] getWifiIfaces() {
         synchronized (mWifiNetworkLock) {
             return mWifiIfaces;
@@ -6569,6 +6584,65 @@
     }
 
     /**
+     * Accumulates stats for a specific binder transaction.
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected static class BinderCallStats {
+        static final Comparator<BinderCallStats> COMPARATOR =
+                Comparator.comparing(BinderCallStats::getClassName)
+                        .thenComparing(BinderCallStats::getMethodName);
+
+        public Class<? extends Binder> binderClass;
+        public int transactionCode;
+        public String methodName;
+
+        public long callCount;
+        public long recordedCallCount;
+        public long recordedCpuTimeMicros;
+
+
+        @Override
+        public int hashCode() {
+            return binderClass.hashCode() * 31 + transactionCode;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof BinderCallStats)) {
+                return false;
+            }
+            BinderCallStats bcsk = (BinderCallStats) obj;
+            return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode;
+        }
+
+        public String getClassName() {
+            return binderClass.getName();
+        }
+
+        public String getMethodName() {
+            return methodName;
+        }
+
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void ensureMethodName(BinderTransactionNameResolver resolver) {
+            if (methodName == null) {
+                methodName = resolver.getMethodName(binderClass, transactionCode);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "BinderCallStats{"
+                    + binderClass
+                    + " transaction=" + transactionCode
+                    + " callCount=" + callCount
+                    + " recordedCallCount=" + recordedCallCount
+                    + " recorderCpuTimeMicros=" + recordedCpuTimeMicros
+                    + "}";
+        }
+    }
+
+    /**
      * The statistics associated with a particular uid.
      */
     public static class Uid extends BatteryStats.Uid {
@@ -6741,6 +6815,16 @@
          */
         final SparseArray<Pid> mPids = new SparseArray<>();
 
+        /**
+         * Grand total of system server binder calls made by this uid.
+         */
+        private long mBinderCallCount;
+
+        /**
+         * Detailed information about system server binder calls made by this uid.
+         */
+        private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
+
         public Uid(BatteryStatsImpl bsi, int uid) {
             mBsi = bsi;
             mUid = uid;
@@ -6849,6 +6933,14 @@
             return nullIfAllZeros(mProcStateScreenOffTimeMs[procState], which);
         }
 
+        public long getBinderCallCount() {
+            return mBinderCallCount;
+        }
+
+        public ArraySet<BinderCallStats> getBinderCallStats() {
+            return mBinderCallStats;
+        }
+
         public void addIsolatedUid(int isolatedUid) {
             if (mChildUids == null) {
                 mChildUids = new IntArray();
@@ -7937,6 +8029,9 @@
             }
             mPackageStats.clear();
 
+            mBinderCallCount = 0;
+            mBinderCallStats.clear();
+
             mLastStepUserTime = mLastStepSystemTime = 0;
             mCurStepUserTime = mCurStepSystemTime = 0;
 
@@ -8692,6 +8787,40 @@
             }
         }
 
+        // Reusable object used as a key to lookup values in mBinderCallStats
+        private static BinderCallStats sTempBinderCallStats = new BinderCallStats();
+
+        /**
+         * Notes incoming binder call stats associated with this work source UID.
+         */
+        public void noteBinderCallStatsLocked(long incrementalCallCount,
+                Collection<BinderCallsStats.CallStat> callStats) {
+            if (DEBUG) {
+                Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], "
+                        + " incrementalCallCount: " + incrementalCallCount + " callStats = ["
+                        + new ArrayList<>(callStats) + "]");
+            }
+            mBinderCallCount += incrementalCallCount;
+            for (BinderCallsStats.CallStat stat : callStats) {
+                BinderCallStats bcs;
+                sTempBinderCallStats.binderClass = stat.binderClass;
+                sTempBinderCallStats.transactionCode = stat.transactionCode;
+                int index = mBinderCallStats.indexOf(sTempBinderCallStats);
+                if (index >= 0) {
+                    bcs = mBinderCallStats.valueAt(index);
+                } else {
+                    bcs = new BinderCallStats();
+                    bcs.binderClass = stat.binderClass;
+                    bcs.transactionCode = stat.transactionCode;
+                    mBinderCallStats.add(bcs);
+                }
+
+                bcs.callCount += stat.incrementalCallCount;
+                bcs.recordedCallCount = stat.recordedCallCount;
+                bcs.recordedCpuTimeMicros = stat.cpuTimeMicros;
+            }
+        }
+
         /**
          * The statistics associated with a particular wake lock.
          */
@@ -13213,6 +13342,45 @@
             pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
             pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
         }
+        pw.println("Per UID system service calls:");
+        BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            Uid uid = mUidStats.get(u);
+            long binderCallCount = uid.getBinderCallCount();
+            if (binderCallCount != 0) {
+                pw.print(" ");
+                pw.print(u);
+                pw.print(" system service calls: ");
+                pw.print(binderCallCount);
+                ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
+                if (!binderCallStats.isEmpty()) {
+                    pw.println(", including");
+                    BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
+                    binderCallStats.toArray(bcss);
+                    for (BinderCallStats bcs : bcss) {
+                        bcs.ensureMethodName(nameResolver);
+                    }
+                    Arrays.sort(bcss, BinderCallStats.COMPARATOR);
+                    for (BinderCallStats callStats : bcss) {
+                        pw.print("    ");
+                        pw.print(callStats.getClassName());
+                        pw.print('#');
+                        pw.print(callStats.getMethodName());
+                        pw.print(" calls: ");
+                        pw.print(callStats.callCount);
+                        if (callStats.recordedCallCount != 0) {
+                            pw.print(" time: ");
+                            pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
+                                    / callStats.recordedCallCount / 1000);
+                        }
+                        pw.println();
+                    }
+                } else {
+                    pw.println();
+                }
+            }
+        }
         pw.println("Per UID CPU active time in ms:");
         for (int i = 0; i < size; i++) {
             int u = mUidStats.keyAt(i);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index dbba469..a3dbd30 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -19,10 +19,14 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -32,13 +36,10 @@
 import com.android.internal.os.BinderInternal.CallSession;
 
 import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
 import java.util.Queue;
 import java.util.Random;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -95,7 +96,38 @@
     private CachedDeviceState.Readonly mDeviceState;
     private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch;
 
+    private static final int CALL_STATS_OBSERVER_DEBOUNCE_MILLIS = 5000;
     private BinderLatencyObserver mLatencyObserver;
+    private BinderInternal.CallStatsObserver mCallStatsObserver;
+    private ArraySet<Integer> mSendUidsToObserver = new ArraySet<>(32);
+    private final Handler mCallStatsObserverHandler;
+    private Runnable mCallStatsObserverRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mCallStatsObserver == null) {
+                return;
+            }
+
+            noteCallsStatsDelayed();
+
+            synchronized (mLock) {
+                int size = mSendUidsToObserver.size();
+                for (int i = 0; i < size; i++) {
+                    UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i));
+                    if (uidEntry != null) {
+                        ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
+                        mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
+                                uidEntry.incrementalCallCount, callStats.values());
+                        uidEntry.incrementalCallCount = 0;
+                        for (int j = callStats.size() - 1; j >= 0; j--) {
+                            callStats.valueAt(j).incrementalCallCount = 0;
+                        }
+                    }
+                }
+                mSendUidsToObserver.clear();
+            }
+        }
+    };
 
     /** Injector for {@link BinderCallsStats}. */
     public static class Injector {
@@ -103,6 +135,10 @@
             return new Random();
         }
 
+        public Handler getHandler() {
+            return new Handler(Looper.getMainLooper());
+        }
+
         public BinderLatencyObserver getLatencyObserver() {
             return new BinderLatencyObserver(new BinderLatencyObserver.Injector());
         }
@@ -110,6 +146,7 @@
 
     public BinderCallsStats(Injector injector) {
         this.mRandom = injector.getRandomGenerator();
+        this.mCallStatsObserverHandler = injector.getHandler();
         this.mLatencyObserver = injector.getLatencyObserver();
     }
 
@@ -121,6 +158,24 @@
         mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch();
     }
 
+    /**
+     * Registers an observer for call stats, which is invoked periodically with accumulated
+     * binder call stats.
+     */
+    public void setCallStatsObserver(
+            BinderInternal.CallStatsObserver callStatsObserver) {
+        mCallStatsObserver = callStatsObserver;
+        noteCallsStatsDelayed();
+    }
+
+    private void noteCallsStatsDelayed() {
+        mCallStatsObserverHandler.removeCallbacks(mCallStatsObserverRunnable);
+        if (mCallStatsObserver != null) {
+            mCallStatsObserverHandler.postDelayed(mCallStatsObserverRunnable,
+                    CALL_STATS_OBSERVER_DEBOUNCE_MILLIS);
+        }
+    }
+
     @Override
     @Nullable
     public CallSession callStarted(Binder binder, int code, int workSourceUid) {
@@ -195,6 +250,7 @@
 
             final UidEntry uidEntry = getUidEntry(workSourceUid);
             uidEntry.callCount++;
+            uidEntry.incrementalCallCount++;
 
             if (recordCall) {
                 uidEntry.cpuTimeMicros += duration;
@@ -210,6 +266,7 @@
                 }
 
                 callStat.callCount++;
+                callStat.incrementalCallCount++;
                 callStat.recordedCallCount++;
                 callStat.cpuTimeMicros += duration;
                 callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration);
@@ -231,8 +288,12 @@
                         screenInteractive);
                 if (callStat != null) {
                     callStat.callCount++;
+                    callStat.incrementalCallCount++;
                 }
             }
+            if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) {
+                mSendUidsToObserver.add(workSourceUid);
+            }
         }
     }
 
@@ -266,29 +327,6 @@
         }
     }
 
-    @Nullable
-    private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) {
-        try {
-            return binder.getMethod("getDefaultTransactionName", int.class);
-        } catch (NoSuchMethodException e) {
-            // The method might not be present for stubs not generated with AIDL.
-            return null;
-        }
-    }
-
-    @Nullable
-    private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) {
-        if (getDefaultTransactionName == null) {
-            return null;
-        }
-
-        try {
-            return (String) getDefaultTransactionName.invoke(null, transactionCode);
-        } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
     /**
      * This method is expensive to call.
      */
@@ -327,31 +365,23 @@
 
         // Resolve codes outside of the lock since it can be slow.
         ExportedCallStat previous = null;
-        // Cache the previous method/transaction code.
-        Method getDefaultTransactionName = null;
         String previousMethodName = null;
         resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode);
+        BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
         for (ExportedCallStat exported : resultCallStats) {
             final boolean isClassDifferent = previous == null
                     || !previous.className.equals(exported.className);
-            if (isClassDifferent) {
-                getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass);
-            }
-
             final boolean isCodeDifferent = previous == null
                     || previous.transactionCode != exported.transactionCode;
             final String methodName;
             if (isClassDifferent || isCodeDifferent) {
-                String resolvedCode = resolveTransactionCode(
-                        getDefaultTransactionName, exported.transactionCode);
-                methodName = resolvedCode == null
-                        ? String.valueOf(exported.transactionCode)
-                        : resolvedCode;
+                methodName = resolver.getMethodName(exported.binderClass, exported.transactionCode);
             } else {
                 methodName = previousMethodName;
             }
             previousMethodName = methodName;
             exported.methodName = methodName;
+            previous = exported;
         }
 
         // Debug entries added to help validate the data.
@@ -649,14 +679,32 @@
         public long maxRequestSizeBytes;
         public long maxReplySizeBytes;
         public long exceptionCount;
+        // Call count since reset
+        public long incrementalCallCount;
 
-        CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
+        public CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode,
                 boolean screenInteractive) {
             this.callingUid = callingUid;
             this.binderClass = binderClass;
             this.transactionCode = transactionCode;
             this.screenInteractive = screenInteractive;
         }
+
+        @Override
+        public String toString() {
+            // This is expensive, but CallStat.toString() is only used for debugging.
+            String methodName = new BinderTransactionNameResolver().getMethodName(binderClass,
+                    transactionCode);
+            return "CallStat{"
+                    + "callingUid=" + callingUid
+                    + ", transaction=" + binderClass.getSimpleName() + '.' + methodName
+                    + ", callCount=" + callCount
+                    + ", incrementalCallCount=" + incrementalCallCount
+                    + ", recordedCallCount=" + recordedCallCount
+                    + ", cpuTimeMicros=" + cpuTimeMicros
+                    + ", latencyMicros=" + latencyMicros
+                    + '}';
+        }
     }
 
     /** Key used to store CallStat object in a Map. */
@@ -704,13 +752,15 @@
         // Approximate total CPU usage can be computed by
         // cpuTimeMicros * callCount / recordedCallCount
         public long cpuTimeMicros;
+        // Call count that gets reset after delivery to BatteryStats
+        public long incrementalCallCount;
 
         UidEntry(int uid) {
             this.workSourceUid = uid;
         }
 
         // Aggregate time spent per each call name: call_desc -> cpu_time_micros
-        private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
+        private ArrayMap<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
         private CallStatKey mTempKey = new CallStatKey();
 
         @Nullable
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 95c36ca..feb5aab 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -31,6 +31,7 @@
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * Private and debugging Binder APIs.
@@ -134,6 +135,17 @@
     }
 
     /**
+     * Allows to track observe incoming binder call stats.
+     */
+    public interface CallStatsObserver {
+        /**
+         * Notes incoming binder call stats associated with this work source UID.
+         */
+        void noteCallStats(int workSourceUid, long incrementalCallCount,
+                Collection<BinderCallsStats.CallStat> callStats);
+    }
+
+    /**
      * Add the calling thread to the IPC thread pool.  This function does
      * not return until the current process is exiting.
      */
diff --git a/core/java/com/android/internal/os/BinderLatencyBuckets.java b/core/java/com/android/internal/os/BinderLatencyBuckets.java
index bdee4ca..d7d2d6a 100644
--- a/core/java/com/android/internal/os/BinderLatencyBuckets.java
+++ b/core/java/com/android/internal/os/BinderLatencyBuckets.java
@@ -20,8 +20,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.Arrays;
 
 /**
  * Generates the bucket thresholds (with a custom logarithmic scale) for a histogram to store
@@ -29,7 +28,7 @@
  */
 public class BinderLatencyBuckets {
     private static final String TAG = "BinderLatencyBuckets";
-    private ArrayList<Integer> mBuckets;
+    private final int[] mBuckets;
 
     /**
      * @param bucketCount      the number of buckets the histogram should have
@@ -37,12 +36,11 @@
      * @param scaleFactor      the rate in which each consecutive bucket increases (before rounding)
      */
     public BinderLatencyBuckets(int bucketCount, int firstBucketSize, float scaleFactor) {
-        mBuckets = new ArrayList<>(bucketCount - 1);
-        mBuckets.add(firstBucketSize);
+        int[] buffer = new int[bucketCount - 1];
+        buffer[0] = firstBucketSize;
 
         // Last value and the target are disjoint as we never want to create buckets smaller than 1.
         double lastTarget = firstBucketSize;
-        int lastValue = firstBucketSize;
 
         // First bucket is already created and the last bucket is anything greater than the final
         // bucket in the list, so create 'bucketCount' - 2 buckets.
@@ -50,29 +48,29 @@
             // Increase the target bucket limit value by the scale factor.
             double nextTarget = lastTarget * scaleFactor;
 
-            if (nextTarget > Integer.MAX_VALUE || lastValue == Integer.MAX_VALUE) {
+            if (nextTarget > Integer.MAX_VALUE) {
                 // Do not throw an exception here as this should not affect binder calls.
                 Slog.w(TAG, "Attempted to create a bucket larger than maxint");
+                mBuckets = Arrays.copyOfRange(buffer, 0, i);
                 return;
             }
 
-            if ((int) nextTarget > lastValue) {
+            if ((int) nextTarget > buffer[i - 1]) {
                 // Convert the target bucket limit value to an integer.
-                mBuckets.add((int) nextTarget);
-                lastValue = (int) nextTarget;
+                buffer[i] = (int) nextTarget;
             } else {
                 // Avoid creating redundant buckets, so bucket size should be 1 at a minimum.
-                mBuckets.add(lastValue + 1);
-                lastValue = lastValue + 1;
+                buffer[i] = buffer[i - 1] + 1;
             }
             lastTarget = nextTarget;
         }
+        mBuckets = buffer;
     }
 
     /** Gets the bucket index to insert the provided sample in. */
     public int sampleToBucket(int sample) {
-        if (sample > mBuckets.get(mBuckets.size() - 1)) {
-            return mBuckets.size();
+        if (sample >= mBuckets[mBuckets.length - 1]) {
+            return mBuckets.length;
         }
 
         // Binary search returns the element index if it is contained in the list - in this case the
@@ -80,12 +78,12 @@
         // Otherwise, it returns (-(insertion point) - 1), where insertion point is the point where
         // to insert the element so that the array remains sorted - in this case the bucket index
         // is the insertion point.
-        int searchResult = Collections.binarySearch(mBuckets, sample);
+        int searchResult = Arrays.binarySearch(mBuckets, sample);
         return searchResult < 0 ? -(1 + searchResult) : searchResult + 1;
     }
 
     @VisibleForTesting
-    public ArrayList<Integer> getBuckets() {
+    public int[] getBuckets() {
         return mBuckets;
     }
 }
diff --git a/core/java/com/android/internal/os/BinderTransactionNameResolver.java b/core/java/com/android/internal/os/BinderTransactionNameResolver.java
new file mode 100644
index 0000000..5f6f427
--- /dev/null
+++ b/core/java/com/android/internal/os/BinderTransactionNameResolver.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Binder;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+
+/**
+ * Maps a binder class and transaction code to the default transaction name.  Since this
+ * resolution is class-based as opposed to instance-based, any custom implementation of
+ * {@link Binder#getTransactionName} will be ignored.
+ *
+ * The class is NOT thread safe
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class BinderTransactionNameResolver {
+    private static final Method NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
+
+    /**
+     * Generates the default transaction method name, which is just the transaction code.
+     * Used when the binder does not define a static "getDefaultTransactionName" method.
+     *
+     * @hide
+     */
+    public static String noDefaultTransactionName(int transactionCode) {
+        return String.valueOf(transactionCode);
+    }
+
+    static {
+        try {
+            NO_GET_DEFAULT_TRANSACTION_NAME_METHOD = BinderTransactionNameResolver.class.getMethod(
+                    "noDefaultTransactionName", int.class);
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final HashMap<Class<? extends Binder>, Method>
+            mGetDefaultTransactionNameMethods = new HashMap<>();
+
+    /**
+     * Given a binder class name and transaction code, returns the corresponding method name.
+     *
+     * @hide
+     */
+    public String getMethodName(Class<? extends Binder> binderClass, int transactionCode) {
+        Method method = mGetDefaultTransactionNameMethods.get(binderClass);
+        if (method == null) {
+            try {
+                method = binderClass.getMethod("getDefaultTransactionName", int.class);
+            } catch (NoSuchMethodException e) {
+                method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
+            }
+            if (method.getReturnType() != String.class
+                    || !Modifier.isStatic(method.getModifiers())) {
+                method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD;
+            }
+            mGetDefaultTransactionNameMethods.put(binderClass, method);
+        }
+
+        try {
+            return (String) method.invoke(null, transactionCode);
+        } catch (IllegalAccessException | InvocationTargetException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 6860759e..508782b 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -21,8 +21,8 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
-import android.system.StructCapUserData;
-import android.system.StructCapUserHeader;
+import android.system.StructUserCapData;
+import android.system.StructUserCapHeader;
 import android.util.Slog;
 import android.util.TimingsTraceLog;
 
@@ -187,9 +187,9 @@
      *       capabilities, which may make it crash, but not exceed its allowances.
      */
     private static void preserveCapabilities() {
-        StructCapUserHeader header = new StructCapUserHeader(
+        StructUserCapHeader header = new StructUserCapHeader(
                 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
-        StructCapUserData[] data;
+        StructUserCapData[] data;
         try {
             data = Os.capget(header);
         } catch (ErrnoException e) {
@@ -199,9 +199,9 @@
 
         if (data[0].permitted != data[0].inheritable ||
                 data[1].permitted != data[1].inheritable) {
-            data[0] = new StructCapUserData(data[0].effective, data[0].permitted,
+            data[0] = new StructUserCapData(data[0].effective, data[0].permitted,
                     data[0].permitted);
-            data[1] = new StructCapUserData(data[1].effective, data[1].permitted,
+            data[1] = new StructUserCapData(data[1].effective, data[1].permitted,
                     data[1].permitted);
             try {
                 Os.capset(header, data);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a541089..cb6008b 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -45,8 +45,8 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.OsConstants;
-import android.system.StructCapUserData;
-import android.system.StructCapUserHeader;
+import android.system.StructUserCapData;
+import android.system.StructUserCapHeader;
 import android.text.Hyphenator;
 import android.util.EventLog;
 import android.util.Log;
@@ -586,10 +586,18 @@
                 codePaths[0],
                 /*dexMetadata*/ null);
 
-        File profileDir = Environment.getDataProfilesDePackageDirectory(
+        File curProfileDir = Environment.getDataProfilesDePackageDirectory(
                 UserHandle.USER_SYSTEM, systemServerPackageName);
-        String profilePath = new File(profileDir, systemServerProfileName).getAbsolutePath();
-        VMRuntime.registerAppInfo(profilePath, codePaths);
+        String curProfilePath = new File(curProfileDir, systemServerProfileName).getAbsolutePath();
+        File refProfileDir = Environment.getDataProfilesDePackageDirectory(
+                UserHandle.USER_SYSTEM, systemServerPackageName);
+        String refProfilePath = new File(refProfileDir, systemServerProfileName).getAbsolutePath();
+        VMRuntime.registerAppInfo(
+                systemServerPackageName,
+                curProfilePath,
+                refProfilePath,
+                codePaths,
+                VMRuntime.CODE_PATH_TYPE_PRIMARY_APK);
     }
 
     /**
@@ -742,9 +750,9 @@
                 OsConstants.CAP_BLOCK_SUSPEND
         );
         /* Containers run without some capabilities, so drop any caps that are not available. */
-        StructCapUserHeader header = new StructCapUserHeader(
+        StructUserCapHeader header = new StructUserCapHeader(
                 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
-        StructCapUserData[] data;
+        StructUserCapData[] data;
         try {
             data = Os.capget(header);
         } catch (ErrnoException ex) {
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
index a045451..5b68159 100644
--- a/core/java/com/android/internal/util/OWNERS
+++ b/core/java/com/android/internal/util/OWNERS
@@ -1,5 +1,6 @@
 per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com
 per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com
+per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS
 per-file Protocol* = etancohen@google.com, lorenzo@google.com
 per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com
 per-file DataClass* = eugenesusla@google.com
\ No newline at end of file
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
index 12629254..554e278 100644
--- a/core/java/com/android/server/OWNERS
+++ b/core/java/com/android/server/OWNERS
@@ -1 +1 @@
-per-file SystemConfig.java = toddke@google.com
+per-file SystemConfig.java = toddke@google.com,patb@google.com
diff --git a/core/proto/OWNERS b/core/proto/OWNERS
index e62b5c1..44ea23f 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,9 +14,10 @@
 # Frameworks
 ogunwale@google.com
 jjaggi@google.com
+kwekua@google.com
 roosa@google.com
-per-file package_item_info.proto = toddke@google.com
-per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
+per-file package_item_info.proto = toddke@google.com,patb@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS
 per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS
 
 # Biometrics
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
new file mode 100644
index 0000000..3e67b8b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Binder;
+import android.os.Process;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Test cases for android.os.BatteryStats, system server Binder call stats.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class BatteryStatsBinderCallStatsTest extends TestCase {
+
+    private static final int TRANSACTION_CODE = 100;
+
+    /**
+     * Test BatteryStatsImpl.Uid.noteBinderCallStats.
+     */
+    @Test
+    public void testNoteBinderCallStats() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        int callingUid = Process.FIRST_APPLICATION_UID + 1;
+        int workSourceUid = Process.FIRST_APPLICATION_UID + 1;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+        stat1.incrementalCallCount = 21;
+        stat1.recordedCallCount = 5;
+        stat1.cpuTimeMicros = 1000;
+        callStats.add(stat1);
+
+        bi.noteBinderCallStats(workSourceUid, 42, callStats);
+
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+        stat2.incrementalCallCount = 9;
+        stat2.recordedCallCount = 8;
+        stat2.cpuTimeMicros = 500;
+        callStats.add(stat2);
+
+        bi.noteBinderCallStats(workSourceUid, 8, callStats);
+
+        BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
+        assertEquals(42 + 8, uid.getBinderCallCount());
+
+        BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
+        ArraySet<BatteryStatsImpl.BinderCallStats> stats = uid.getBinderCallStats();
+        assertEquals(1, stats.size());
+        BatteryStatsImpl.BinderCallStats value = stats.valueAt(0);
+        value.ensureMethodName(resolver);
+        assertEquals("testMethod", value.getMethodName());
+        assertEquals(21 + 9, value.callCount);
+        assertEquals(8, value.recordedCallCount);
+        assertEquals(500, value.recordedCpuTimeMicros);
+    }
+
+    private static class MockBinder extends Binder {
+        public static String getDefaultTransactionName(int txCode) {
+            return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 2ad8e18..7807f01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -23,6 +23,7 @@
 @Suite.SuiteClasses({
         BatteryStatsCpuTimesTest.class,
         BatteryStatsBackgroundStatsTest.class,
+        BatteryStatsBinderCallStatsTest.class,
         BatteryStatsCounterTest.class,
         BatteryStatsDualTimerTest.class,
         BatteryStatsDurationTimerTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index cec6216..68c6870 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -16,10 +16,16 @@
 
 package com.android.internal.os;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
@@ -46,11 +52,12 @@
 @RunWith(AndroidJUnit4.class)
 @Presubmit
 public class BinderCallsStatsTest {
-    private static final int WORKSOURCE_UID = 1;
+    private static final int WORKSOURCE_UID = Process.FIRST_APPLICATION_UID;
     private static final int CALLING_UID = 2;
     private static final int REQUEST_SIZE = 2;
     private static final int REPLY_SIZE = 3;
     private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true);
+    private final TestHandler mHandler = new TestHandler();
 
     @Test
     public void testDetailedOff() {
@@ -754,6 +761,53 @@
     }
 
     @Test
+    public void testCallStatsObserver() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setSamplingInterval(1);
+        bcs.setTrackScreenInteractive(false);
+
+        final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
+        bcs.setCallStatsObserver(
+                (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
+                        callStats));
+
+        Binder binder = new Binder();
+
+        CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+        callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID);
+        bcs.time += 20;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+        callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID);
+        bcs.time += 30;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID);
+
+        for (Runnable runnable: mHandler.mRunnables) {
+            // Execute all pending runnables. Ignore the delay.
+            runnable.run();
+        }
+
+        assertThat(callStatsList).hasSize(2);
+        for (int i = 0; i < 2; i++) {
+            BinderCallsStats.CallStat callStats = callStatsList.get(i);
+            if (callStats.transactionCode == 1) {
+                assertEquals(2, callStats.callCount);
+                assertEquals(2, callStats.recordedCallCount);
+                assertEquals(30, callStats.cpuTimeMicros);
+                assertEquals(20, callStats.maxCpuTimeMicros);
+            } else {
+                assertEquals(1, callStats.callCount);
+                assertEquals(1, callStats.recordedCallCount);
+                assertEquals(30, callStats.cpuTimeMicros);
+                assertEquals(30, callStats.maxCpuTimeMicros);
+            }
+        }
+    }
+
+    @Test
     public void testLatencyCollectionEnabled() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setCollectLatencyData(true);
@@ -781,6 +835,20 @@
         assertEquals(0, bcs.getLatencyObserver().getLatencyHistograms().size());
     }
 
+    private static class TestHandler extends Handler {
+        ArrayList<Runnable> mRunnables = new ArrayList<>();
+
+        TestHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
+            mRunnables.add(msg.getCallback());
+            return true;
+        }
+    }
+
     class TestBinderCallsStats extends BinderCallsStats {
         public int callingUid = CALLING_UID;
         public long time = 1234;
@@ -803,6 +871,10 @@
                     };
                 }
 
+                public Handler getHandler() {
+                    return mHandler;
+                }
+
                 public BinderLatencyObserver getLatencyObserver() {
                     return new BinderLatencyObserverTest.TestBinderLatencyObserver();
                 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
index 00443a9..b2054f1 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyBucketsTest.java
@@ -36,6 +36,7 @@
     public void testBucketThresholds() {
         BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(10, 2, 1.45f);
         assertThat(latencyBuckets.getBuckets())
+            .asList()
             .containsExactly(2, 3, 4, 6, 8, 12, 18, 26, 39)
             .inOrder();
     }
@@ -58,6 +59,7 @@
     public void testMaxIntBuckets() {
         BinderLatencyBuckets latencyBuckets = new BinderLatencyBuckets(5, Integer.MAX_VALUE / 2, 2);
         assertThat(latencyBuckets.getBuckets())
+            .asList()
             .containsExactly(Integer.MAX_VALUE / 2, Integer.MAX_VALUE - 1)
             .inOrder();
 
diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
index 3f9e62e..95272132 100644
--- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java
@@ -29,6 +29,8 @@
 
 import com.android.internal.util.FileRotator.Reader;
 import com.android.internal.util.FileRotator.Writer;
+import com.android.internal.util.test.FsUtil;
+
 import com.google.android.collect.Lists;
 
 import java.io.DataInputStream;
@@ -38,15 +40,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Random;
 
-import junit.framework.Assert;
-
-import libcore.io.IoUtils;
-
 /**
  * Tests for {@link FileRotator}.
  */
@@ -67,7 +64,7 @@
         super.setUp();
 
         mBasePath = getContext().getFilesDir();
-        IoUtils.deleteContents(mBasePath);
+        FsUtil.deleteContents(mBasePath);
     }
 
     public void testEmpty() throws Exception {
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 5aacfdd..ea23aba 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -10,6 +10,7 @@
 svetoslavganov@google.com
 toddke@android.com
 toddke@google.com
+patb@google.com
 yamasani@google.com
 
 per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 62e11b9..ee2387b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -444,6 +444,7 @@
         <permission name="android.permission.MANAGE_DEBUGGING" />
         <!-- Permissions required for CTS test - TimeManagerTest -->
         <permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+        <permission name="android.permission.SUGGEST_EXTERNAL_TIME" />
         <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases -->
         <permission name="android.permission.HDMI_CEC"/>
         <!-- Permission required for CTS test - MediaPlayerTest -->
diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl
new file mode 100644
index 0000000..05a25f0
--- /dev/null
+++ b/data/keyboards/Vendor_0171_Product_0419.kl
@@ -0,0 +1,55 @@
+# 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.
+
+#
+# Amazon Luna Controller
+#
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+# Left and right stick.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x02 Z flat 4096
+axis 0x05 RZ flat 4096
+
+# Triggers.
+axis 0x0a LTRIGGER
+axis 0x09 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Action button (circle icon, left of the Home button)
+key 158   BUTTON_SELECT
+
+# Home button (branded button in the center of the controller)
+key 172   BUTTON_MODE
+
+# Menu button (hamburger icon, right of the Home button)
+key 315   BUTTON_START
+
+# Alexa Push-To-Talk button (microphone icon, below the Home button)
+key 217   MEDIA_RECORD
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index bc056df..610fd80 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -1,6 +1,7 @@
 set noparent
 toddke@google.com
 rtmitchell@google.com
+patb@google.com
 
 per-file CursorWindow.cpp=omakoto@google.com
 per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
index 3598ebc..dcc8a5e 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java
@@ -713,7 +713,9 @@
      * <p>Callbacks registered by apps not meeting the above criteria will not be invoked.
      *
      * <p>If a registering app loses its relevant permissions, any callbacks it registered will
-     * silently stop receiving callbacks.
+     * silently stop receiving callbacks. Note that registering apps must also have location
+     * permissions to receive callbacks as some Networks may be location-bound (such as WiFi
+     * networks).
      *
      * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is
      * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index dd88c5a..e6a96ef 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -200,8 +200,9 @@
 
         private final NetworkCapabilities mNetworkCapabilities;
 
-        // A boolean that represents the user modified NOT_VCN_MANAGED capability.
-        private boolean mModifiedNotVcnManaged = false;
+        // A boolean that represents whether the NOT_VCN_MANAGED capability should be deduced when
+        // the NetworkRequest object is built.
+        private boolean mShouldDeduceNotVcnManaged = true;
 
         /**
          * Default constructor for Builder.
@@ -223,7 +224,7 @@
             // If the caller constructed the builder from a request, it means the user
             // might explicitly want the capabilities from the request. Thus, the NOT_VCN_MANAGED
             // capabilities should not be touched later.
-            mModifiedNotVcnManaged = true;
+            mShouldDeduceNotVcnManaged = false;
         }
 
         /**
@@ -254,7 +255,7 @@
         public Builder addCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.addCapability(capability);
             if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
-                mModifiedNotVcnManaged = true;
+                mShouldDeduceNotVcnManaged = false;
             }
             return this;
         }
@@ -268,7 +269,7 @@
         public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) {
             mNetworkCapabilities.removeCapability(capability);
             if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) {
-                mModifiedNotVcnManaged = true;
+                mShouldDeduceNotVcnManaged = false;
             }
             return this;
         }
@@ -352,7 +353,7 @@
             mNetworkCapabilities.clearAll();
             // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
             // should not be add back later.
-            mModifiedNotVcnManaged = true;
+            mShouldDeduceNotVcnManaged = false;
             return this;
         }
 
@@ -453,6 +454,9 @@
                 throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
             }
             mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+            // Do not touch NOT_VCN_MANAGED if the caller needs to access to a very specific
+            // Network.
+            mShouldDeduceNotVcnManaged = false;
             return this;
         }
 
@@ -486,12 +490,13 @@
          *      {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to
          *      allow the callers automatically utilize VCN networks if available.
          *   2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED,
+         *      or has clear intention of tracking specific network,
          *      do not alter them to allow user fire request that suits their need.
          *
          * @hide
          */
         private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
-            if (mModifiedNotVcnManaged) return;
+            if (!mShouldDeduceNotVcnManaged) return;
             for (final int cap : nc.getCapabilities()) {
                 if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
             }
diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
index 85b24713..663c1b3 100644
--- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java
@@ -131,43 +131,21 @@
      * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
      */
     public static boolean getApfDrop8023Frames() {
-        // TODO(b/183076074): remove reading resources from system resources
+        // TODO: deprecate/remove this method (now unused in the platform), as the resource was
+        // moved to NetworkStack.
         final Resources systemRes = Resources.getSystem();
         final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android");
         return systemRes.getBoolean(id);
     }
 
     /**
-     * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
-     * @hide
-     */
-    public static boolean getApfDrop8023Frames(@NonNull Context context) {
-        final ConnectivityResources res = getResources(context);
-        // TODO(b/183076074): use R.bool.config_apfDrop802_3Frames directly
-        final int id = res.get().getIdentifier("config_apfDrop802_3Frames", "bool",
-                res.getResourcesContext().getPackageName());
-        return res.get().getBoolean(id);
-    }
-
-    /**
      * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
      */
     public static @NonNull int[] getApfEtherTypeBlackList() {
-        // TODO(b/183076074): remove reading resources from system resources
+        // TODO: deprecate/remove this method (now unused in the platform), as the resource was
+        // moved to NetworkStack.
         final Resources systemRes = Resources.getSystem();
         final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android");
         return systemRes.getIntArray(id);
     }
-
-    /**
-     * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped.
-     * @hide
-     */
-    public static @NonNull int[] getApfEtherTypeDenyList(@NonNull Context context) {
-        final ConnectivityResources res = getResources(context);
-        // TODO(b/183076074): use R.array.config_apfEthTypeDenyList directly
-        final int id = res.get().getIdentifier("config_apfEthTypeDenyList", "array",
-                res.getResourcesContext().getPackageName());
-        return res.get().getIntArray(id);
-    }
 }
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
index 9ff2a22..078a9eb 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml
@@ -52,22 +52,6 @@
         <item>12,60000</item><!-- mobile_cbs -->
     </string-array>
 
-    <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames
-         Those frames are identified by the field Eth-type having values
-         less than 0x600 -->
-    <bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
-
-    <!-- An array of Denylisted EtherType, packets with EtherTypes within this array
-         will be dropped
-         TODO: need to put proper values, these are for testing purposes only -->
-    <integer-array translatable="false" name="config_apfEthTypeDenyList">
-        <item>0x88A2</item>
-        <item>0x88A4</item>
-        <item>0x88B8</item>
-        <item>0x88CD</item>
-        <item>0x88E3</item>
-    </integer-array>
-
     <!-- Default supported concurrent socket keepalive slots per transport type, used by
          ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive
          offload slots that should be reserved for privileged access. This string array should be
diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
index 717d08e..f0f4ae8 100644
--- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -21,8 +21,6 @@
             <item type="string" name="config_networkCaptivePortalServerUrl"/>
             <item type="integer" name="config_networkTransitionTimeout"/>
             <item type="array" name="config_wakeonlan_supported_interfaces"/>
-            <item type="bool" name="config_apfDrop802_3Frames"/>
-            <item type="array" name="config_apfEthTypeDenyList"/>
             <item type="integer" name="config_networkMeteredMultipathPreference"/>
             <item type="array" name="config_networkSupportedKeepaliveCount"/>
             <item type="integer" name="config_networkAvoidBadWifi"/>
diff --git a/packages/Connectivity/service/src/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
index b2e2943..842ad62 100644
--- a/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
+++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java
@@ -9175,6 +9175,34 @@
         return results;
     }
 
+    private boolean hasLocationPermission(String packageName, int uid) {
+        // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
+        // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
+        // call in a try-catch.
+        try {
+            if (!mLocationPermissionChecker.checkLocationPermission(
+                        packageName, null /* featureId */, uid, null /* message */)) {
+                return false;
+            }
+        } catch (SecurityException e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean ownsVpnRunningOverNetwork(int uid, Network network) {
+        for (NetworkAgentInfo virtual : mNetworkAgentInfos) {
+            if (virtual.supportsUnderlyingNetworks()
+                    && virtual.networkCapabilities.getOwnerUid() == uid
+                    && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, network)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     @VisibleForTesting
     boolean checkConnectivityDiagnosticsPermissions(
             int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) {
@@ -9182,29 +9210,14 @@
             return true;
         }
 
-        // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid
-        // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the
-        // call in a try-catch.
-        try {
-            if (!mLocationPermissionChecker.checkLocationPermission(
-                    callbackPackageName, null /* featureId */, callbackUid, null /* message */)) {
-                return false;
-            }
-        } catch (SecurityException e) {
+        // Administrator UIDs also contains the Owner UID
+        final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
+        if (!CollectionUtils.contains(administratorUids, callbackUid)
+                && !ownsVpnRunningOverNetwork(callbackUid, nai.network)) {
             return false;
         }
 
-        for (NetworkAgentInfo virtual : mNetworkAgentInfos) {
-            if (virtual.supportsUnderlyingNetworks()
-                    && virtual.networkCapabilities.getOwnerUid() == callbackUid
-                    && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) {
-                return true;
-            }
-        }
-
-        // Administrator UIDs also contains the Owner UID
-        final int[] administratorUids = nai.networkCapabilities.getAdministratorUids();
-        return CollectionUtils.contains(administratorUids, callbackUid);
+        return hasLocationPermission(callbackPackageName, callbackUid);
     }
 
     @Override
diff --git a/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
index d50406f..88996d9 100644
--- a/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
+++ b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.testutils.ParcelUtils.assertParcelSane;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
@@ -25,12 +26,17 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.os.Build;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -39,6 +45,9 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ApfCapabilitiesTest {
+    @Rule
+    public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
     private Context mContext;
 
     @Before
@@ -85,6 +94,17 @@
         assertEquals(shouldDrop8023Frames, actual);
     }
 
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testGetApfDrop8023Frames_S() {
+        // IpClient does not call getApfDrop8023Frames() since S, so any customization of the return
+        // value on S+ is a configuration error as it will not be used by IpClient.
+        assertTrue("android.R.bool.config_apfDrop802_3Frames has been modified to false, but "
+                + "starting from S its value is not used by IpClient. If the modification is "
+                + "intentional, use a runtime resource overlay for the NetworkStack package to "
+                + "overlay com.android.networkstack.R.bool.config_apfDrop802_3Frames instead.",
+                ApfCapabilities.getApfDrop8023Frames());
+    }
+
     @Test
     public void testGetApfEtherTypeBlackList() {
         // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly
@@ -96,4 +116,17 @@
         assertNotNull(actual);
         assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual));
     }
+
+    @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+    public void testGetApfEtherTypeBlackList_S() {
+        // IpClient does not call getApfEtherTypeBlackList() since S, so any customization of the
+        // return value on S+ is a configuration error as it will not be used by IpClient.
+        assertArrayEquals("android.R.array.config_apfEthTypeBlackList has been modified, but "
+                        + "starting from S its value is not used by IpClient. If the modification "
+                        + "is intentional, use a runtime resource overlay for the NetworkStack "
+                        + "package to overlay "
+                        + "com.android.networkstack.R.array.config_apfEthTypeDenyList instead.",
+                new int[] { 0x88a2, 0x88a4, 0x88b8, 0x88cd, 0x88e3 },
+                ApfCapabilities.getApfEtherTypeBlackList());
+    }
 }
diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
index ab6b2f4..cb39a0c 100644
--- a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -40,7 +40,7 @@
 import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT
 import android.net.NetworkTemplate.buildTemplateWifi
 import android.net.NetworkTemplate.buildTemplateWifiWildcard
-import android.net.NetworkTemplate.buildTemplateCarrier
+import android.net.NetworkTemplate.buildTemplateCarrierMetered
 import android.net.NetworkTemplate.buildTemplateMobileWithRatType
 import android.telephony.TelephonyManager
 import com.android.testutils.assertParcelSane
@@ -73,11 +73,12 @@
         type: Int,
         subscriberId: String? = null,
         ssid: String? = null,
-        oemManaged: Int = OEM_NONE
+        oemManaged: Int = OEM_NONE,
+        metered: Boolean = true
     ): NetworkStateSnapshot {
         val lp = LinkProperties()
         val caps = NetworkCapabilities().apply {
-            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
+            setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !metered)
             setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
             setSSID(ssid)
             setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
@@ -167,25 +168,38 @@
     }
 
     @Test
-    fun testCarrierMatches() {
-        val templateCarrierImsi1 = buildTemplateCarrier(TEST_IMSI1)
+    fun testCarrierMeteredMatches() {
+        val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1)
 
-        val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1),
-                false, TelephonyManager.NETWORK_TYPE_UMTS)
-        val identMobile2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
-                false, TelephonyManager.NETWORK_TYPE_UMTS)
-        val identWifiSsid1 = buildNetworkIdentity(
-                mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0)
-        val identCarrierWifiImsi1 = buildNetworkIdentity(
-                mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
-        val identCarrierWifiImsi2 = buildNetworkIdentity(
-                mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0)
+        val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
+        val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */,
+                OEM_NONE, false /* metered */)
+        val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
+        val wifiSsid1 = buildWifiNetworkState(null /* subscriberId */, TEST_SSID1)
+        val wifiImsi1Ssid1 = buildWifiNetworkState(TEST_IMSI1, TEST_SSID1)
+        val wifiImsi1Ssid1Unmetered = buildNetworkState(TYPE_WIFI, TEST_IMSI1, TEST_SSID1,
+                OEM_NONE, false /* metered */)
 
-        templateCarrierImsi1.assertMatches(identCarrierWifiImsi1)
-        templateCarrierImsi1.assertDoesNotMatch(identCarrierWifiImsi2)
-        templateCarrierImsi1.assertDoesNotMatch(identWifiSsid1)
-        templateCarrierImsi1.assertMatches(identMobile1)
-        templateCarrierImsi1.assertDoesNotMatch(identMobile2)
+        val identMobileImsi1Metered = buildNetworkIdentity(mockContext,
+                mobileImsi1, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identMobileImsi1Unmetered = buildNetworkIdentity(mockContext,
+                mobileImsi1Unmetered, false /* defaultNetwork */,
+                TelephonyManager.NETWORK_TYPE_UMTS)
+        val identMobileImsi2Metered = buildNetworkIdentity(mockContext,
+                mobileImsi2, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+        val identWifiSsid1Metered = buildNetworkIdentity(
+                mockContext, wifiSsid1, true /* defaultNetwork */, 0 /* subType */)
+        val identCarrierWifiImsi1Metered = buildNetworkIdentity(
+                mockContext, wifiImsi1Ssid1, true /* defaultNetwork */, 0 /* subType */)
+        val identCarrierWifiImsi1NonMetered = buildNetworkIdentity(mockContext,
+                wifiImsi1Ssid1Unmetered, true /* defaultNetwork */, 0 /* subType */)
+
+        templateCarrierImsi1Metered.assertMatches(identMobileImsi1Metered)
+        templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi1Unmetered)
+        templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi2Metered)
+        templateCarrierImsi1Metered.assertDoesNotMatch(identWifiSsid1Metered)
+        templateCarrierImsi1Metered.assertMatches(identCarrierWifiImsi1Metered)
+        templateCarrierImsi1Metered.assertDoesNotMatch(identCarrierWifiImsi1NonMetered)
     }
 
     @Test
diff --git a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 41458f1..1b4f836 100644
--- a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -9743,28 +9743,32 @@
 
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception {
-        final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
+        final int wrongUid = Process.myUid() + 1;
+
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.setAdministratorUids(new int[] {wrongUid});
+        final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
 
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         assertFalse(
                 "Mismatched uid/package name should not pass the location permission check",
                 mService.checkConnectivityDiagnosticsPermissions(
-                        Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid,
-                        mContext.getOpPackageName()));
+                        Process.myPid() + 1, wrongUid, naiWithUid, mContext.getOpPackageName()));
     }
 
     @Test
     public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception {
-        final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities());
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.setAdministratorUids(new int[] {Process.myUid()});
+        final NetworkAgentInfo naiWithUid = fakeMobileNai(nc);
 
         mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
 
         assertFalse(
                 "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics",
                 mService.checkConnectivityDiagnosticsPermissions(
-                        Process.myPid(), Process.myUid(), naiWithoutUid,
-                        mContext.getOpPackageName()));
+                        Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName()));
     }
 
     @Test
diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index f3ae9b0..93599f3 100644
--- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -43,6 +43,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.tests.net.R;
+import com.android.internal.util.test.FsUtil;
 
 import libcore.io.IoUtils;
 import libcore.io.Streams;
@@ -71,7 +72,7 @@
     public void setUp() throws Exception {
         mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc");
         if (mTestProc.exists()) {
-            IoUtils.deleteContents(mTestProc);
+            FsUtil.deleteContents(mTestProc);
         }
 
         // The libandroid_servers which have the native method is not available to
@@ -87,7 +88,7 @@
         mFactory = null;
 
         if (mTestProc.exists()) {
-            IoUtils.deleteContents(mTestProc);
+            FsUtil.deleteContents(mTestProc);
         }
     }
 
diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index fe2985c..0ba5f7d 100644
--- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -112,13 +112,12 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FsUtil;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
 import com.android.testutils.HandlerUtils;
 import com.android.testutils.TestableNetworkStatsProviderBinder;
 
-import libcore.io.IoUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -152,6 +151,8 @@
     private static final String TEST_SSID = "AndroidAP";
 
     private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+    private static NetworkTemplate sTemplateCarrierWifi1 =
+            buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, IMSI_1);
     private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
     private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
 
@@ -213,7 +214,7 @@
         mServiceContext = new MockContext(context);
         mStatsDir = context.getFilesDir();
         if (mStatsDir.exists()) {
-            IoUtils.deleteContents(mStatsDir);
+            FsUtil.deleteContents(mStatsDir);
         }
 
         PowerManager powerManager = (PowerManager) mServiceContext.getSystemService(
@@ -283,7 +284,7 @@
 
     @After
     public void tearDown() throws Exception {
-        IoUtils.deleteContents(mStatsDir);
+        FsUtil.deleteContents(mStatsDir);
 
         mServiceContext = null;
         mStatsDir = null;
@@ -297,45 +298,82 @@
         mHandlerThread.quitSafely();
     }
 
-    @Test
-    public void testNetworkStatsWifi() throws Exception {
+    private void initWifiStats(NetworkStateSnapshot snapshot) throws Exception {
         // pretend that wifi network comes online; service should ask about full
         // network state, and poll any existing interfaces before updating.
         expectDefaultSettings();
-        NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()};
+        NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {snapshot};
         expectNetworkStatsSummary(buildEmptyStats());
         expectNetworkStatsUidDetail(buildEmptyStats());
 
         mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
                 new UnderlyingNetworkInfo[0]);
+    }
 
-        // verify service has empty history for wifi
+    private void incrementWifiStats(long durationMillis, String iface,
+            long rxb, long rxp, long txb, long txp) throws Exception {
+        incrementCurrentTime(durationMillis);
+        expectDefaultSettings();
+        expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+                .insertEntry(iface, rxb, rxp, txb, txp));
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        forcePollAndWaitForIdle();
+    }
+
+    @Test
+    public void testNetworkStatsCarrierWifi() throws Exception {
+        initWifiStats(buildWifiState(true, TEST_IFACE, IMSI_1));
+        // verify service has empty history for carrier merged wifi and non-carrier wifi
+        assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
         assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
 
         // modify some number on wifi, and trigger poll event
-        incrementCurrentTime(HOUR_IN_MILLIS);
-        expectDefaultSettings();
-        expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L));
-        expectNetworkStatsUidDetail(buildEmptyStats());
-        forcePollAndWaitForIdle();
+        incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L);
 
         // verify service recorded history
-        assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
+        assertNetworkTotal(sTemplateCarrierWifi1, 1024L, 1L, 2048L, 2L, 0);
+
+        // verify service recorded history for wifi with SSID filter
+        assertNetworkTotal(sTemplateWifi,  1024L, 1L, 2048L, 2L, 0);
 
 
         // and bump forward again, with counters going higher. this is
         // important, since polling should correctly subtract last snapshot.
-        incrementCurrentTime(DAY_IN_MILLIS);
-        expectDefaultSettings();
-        expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
-                .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L));
-        expectNetworkStatsUidDetail(buildEmptyStats());
-        forcePollAndWaitForIdle();
+        incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L);
+
+        // verify service recorded history
+        assertNetworkTotal(sTemplateCarrierWifi1, 4096L, 4L, 8192L, 8L, 0);
+        // verify service recorded history for wifi with SSID filter
+        assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
+    }
+
+    @Test
+    public void testNetworkStatsNonCarrierWifi() throws Exception {
+        initWifiStats(buildWifiState());
+
+        // verify service has empty history for wifi
+        assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
+        // verify service has empty history for carrier merged wifi
+        assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
+
+        // modify some number on wifi, and trigger poll event
+        incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L);
+
+        // verify service recorded history
+        assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
+        // verify service has empty history for carrier wifi since current network is non carrier
+        // wifi
+        assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
+
+        // and bump forward again, with counters going higher. this is
+        // important, since polling should correctly subtract last snapshot.
+        incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L);
 
         // verify service recorded history
         assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0);
-
+        // verify service has empty history for carrier wifi since current network is non carrier
+        // wifi
+        assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0);
     }
 
     @Test
@@ -1662,10 +1700,15 @@
     }
 
     private static NetworkStateSnapshot buildWifiState() {
-        return buildWifiState(false, TEST_IFACE);
+        return buildWifiState(false, TEST_IFACE, null);
     }
 
     private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) {
+        return buildWifiState(isMetered, iface, null);
+    }
+
+    private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface,
+            String subscriberId) {
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
         final NetworkCapabilities capabilities = new NetworkCapabilities();
@@ -1673,7 +1716,7 @@
         capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
         capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
         capabilities.setSSID(TEST_SSID);
-        return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI);
+        return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, subscriberId, TYPE_WIFI);
     }
 
     private static NetworkStateSnapshot buildMobile3gState(String subscriberId) {
diff --git a/packages/CtsShim/OWNERS b/packages/CtsShim/OWNERS
index ba9f2b9..9419771 100644
--- a/packages/CtsShim/OWNERS
+++ b/packages/CtsShim/OWNERS
@@ -1,2 +1,3 @@
 ioffe@google.com
-toddke@google.com
\ No newline at end of file
+toddke@google.com
+patb@google.com
\ No newline at end of file
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
index 8e1774b..c633113 100644
--- a/packages/PackageInstaller/OWNERS
+++ b/packages/PackageInstaller/OWNERS
@@ -1,5 +1,6 @@
 svetoslavganov@google.com
 toddke@google.com
+patb@google.com
 suprabh@google.com
 
 # For automotive related changes
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index cf9799c..6c61d4b 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -4,3 +4,4 @@
 svetoslavganov@google.com
 schfan@google.com
 toddke@google.com
+patb@google.com
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2a12ce2..133a839 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -427,8 +427,9 @@
     <!-- Permission needed for CTS test - DisplayTest -->
     <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" />
 
-    <!-- Permission needed for CTS test - TimeManagerTest -->
+    <!-- Permissions needed for CTS test - TimeManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
+    <uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
 
     <!-- Permission needed for CTS test - CtsHdmiCecHostTestCases -->
     <uses-permission android:name="android.permission.HDMI_CEC" />
diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS
index 6d738f8..177f86b 100644
--- a/packages/Shell/OWNERS
+++ b/packages/Shell/OWNERS
@@ -7,6 +7,7 @@
 hackbod@google.com
 yamasani@google.com
 toddke@google.com
+patb@google.com
 cbrubaker@google.com
 omakoto@google.com
 michaelwr@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 5a52597..9e60356 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -114,7 +114,7 @@
     private val onSeedingComplete = Consumer<Boolean> {
         accepted ->
             if (accepted) {
-                selectedStructure = controlsController.get().getFavorites().maxBy {
+                selectedStructure = controlsController.get().getFavorites().maxByOrNull {
                     it.controls.size
                 } ?: EMPTY_STRUCTURE
                 updatePreferences(selectedStructure)
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
index 1d2e747..eec69f98 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
@@ -28,7 +28,7 @@
         appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType })
                 .toList()
                 .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps
-                        { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest)
+                        { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest)
         types = itemsList.map { it.privacyType }.distinct().sorted()
     }
 
diff --git a/services/OWNERS b/services/OWNERS
index 03e0807..3b972e9 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -3,4 +3,4 @@
 # art-team@ manages the system server profile
 per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
 
-per-file java/com/android/server/* = toddke@google.com
+per-file java/com/android/server/* = toddke@google.com,patb@google.com
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 679f18e..7cf5fd6 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -16,6 +16,10 @@
 
 package android.os;
 
+import com.android.internal.os.BinderCallsStats;
+
+import java.util.Collection;
+
 /**
  * Battery stats local system service interface. This is used to pass internal data out of
  * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
@@ -41,4 +45,10 @@
      * @param sinceLast how long in millis has it been since a job was run
      */
     public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
+
+    /**
+     * Informs battery stats of binder stats for the given work source UID.
+     */
+    public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
+            Collection<BinderCallsStats.CallStat> callStats);
 }
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index f4a8f37..339ca84 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Process;
 import android.os.SystemProperties;
@@ -173,10 +174,10 @@
             }
 
             try {
-                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
-                            Settings.Global.BINDER_CALLS_STATS));
+                mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                        Settings.Global.BINDER_CALLS_STATS));
             } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Bad binder call stats settings", e);
+                Slog.e(TAG, "Bad binder call stats settings", e);
             }
             mBinderCallsStats.setDetailedTracking(mParser.getBoolean(
                     SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT));
@@ -298,6 +299,11 @@
                 CachedDeviceState.Readonly deviceState = getLocalService(
                         CachedDeviceState.Readonly.class);
                 mBinderCallsStats.setDeviceState(deviceState);
+
+                BatteryStatsInternal batteryStatsInternal = getLocalService(
+                        BatteryStatsInternal.class);
+                mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats);
+
                 // It needs to be called before mService.systemReady to make sure the observer is
                 // initialized before installing it.
                 mWorkSourceProvider.systemReady(getContext());
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2744f11..5122be2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10272,11 +10272,13 @@
                 if (lines > 0) {
                     sb.append("\n");
 
-                    // Merge several logcat streams, and take the last N lines
                     InputStreamReader input = null;
                     try {
                         java.lang.Process logcat = new ProcessBuilder(
-                                "/system/bin/timeout", "-k", "15s", "10s",
+                                // Time out after 10s, but kill logcat with SEGV
+                                // so we can investigate why it didn't finish.
+                                "/system/bin/timeout", "-s", "SEGV", "10s",
+                                // Merge several logcat streams, and take the last N lines.
                                 "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system",
                                 "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
                                         .redirectErrorStream(true).start();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 226802c..b45237c 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -66,6 +66,7 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.PowerProfile;
 import com.android.internal.os.RailStats;
 import com.android.internal.os.RpmStats;
@@ -87,6 +88,7 @@
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
@@ -304,6 +306,12 @@
             if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast);
             BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast);
         }
+
+        @Override
+        public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
+                Collection<BinderCallsStats.CallStat> callStats) {
+            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
+        }
     }
 
     private static void awaitUninterruptibly(Future<?> future) {
@@ -1771,5 +1779,4 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 90d9409..0c8114c 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -20,6 +20,7 @@
 # Permissions & Packages
 svetoslavganov@google.com
 toddke@google.com
+patb@google.com
 
 # Battery Stats
 joeo@google.com
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 115899a..668d290 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -2026,8 +2026,7 @@
         };
         for (File baseDir: baseDirs) {
             File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH);
-            try {
-                InputStream stream = new FileInputStream(confFile);
+            try (InputStream stream = new FileInputStream(confFile)) {
                 names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream));
             } catch (FileNotFoundException e) {
                 // It's ok if the file does not exist.
@@ -2060,8 +2059,7 @@
         final File baseDir = Environment.getVendorDirectory();
         final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH);
 
-        try {
-            final InputStream stream = new FileInputStream(confFile);
+        try (final InputStream stream = new FileInputStream(confFile)) {
             return ConfigurationProcessor.processInputPortAssociations(stream);
         } catch (FileNotFoundException e) {
             // Most of the time, file will not exist, which is expected.
@@ -2173,10 +2171,10 @@
             @Override
             public void visitKeyboardLayout(Resources resources,
                     int keyboardLayoutResId, KeyboardLayout layout) {
-                try {
+                try (final InputStreamReader stream = new InputStreamReader(
+                               resources.openRawResource(keyboardLayoutResId))) {
                     result[0] = layout.getDescriptor();
-                    result[1] = Streams.readFully(new InputStreamReader(
-                            resources.openRawResource(keyboardLayoutResId)));
+                    result[1] = Streams.readFully(stream);
                 } catch (IOException ex) {
                 } catch (NotFoundException ex) {
                 }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 77c1c1d..49a0a88 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -31,6 +31,9 @@
 import android.content.pm.PackageInfo;
 import android.os.BatteryManagerInternal;
 import android.os.Environment;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -82,10 +85,15 @@
     private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2;
     // Optimizations should be aborted. No space left on device.
     private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
+    // Optimizations should be aborted. Thermal throttling level too high.
+    private static final int OPTIMIZE_ABORT_THERMAL = 4;
 
     // Used for calculating space threshold for downgrading unused apps.
     private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
 
+    // Thermal cutoff value used if one isn't defined by a system property.
+    private static final int THERMAL_CUTOFF_DEFAULT = PowerManager.THERMAL_STATUS_MODERATE;
+
     /**
      * Set of failed packages remembered across job runs.
      */
@@ -107,8 +115,14 @@
     private static final long mDowngradeUnusedAppsThresholdInMillis =
             getDowngradeUnusedAppsThresholdInMillis();
 
+    private final IThermalService mThermalService =
+            IThermalService.Stub.asInterface(
+                ServiceManager.getService(Context.THERMAL_SERVICE));
+
     private static List<PackagesUpdatedListener> sPackagesUpdatedListeners = new ArrayList<>();
 
+    private int mThermalStatusCutoff = THERMAL_CUTOFF_DEFAULT;
+
     public static void schedule(Context context) {
         if (isBackgroundDexoptDisabled()) {
             return;
@@ -251,12 +265,18 @@
                     Slog.w(TAG, "Idle optimizations aborted because of space constraints.");
                 } else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
                     Slog.w(TAG, "Idle optimizations aborted by job scheduler.");
+                } else if (result == OPTIMIZE_ABORT_THERMAL) {
+                    Slog.w(TAG, "Idle optimizations aborted by thermal throttling.");
                 } else {
                     Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result);
                 }
-                if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+
+                if (result == OPTIMIZE_ABORT_THERMAL) {
+                    // Abandon our timeslice and reschedule
+                    jobFinished(jobParams, /* wantsReschedule */ true);
+                } else if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
                     // Abandon our timeslice and do not reschedule.
-                    jobFinished(jobParams, /* reschedule */ false);
+                    jobFinished(jobParams, /* wantsReschedule */ false);
                 }
             }
         }.start();
@@ -542,6 +562,24 @@
             // JobScheduler requested an early abort.
             return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
         }
+
+        // Abort background dexopt if the device is in a moderate or stronger thermal throttling
+        // state.
+        try {
+            final int thermalStatus = mThermalService.getCurrentThermalStatus();
+
+            if (DEBUG) {
+                Log.i(TAG, "Thermal throttling status during bgdexopt: " + thermalStatus);
+            }
+
+            if (thermalStatus >= mThermalStatusCutoff) {
+                return OPTIMIZE_ABORT_THERMAL;
+            }
+        } catch (RemoteException ex) {
+            // Because this is a intra-process Binder call it is impossible for a RemoteException
+            // to be raised.
+        }
+
         long usableSpace = mDataDir.getUsableSpace();
         if (usableSpace < lowStorageThreshold) {
             // Rather bail than completely fill up the disk.
@@ -603,6 +641,9 @@
             return false;
         }
 
+        mThermalStatusCutoff =
+            SystemProperties.getInt("dalvik.vm.dexopt.thermal-cutoff", THERMAL_CUTOFF_DEFAULT);
+
         boolean result;
         if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
             result = runPostBootUpdate(params, pm, pkgs);
diff --git a/services/incremental/OWNERS b/services/incremental/OWNERS
index ad5eca7..7ebb962 100644
--- a/services/incremental/OWNERS
+++ b/services/incremental/OWNERS
@@ -5,3 +5,4 @@
 schfan@google.com
 toddke@google.com
 zyy@google.com
+patb@google.com
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index a02a039..d041eec 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -159,13 +159,13 @@
 
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
+import com.android.internal.util.test.FsUtil;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.usage.AppStandbyInternal;
 
 import com.google.common.util.concurrent.AbstractFuture;
 
-import libcore.io.IoUtils;
 import libcore.io.Streams;
 
 import org.junit.After;
@@ -2347,7 +2347,7 @@
     private void setNetpolicyXml(Context context) throws Exception {
         mPolicyDir = context.getFilesDir();
         if (mPolicyDir.exists()) {
-            IoUtils.deleteContents(mPolicyDir);
+            FsUtil.deleteContents(mPolicyDir);
         }
         if (!TextUtils.isEmpty(mNetpolicyXml)) {
             final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml;
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 579b33e..e332d3f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -27,6 +27,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 
@@ -283,10 +284,13 @@
      * number relies on presence.  Should only be set if the {@code PhoneAccount} also has
      * {@link #CAPABILITY_VIDEO_CALLING}.
      * <p>
-     * When set, the {@link ConnectionService} is responsible for toggling the
+     * Note: As of Android 12, using the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit on the
      * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} column to indicate whether
-     * a contact's phone number supports video calling.
+     * a contact's phone number supports video calling has been deprecated and should only be used
+     * on devices where {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set. On newer
+     * devices, applications must use {@link android.telephony.ims.RcsUceAdapter} instead to
+     * determine whether or not a contact's phone number supports carrier video calling.
      * <p>
      * See {@link #getCapabilities}
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f6ba7be..eb423b4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -36,6 +36,7 @@
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.ImsRegistrationAttributes;
 import android.telephony.ims.ImsSsData;
+import android.telephony.ims.RcsUceAdapter;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.feature.RcsFeature;
 
@@ -4071,6 +4072,9 @@
          * If this flag is disabled, the capabilities cache will not be refreshed internally at all
          * and will only be updated if the cached capabilities are stale when an application
          * requests them.
+         *
+         * @see RcsUceAdapter#isUceSettingEnabled() more information about this feature and how
+         * it is enabled by the user.
          */
         public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL =
                 KEY_PREFIX + "rcs_bulk_capability_exchange_bool";
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 5f4e1e6..81d8207 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -56,7 +56,9 @@
 
     /**
      * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery
-     * using User Capability Exchange (UCE).
+     * using User Capability Exchange (UCE), which enables a service that periodically shares the
+     * phone numbers of all of the contacts in the user's address book with the carrier to refresh
+     * the RCS capabilities associated with those contacts as the local cache becomes stale.
      * <p>
      * An application that depends on RCS contact discovery being enabled must send this intent
      * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index dd91026..7a1c092 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -28,10 +28,13 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
 import android.telephony.ims.aidl.IImsRcsController;
 import android.telephony.ims.aidl.IRcsUceControllerCallback;
 import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -417,7 +420,7 @@
      * <p>
      * After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has
      * been called, the reference to this callback will be discarded on the service side.
-     * @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+     * @see #requestCapabilities(Collection, Executor, CapabilitiesCallback)
      * @hide
      */
     @SystemApi
@@ -464,10 +467,16 @@
     }
 
     /**
-     * Request the User Capability Exchange capabilities for one or more contacts.
+     * Request the RCS capabilities for one or more contacts using RCS User Capability Exchange.
      * <p>
-     * This will return the cached capabilities of the contact and will not perform a capability
-     * poll on the network unless there are contacts being queried with stale information.
+     * This API will first check a local cache for the requested numbers and return the cached
+     * RCS capabilities of each number if the cache exists and is not stale. If the cache for a
+     * number is stale or there is no cached information about the requested number, the device will
+     * then perform a query to the carrier's network to request the RCS capabilities of the
+     * requested numbers.
+     * <p>
+     * Depending on the number of requests being sent, this API may throttled internally as the
+     * operations are queued to be executed by the carrier's network.
      * <p>
      * Be sure to check the availability of this feature using
      * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
@@ -552,13 +561,15 @@
     }
 
     /**
-     * Ignore the device cache and perform a capability discovery for one contact, also called
-     * "availability fetch."
+     * Request the RCS capabilities for a phone number using User Capability Exchange.
      * <p>
-     * This will always perform a query to the network as long as requests are over the carrier
-     * availability fetch throttling threshold. If too many network requests are sent too quickly,
-     * #ERROR_TOO_MANY_REQUESTS will be returned.
-     *
+     * Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches
+     * the result received from the network for a certain amount of time and uses that cached result
+     * for subsequent requests for RCS capabilities of the same phone number, this API will always
+     * request the RCS capabilities of a contact from the carrier's network.
+     * <p>
+     * Depending on the number of requests, this API may throttled internally as the operations are
+     * queued to be executed by the carrier's network.
      * <p>
      * Be sure to check the availability of this feature using
      * {@link ImsRcsManager#isAvailable(int, int)} and ensuring
@@ -680,7 +691,8 @@
      * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}.
      * <p>
      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription
-     * changed events and call {@link #unregisterPublishStateCallback} to clean up.
+     * changed events and call
+     * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up.
      * <p>
      * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is
      * registered with the current publish state.
@@ -770,13 +782,23 @@
     }
 
     /**
-     * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
-     * associated subscription.
+     * The setting for whether or not the user has opted in to the automatic refresh of the RCS
+     * capabilities associated with the contacts in the user's contact address book. By default,
+     * this setting is disabled and must be enabled after the user has seen the opt-in dialog shown
+     * by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
+     * <p>
+     * If this feature is enabled, the device will periodically share the phone numbers of all of
+     * the contacts in the user's address book with the carrier to refresh the RCS capabilities
+     * cache associated with those contacts as the local cache becomes stale.
+     * <p>
+     * This setting will only enable this feature if
+     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
      * <p>
      * Note: This setting does not affect whether or not the device publishes its service
      * capabilities if the subscription supports presence publication.
      *
-     * @return true if the user’s setting for UCE is enabled, false otherwise.
+     * @return true if the user has opted in for automatic refresh of the RCS capabilities of their
+     * contacts, false otherwise.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
@@ -802,18 +824,33 @@
     }
 
     /**
-     * Change the user’s setting for whether or not UCE is enabled for the associated subscription.
+     * Change the user’s setting for whether or not the user has opted in to the automatic
+     * refresh of the RCS capabilities associated with the contacts in the user's contact address
+     * book. By default, this setting is disabled and must be enabled using this method after the
+     * user has seen the opt-in dialog shown by
+     * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}.
      * <p>
-     * If an application Requires UCE, they will launch an Activity using the Intent
-     * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if
-     * they wish to enable this feature. This setting should only be enabled after the user has
-     * opted-in to capability exchange.
+     * If an application wishes to request that the user enable this feature, they must launch an
+     * Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN},
+     * which will ask the user if they wish to enable this feature. This setting must only be
+     * enabled after the user has opted-in to this feature.
+     * <p>
+     * This must not affect the
+     * {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or
+     * {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API,
+     * as those APIs are still required for per-contact RCS capability queries of phone numbers
+     * required for operations such as placing a Video Telephony call or starting an RCS chat
+     * session.
+     * <p>
+     * This setting will only enable this feature if
+     * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled.
      * <p>
      * Note: This setting does not affect whether or not the device publishes its service
      * capabilities if the subscription supports presence publication.
      *
-     * @param isEnabled the user's setting for whether or not they wish for User
-     *         Capability Exchange to be enabled.
+     * @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities
+     *                  of their contacts, or false if they have chosen to opt-out. By default this
+     *                  setting is disabled.
      * @throws ImsException if the subscription associated with this instance of
      * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
      * available. This can happen if the ImsService has crashed, for example, or if the subscription
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/OWNERS b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS
new file mode 100644
index 0000000..3414a74
--- /dev/null
+++ b/tests/BackgroundDexOptServiceIntegrationTests/OWNERS
@@ -0,0 +1 @@
+include platform/art:/OWNERS
diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
index e05816e..90ddb6f 100644
--- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
+++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
 import android.os.SystemProperties;
 import android.os.storage.StorageManager;
 import android.util.Log;
@@ -201,11 +202,16 @@
         fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER));
     }
 
-    // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run
     private static void runBackgroundDexOpt() throws IOException {
+        runBackgroundDexOpt("Success");
+    }
+
+    // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run
+    private static void runBackgroundDexOpt(String expectedStatus) throws IOException {
         String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME);
-        if (!result.trim().equals("Success")) {
-            throw new IllegalStateException("Expected command success, received >" + result + "<");
+        if (!result.trim().equals(expectedStatus)) {
+            throw new IllegalStateException("Expected status: " + expectedStatus
+                + "; Received: " + result.trim());
         }
     }
 
@@ -242,6 +248,16 @@
         runShellCommand(String.format("cmd package compile -f -m %s %s", filter, pkg));
     }
 
+    // Override the thermal status of the device
+    public static void overrideThermalStatus(int status) throws IOException {
+        runShellCommand("cmd thermalservice override-status " + status);
+    }
+
+    // Reset the thermal status of the device
+    public static void resetThermalStatus() throws IOException {
+        runShellCommand("cmd thermalservice reset");
+    }
+
     // Test that background dexopt under normal conditions succeeds.
     @Test
     public void testBackgroundDexOpt() throws IOException {
@@ -307,4 +323,17 @@
         }
     }
 
+    // Test that background dexopt job doesn't trigger if the device is under thermal throttling.
+    @Test
+    public void testBackgroundDexOptThermalThrottling() throws IOException {
+        try {
+            compilePackageWithFilter(PACKAGE_NAME, "verify");
+            overrideThermalStatus(PowerManager.THERMAL_STATUS_MODERATE);
+            // The bgdexopt task should fail when onStartJob is run
+            runBackgroundDexOpt("Failure");
+            Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME));
+        } finally {
+            resetThermalStatus();
+        }
+    }
 }
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java
new file mode 100644
index 0000000..e656612
--- /dev/null
+++ b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.internal.util.test;
+
+import java.io.File;
+
+public class FsUtil {
+
+    /**
+     * Deletes all files under a given directory. Deliberately ignores errors, on the assumption
+     * that test cleanup is only supposed to be best-effort.
+     *
+     * @param dir directory to clear its contents
+     */
+    public static void deleteContents(File dir) {
+        File[] files = dir.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                if (file.isDirectory()) {
+                    deleteContents(file);
+                }
+                file.delete();
+            }
+        }
+    }
+}
diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS
index f1903a5..69dfcc9 100644
--- a/tools/aapt2/OWNERS
+++ b/tools/aapt2/OWNERS
@@ -1,3 +1,4 @@
 set noparent
 toddke@google.com
-rtmitchell@google.com
\ No newline at end of file
+rtmitchell@google.com
+patb@google.com
\ No newline at end of file
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index f25fcdc..514f17a0 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -5,7 +5,21 @@
     # Change appears to be in AOSP
     exit 0
 else
-    # Change appears to be non-AOSP; search for files
+    # Change appears to be non-AOSP.
+
+    # If this is a cherry-pick, then allow it.
+    cherrypick=0
+    while read -r line ; do
+      if [[ $line =~ cherry\ picked\ from  ]] ; then
+        (( cherrypick++ ))
+      fi
+    done < <(git show $1)
+    if (( cherrypick != 0 )); then
+      # This is a cherry-pick, so allow it.
+      exit 0
+    fi
+
+    # See if any files are affected.
     count=0
     while read -r file ; do
         if (( count == 0 )); then
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index c19ae3b..a117aa0 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -43,8 +43,8 @@
  * cccc dd
  */
 fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String {
-    val col1w = map { (a, _) -> a.length }.max()!!
-    val col2w = map { (_, b) -> b.length }.max()!!
+    val col1w = map { (a, _) -> a.length }.maxOrNull()!!
+    val col2w = map { (_, b) -> b.length }.maxOrNull()!!
     return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n")
 }