Merge "More OWNERS refactoring"
diff --git a/Android.bp b/Android.bp
index d976b91..00b4198 100644
--- a/Android.bp
+++ b/Android.bp
@@ -294,6 +294,7 @@
     srcs: [
         ":framework-non-updatable-sources",
         "core/java/**/*.logtags",
+        ":apex-info-list",
     ],
     aidl: {
         generate_get_transaction_name: true,
diff --git a/PACKAGE_MANAGER_OWNERS b/PACKAGE_MANAGER_OWNERS
new file mode 100644
index 0000000..e4549b4
--- /dev/null
+++ b/PACKAGE_MANAGER_OWNERS
@@ -0,0 +1,3 @@
+chiuwinson@google.com
+patb@google.com
+schfan@google.com
\ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index 0eba9d7..6f4a779 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -112,6 +112,7 @@
     field public static final String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
     field public static final String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
     field public static final String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
+    field public static final String NEARBY_WIFI_DEVICES = "android.permission.NEARBY_WIFI_DEVICES";
     field public static final String NFC = "android.permission.NFC";
     field public static final String NFC_PREFERRED_PAYMENT_INFO = "android.permission.NFC_PREFERRED_PAYMENT_INFO";
     field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
@@ -31432,7 +31433,9 @@
     method @NonNull public static android.os.Parcel obtain();
     method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
     method @Nullable public Object[] readArray(@Nullable ClassLoader);
+    method @Nullable public <T> T[] readArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public java.util.ArrayList readArrayList(@Nullable ClassLoader);
+    method @Nullable public <T> java.util.ArrayList<T> readArrayList(@Nullable ClassLoader, @NonNull Class<? extends T>);
     method public void readBinderArray(@NonNull android.os.IBinder[]);
     method public void readBinderList(@NonNull java.util.List<android.os.IBinder>);
     method public boolean readBoolean();
@@ -31460,6 +31463,7 @@
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
     method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
+    method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
     method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
     method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
@@ -31469,6 +31473,7 @@
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
     method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
+    method @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader, @NonNull Class<? extends T>);
     method @Nullable public android.util.SparseBooleanArray readSparseBooleanArray();
     method @Nullable public String readString();
     method public void readStringArray(@NonNull String[]);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index b574d04..49df45a 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -311,6 +311,10 @@
 
 package android.provider {
 
+  public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns {
+    method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static java.util.Map<java.lang.String,java.util.List<android.content.ContentValues>> queryRawContactEntity(@NonNull android.content.ContentResolver, long);
+  }
+
   public final class DeviceConfig {
     field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager";
     field public static final String NAMESPACE_APP_STANDBY = "app_standby";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 13f7cfb..db3cedd 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1935,6 +1935,22 @@
     field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
   }
 
+  public final class BluetoothActivityEnergyInfo implements android.os.Parcelable {
+    method public int getBluetoothStackState();
+    method public long getControllerEnergyUsed();
+    method public long getControllerIdleTimeMillis();
+    method public long getControllerRxTimeMillis();
+    method public long getControllerTxTimeMillis();
+    method public long getTimestampMillis();
+    method @NonNull public java.util.List<android.bluetooth.UidTraffic> getUidTraffic();
+    method public boolean isValid();
+    field public static final int BT_STACK_STATE_INVALID = 0; // 0x0
+    field public static final int BT_STACK_STATE_STATE_ACTIVE = 1; // 0x1
+    field public static final int BT_STACK_STATE_STATE_IDLE = 3; // 0x3
+    field public static final int BT_STACK_STATE_STATE_SCANNING = 2; // 0x2
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothActivityEnergyInfo> CREATOR;
+  }
+
   public final class BluetoothAdapter {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean disable(boolean);
@@ -2234,6 +2250,13 @@
     method @NonNull public android.bluetooth.OobData.LeBuilder setRandomizerHash(@NonNull byte[]);
   }
 
+  public final class UidTraffic implements java.lang.Cloneable android.os.Parcelable {
+    method public long getRxBytes();
+    method public long getTxBytes();
+    method public int getUid();
+    field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.UidTraffic> CREATOR;
+  }
+
 }
 
 package android.bluetooth.le {
@@ -9139,6 +9162,7 @@
     field public static final String NAMESPACE_MEDIA = "media";
     field public static final String NAMESPACE_MEDIA_NATIVE = "media_native";
     field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
+    field public static final String NAMESPACE_NNAPI_NATIVE = "nnapi_native";
     field public static final String NAMESPACE_OTA = "ota";
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 5f2c456..e08a493 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -158,10 +158,7 @@
         "android/util/LocalLog.java",
         // This should be android.util.IndentingPrintWriter, but it's not available in all branches.
         "com/android/internal/util/IndentingPrintWriter.java",
-        "com/android/internal/util/IState.java",
         "com/android/internal/util/MessageUtils.java",
-        "com/android/internal/util/State.java",
-        "com/android/internal/util/StateMachine.java",
         "com/android/internal/util/WakeupMessage.java",
     ],
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d932a29..0b336f6 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1292,6 +1292,9 @@
     /** @hide */
     public static final int OP_UWB_RANGING = AppProtoEnums.APP_OP_UWB_RANGING;
 
+    /** @hide */
+    public static final int OP_NEARBY_WIFI_DEVICES = AppProtoEnums.APP_OP_NEARBY_WIFI_DEVICES;
+
     /**
      * Activity recognition being accessed by an activity recognition source, which
      * is a component that already has access since it is the one that detects
@@ -1312,7 +1315,7 @@
 
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 116;
+    public static final int _NUM_OP = 117;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1731,6 +1734,8 @@
     public static final String OPSTR_MANAGE_MEDIA = "android:manage_media";
     /** @hide */
     public static final String OPSTR_UWB_RANGING = "android:uwb_ranging";
+    /** @hide */
+    public static final String OPSTR_NEARBY_WIFI_DEVICES = "android:nearby_wifi_devices";
 
     /**
      * Activity recognition being accessed by an activity recognition source, which
@@ -1819,6 +1824,7 @@
             OP_BLUETOOTH_CONNECT,
             OP_BLUETOOTH_ADVERTISE,
             OP_UWB_RANGING,
+            OP_NEARBY_WIFI_DEVICES,
 
             // APPOP PERMISSIONS
             OP_ACCESS_NOTIFICATIONS,
@@ -1963,6 +1969,7 @@
             OP_ACTIVITY_RECOGNITION,            // OP_ACTIVITY_RECOGNITION_SOURCE
             OP_BLUETOOTH_ADVERTISE,             // OP_BLUETOOTH_ADVERTISE
             OP_RECORD_INCOMING_PHONE_AUDIO,     // OP_RECORD_INCOMING_PHONE_AUDIO
+            OP_NEARBY_WIFI_DEVICES,             // OP_NEARBY_WIFI_DEVICES
     };
 
     /**
@@ -2085,6 +2092,7 @@
             OPSTR_ACTIVITY_RECOGNITION_SOURCE,
             OPSTR_BLUETOOTH_ADVERTISE,
             OPSTR_RECORD_INCOMING_PHONE_AUDIO,
+            OPSTR_NEARBY_WIFI_DEVICES,
     };
 
     /**
@@ -2208,6 +2216,7 @@
             "ACTIVITY_RECOGNITION_SOURCE",
             "BLUETOOTH_ADVERTISE",
             "RECORD_INCOMING_PHONE_AUDIO",
+            "NEARBY_WIFI_DEVICES"
     };
 
     /**
@@ -2332,6 +2341,7 @@
             null, // no permission for OP_ACTIVITY_RECOGNITION_SOURCE,
             Manifest.permission.BLUETOOTH_ADVERTISE,
             null, // no permission for OP_RECORD_INCOMING_PHONE_AUDIO,
+            Manifest.permission.NEARBY_WIFI_DEVICES,
     };
 
     /**
@@ -2456,6 +2466,7 @@
             null, // ACTIVITY_RECOGNITION_SOURCE
             null, // BLUETOOTH_ADVERTISE
             null, // RECORD_INCOMING_PHONE_AUDIO
+            null, // NEARBY_WIFI_DEVICES
     };
 
     /**
@@ -2579,6 +2590,7 @@
             null, // ACTIVITY_RECOGNITION_SOURCE
             null, // BLUETOOTH_ADVERTISE
             null, // RECORD_INCOMING_PHONE_AUDIO
+            null, // NEARBY_WIFI_DEVICES
     };
 
     /**
@@ -2701,6 +2713,7 @@
             AppOpsManager.MODE_ALLOWED, // ACTIVITY_RECOGNITION_SOURCE
             AppOpsManager.MODE_ALLOWED, // BLUETOOTH_ADVERTISE
             AppOpsManager.MODE_ALLOWED, // RECORD_INCOMING_PHONE_AUDIO
+            AppOpsManager.MODE_ALLOWED, // NEARBY_WIFI_DEVICES
     };
 
     /**
@@ -2827,6 +2840,7 @@
             false, // ACTIVITY_RECOGNITION_SOURCE
             false, // BLUETOOTH_ADVERTISE
             false, // RECORD_INCOMING_PHONE_AUDIO
+            false, // NEARBY_WIFI_DEVICES
     };
 
     /**
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index e0e9b62..4da51c1 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -28,6 +28,7 @@
 per-file Service* = file:/services/core/java/com/android/server/am/OWNERS
 per-file SystemServiceRegistry.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file *UserSwitchObserver* = file:/services/core/java/com/android/server/am/OWNERS
+per-file UiAutomation.java = file:/services/accessibility/OWNERS
 
 # ActivityThread
 per-file ActivityThread.java = file:/services/core/java/com/android/server/am/OWNERS
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 32ea41b..e2fd359 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1914,39 +1914,6 @@
         public abstract T createService() throws ServiceNotFoundException;
     }
 
-    /**
-     * Like StaticServiceFetcher, creates only one instance of the service per application, but when
-     * creating the service for the first time, passes it the application context of the creating
-     * application.
-     *
-     * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
-     * case where multiple application components each have their own ConnectivityManager object.
-     */
-    static abstract class StaticApplicationContextServiceFetcher<T> implements ServiceFetcher<T> {
-        private T mCachedInstance;
-
-        @Override
-        public final T getService(ContextImpl ctx) {
-            synchronized (StaticApplicationContextServiceFetcher.this) {
-                if (mCachedInstance == null) {
-                    Context appContext = ctx.getApplicationContext();
-                    // If the application context is null, we're either in the system process or
-                    // it's the application context very early in app initialization. In both these
-                    // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
-                    // to the service. http://b/27532714 .
-                    try {
-                        mCachedInstance = createService(appContext != null ? appContext : ctx);
-                    } catch (ServiceNotFoundException e) {
-                        onServiceNotFound(e);
-                    }
-                }
-                return mCachedInstance;
-            }
-        }
-
-        public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
-    }
-
     /** @hide */
     public static void onServiceNotFound(ServiceNotFoundException e) {
         // We're mostly interested in tracking down long-lived core system
diff --git a/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
new file mode 100644
index 0000000..f560434
--- /dev/null
+++ b/core/java/android/app/admin/EnterprisePlatformSecurity_OWNERS
@@ -0,0 +1,4 @@
+rubinxu@google.com
+acjohnston@google.com
+pgrafov@google.com
+alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/admin/EnterprisePlatform_OWNERS b/core/java/android/app/admin/EnterprisePlatform_OWNERS
new file mode 100644
index 0000000..fb00fe5
--- /dev/null
+++ b/core/java/android/app/admin/EnterprisePlatform_OWNERS
@@ -0,0 +1,2 @@
+file:WorkDeviceExperience_OWNERS
+file:EnterprisePlatformSecurity_OWNERS
\ No newline at end of file
diff --git a/core/java/android/app/admin/OWNERS b/core/java/android/app/admin/OWNERS
index 6acbef2..10a5f14 100644
--- a/core/java/android/app/admin/OWNERS
+++ b/core/java/android/app/admin/OWNERS
@@ -1,11 +1,5 @@
 # Bug component: 142675
 
-# Android Enterprise team
-rubinxu@google.com
-sandness@google.com
-alexkershaw@google.com
-pgrafov@google.com
+file:EnterprisePlatform_OWNERS
 
-# Emeritus
-yamasani@google.com
-eranm@google.com
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/app/admin/WorkDeviceExperience_OWNERS b/core/java/android/app/admin/WorkDeviceExperience_OWNERS
new file mode 100644
index 0000000..dcacaa2
--- /dev/null
+++ b/core/java/android/app/admin/WorkDeviceExperience_OWNERS
@@ -0,0 +1,5 @@
+work-device-experience+reviews@google.com
+scottjonathan@google.com
+arangelov@google.com
+kholoudm@google.com
+alexkershaw@google.com #{LAST_RESORT_SUGGESTION}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
index df065bf..c17a7b4 100644
--- a/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
+++ b/core/java/android/bluetooth/BluetoothActivityEnergyInfo.java
@@ -16,18 +16,26 @@
 
 package android.bluetooth;
 
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Record of energy and activity information from controller and
  * underlying bt stack state.Timestamp the record with system
- * time
+ * time.
  *
  * @hide
  */
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
 public final class BluetoothActivityEnergyInfo implements Parcelable {
     private final long mTimestamp;
     private int mBluetoothStackState;
@@ -35,13 +43,24 @@
     private long mControllerRxTimeMs;
     private long mControllerIdleTimeMs;
     private long mControllerEnergyUsed;
-    private UidTraffic[] mUidTraffic;
+    private List<UidTraffic> mUidTraffic;
+
+    /** @hide */
+    @IntDef(prefix = { "BT_STACK_STATE_" }, value = {
+            BT_STACK_STATE_INVALID,
+            BT_STACK_STATE_STATE_ACTIVE,
+            BT_STACK_STATE_STATE_SCANNING,
+            BT_STACK_STATE_STATE_IDLE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BluetoothStackState {}
 
     public static final int BT_STACK_STATE_INVALID = 0;
     public static final int BT_STACK_STATE_STATE_ACTIVE = 1;
     public static final int BT_STACK_STATE_STATE_SCANNING = 2;
     public static final int BT_STACK_STATE_STATE_IDLE = 3;
 
+    /** @hide */
     public BluetoothActivityEnergyInfo(long timestamp, int stackState,
             long txTime, long rxTime, long idleTime, long energyUsed) {
         mTimestamp = timestamp;
@@ -52,17 +71,18 @@
         mControllerEnergyUsed = energyUsed;
     }
 
-    @SuppressWarnings("unchecked")
-    BluetoothActivityEnergyInfo(Parcel in) {
+    /** @hide */
+    private BluetoothActivityEnergyInfo(Parcel in) {
         mTimestamp = in.readLong();
         mBluetoothStackState = in.readInt();
         mControllerTxTimeMs = in.readLong();
         mControllerRxTimeMs = in.readLong();
         mControllerIdleTimeMs = in.readLong();
         mControllerEnergyUsed = in.readLong();
-        mUidTraffic = in.createTypedArray(UidTraffic.CREATOR);
+        mUidTraffic = in.createTypedArrayList(UidTraffic.CREATOR);
     }
 
+    /** @hide */
     @Override
     public String toString() {
         return "BluetoothActivityEnergyInfo{"
@@ -72,11 +92,11 @@
                 + " mControllerRxTimeMs=" + mControllerRxTimeMs
                 + " mControllerIdleTimeMs=" + mControllerIdleTimeMs
                 + " mControllerEnergyUsed=" + mControllerEnergyUsed
-                + " mUidTraffic=" + Arrays.toString(mUidTraffic)
+                + " mUidTraffic=" + mUidTraffic
                 + " }";
     }
 
-    public static final @android.annotation.NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
+    public static final @NonNull Parcelable.Creator<BluetoothActivityEnergyInfo> CREATOR =
             new Parcelable.Creator<BluetoothActivityEnergyInfo>() {
                 public BluetoothActivityEnergyInfo createFromParcel(Parcel in) {
                     return new BluetoothActivityEnergyInfo(in);
@@ -87,9 +107,8 @@
                 }
             };
 
-
+    /** @hide */
     @Override
-    @SuppressWarnings("unchecked")
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(mTimestamp);
         out.writeInt(mBluetoothStackState);
@@ -97,17 +116,21 @@
         out.writeLong(mControllerRxTimeMs);
         out.writeLong(mControllerIdleTimeMs);
         out.writeLong(mControllerEnergyUsed);
-        out.writeTypedArray(mUidTraffic, flags);
+        out.writeTypedList(mUidTraffic);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
     /**
-     * @return bt stack reported state
+     * Get the Bluetooth stack state associated with the energy info.
+     *
+     * @return one of {@link #BluetoothStackState} states
      */
+    @BluetoothStackState
     public int getBluetoothStackState() {
         return mBluetoothStackState;
     }
@@ -134,7 +157,7 @@
     }
 
     /**
-     * product of current(mA), voltage(V) and time(ms)
+     * Get the product of current (mA), voltage (V), and time (ms).
      *
      * @return energy used
      */
@@ -143,22 +166,31 @@
     }
 
     /**
-     * @return timestamp(real time elapsed in milliseconds since boot) of record creation.
+     * @return timestamp (real time elapsed in milliseconds since boot) of record creation
      */
-    public long getTimeStamp() {
+    public @ElapsedRealtimeLong long getTimestampMillis() {
         return mTimestamp;
     }
 
-    public UidTraffic[] getUidTraffic() {
+    /**
+     * Get the {@link List} of each application {@link android.bluetooth.UidTraffic}.
+     *
+     * @return current {@link List} of {@link android.bluetooth.UidTraffic}
+     */
+    public @NonNull List<UidTraffic> getUidTraffic() {
+        if (mUidTraffic == null) {
+            return Collections.emptyList();
+        }
         return mUidTraffic;
     }
 
-    public void setUidTraffic(UidTraffic[] traffic) {
+    /** @hide */
+    public void setUidTraffic(List<UidTraffic> traffic) {
         mUidTraffic = traffic;
     }
 
     /**
-     * @return if the record is valid
+     * @return true if the record Tx time, Rx time, and Idle time are more than 0.
      */
     public boolean isValid() {
         return ((mControllerTxTimeMs >= 0) && (mControllerRxTimeMs >= 0)
diff --git a/core/java/android/bluetooth/UidTraffic.java b/core/java/android/bluetooth/UidTraffic.java
index 2ee786a..9982fa6 100644
--- a/core/java/android/bluetooth/UidTraffic.java
+++ b/core/java/android/bluetooth/UidTraffic.java
@@ -15,6 +15,7 @@
  */
 package android.bluetooth;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -23,27 +24,27 @@
  *
  * @hide
  */
-public class UidTraffic implements Cloneable, Parcelable {
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+public final class UidTraffic implements Cloneable, Parcelable {
     private final int mAppUid;
     private long mRxBytes;
     private long mTxBytes;
 
-    public UidTraffic(int appUid) {
-        mAppUid = appUid;
-    }
-
+    /** @hide */
     public UidTraffic(int appUid, long rx, long tx) {
         mAppUid = appUid;
         mRxBytes = rx;
         mTxBytes = tx;
     }
 
-    UidTraffic(Parcel in) {
+    /** @hide */
+    private UidTraffic(Parcel in) {
         mAppUid = in.readInt();
         mRxBytes = in.readLong();
         mTxBytes = in.readLong();
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mAppUid);
@@ -51,44 +52,60 @@
         dest.writeLong(mTxBytes);
     }
 
+    /** @hide */
     public void setRxBytes(long bytes) {
         mRxBytes = bytes;
     }
 
+    /** @hide */
     public void setTxBytes(long bytes) {
         mTxBytes = bytes;
     }
 
+    /** @hide */
     public void addRxBytes(long bytes) {
         mRxBytes += bytes;
     }
 
+    /** @hide */
     public void addTxBytes(long bytes) {
         mTxBytes += bytes;
     }
 
+    /**
+     * @return corresponding app Uid
+     */
     public int getUid() {
         return mAppUid;
     }
 
+    /**
+     * @return rx bytes count
+     */
     public long getRxBytes() {
         return mRxBytes;
     }
 
+    /**
+     * @return tx bytes count
+     */
     public long getTxBytes() {
         return mTxBytes;
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     @Override
     public UidTraffic clone() {
         return new UidTraffic(mAppUid, mRxBytes, mTxBytes);
     }
 
+    /** @hide */
     @Override
     public String toString() {
         return "UidTraffic{mAppUid=" + mAppUid + ", mRxBytes=" + mRxBytes + ", mTxBytes="
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
index 6381f55..ed50b09 100644
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ b/core/java/android/bluetooth/le/BluetoothLeUtils.java
@@ -24,6 +24,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.UUID;
 
 /**
  * Helper class for Bluetooth LE utils.
@@ -137,4 +138,21 @@
         }
     }
 
+    /**
+     * Compares two UUIDs with a UUID mask.
+     *
+     * @param data first {@link #UUID} to compare.
+     * @param uuid second {@link #UUID} to compare.
+     * @param mask mask {@link #UUID}.
+     * @return true if both UUIDs are equals when masked, false otherwise.
+     */
+    static boolean maskedEquals(UUID data, UUID uuid, UUID mask) {
+        if (mask == null) {
+            return Objects.equals(data, uuid);
+        }
+        return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                && (data.getMostSignificantBits() & mask.getMostSignificantBits())
+                == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
+    }
 }
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 8ff0181..b059193 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -28,8 +28,6 @@
 import android.os.ParcelUuid;
 import android.os.Parcelable;
 
-import com.android.internal.util.BitUtils;
-
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -448,7 +446,7 @@
 
     // Check if the uuid pattern matches the particular service uuid.
     private static boolean matchesServiceUuid(UUID uuid, UUID mask, UUID data) {
-        return BitUtils.maskedEquals(data, uuid, mask);
+        return BluetoothLeUtils.maskedEquals(data, uuid, mask);
     }
 
     /**
@@ -478,7 +476,7 @@
     // Check if the solicitation uuid pattern matches the particular service solicitation uuid.
     private static boolean matchesServiceSolicitationUuid(UUID solicitationUuid,
             UUID solicitationUuidMask, UUID data) {
-        return BitUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
+        return BluetoothLeUtils.maskedEquals(data, solicitationUuid, solicitationUuidMask);
     }
 
     // Check whether the data pattern matches the parsed data.
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 5e2340c..ef5f84c 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -22,7 +22,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothDevice;
-import android.bluetooth.le.ScanFilter;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.net.wifi.ScanResult;
 import android.os.Build;
@@ -30,9 +29,13 @@
 import android.os.Parcelable;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
 import java.util.regex.Pattern;
 
 /** @hide */
@@ -73,12 +76,19 @@
 
     static boolean matchesServiceUuid(ParcelUuid serviceUuid, ParcelUuid serviceUuidMask,
             BluetoothDevice device) {
-        ParcelUuid[] uuids = device.getUuids();
-        final boolean result = serviceUuid == null ||
-                ScanFilter.matchesServiceUuids(
-                        serviceUuid,
-                        serviceUuidMask,
-                        uuids == null ? Collections.emptyList() : Arrays.asList(uuids));
+        boolean result = false;
+        List<ParcelUuid> deviceUuids = device.getUuids() == null
+                ? Collections.emptyList() : Arrays.asList(device.getUuids());
+        if (serviceUuid == null) {
+            result = true;
+        } else {
+            for (ParcelUuid parcelUuid : deviceUuids) {
+                UUID uuidMask = serviceUuidMask == null ? null : serviceUuidMask.getUuid();
+                if (uuidsMaskedEquals(parcelUuid.getUuid(), serviceUuid.getUuid(), uuidMask)) {
+                    result = true;
+                }
+            }
+        }
         if (DEBUG) debugLogMatchResult(result, device, serviceUuid);
         return result;
     }
@@ -143,4 +153,23 @@
             throw new IllegalArgumentException("Unknown device type: " + device);
         }
     }
+
+    /**
+     * Compares two {@link #UUID} with a {@link #UUID} mask.
+     *
+     * @param data first {@link #UUID}.
+     * @param uuid second {@link #UUID}.
+     * @param mask mask {@link #UUID}.
+     * @return true if both UUIDs are equals when masked, false otherwise.
+     */
+    @VisibleForTesting
+    public static boolean uuidsMaskedEquals(UUID data, UUID uuid, UUID mask) {
+        if (mask == null) {
+            return Objects.equals(data, uuid);
+        }
+        return (data.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                == (uuid.getLeastSignificantBits() & mask.getLeastSignificantBits())
+                && (data.getMostSignificantBits() & mask.getMostSignificantBits())
+                == (uuid.getMostSignificantBits() & mask.getMostSignificantBits());
+    }
 }
diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java
index 828d482..58898cc 100644
--- a/core/java/android/companion/BluetoothLeDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLeDeviceFilter.java
@@ -75,7 +75,7 @@
             String renameSuffix, int renameBytesFrom, int renameBytesLength,
             int renameNameFrom, int renameNameLength, boolean renameBytesReverseOrder) {
         mNamePattern = namePattern;
-        mScanFilter = ObjectUtils.firstNotNull(scanFilter, ScanFilter.EMPTY);
+        mScanFilter = ObjectUtils.firstNotNull(scanFilter, new ScanFilter.Builder().build());
         mRawDataFilter = rawDataFilter;
         mRawDataFilterMask = rawDataFilterMask;
         mRenamePrefix = renamePrefix;
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index c7f92c9..660368a 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -2,11 +2,9 @@
 per-file Context.java = *
 per-file ContextWrapper.java = *
 per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
-per-file IntentFilter.java = toddke@google.com
-per-file IntentFilter.java = patb@google.com
+per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
 per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
-per-file Intent.java = toddke@google.com
-per-file Intent.java = patb@google.com
+per-file Intent.java = file:/PACKAGE_MANAGER_OWNERS
 per-file Intent.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file Intent.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file AutofillOptions* = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 1d07a03..eb8f43e 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -220,8 +220,10 @@
         String networkId = null;
         boolean roaming = !snapshot.getNetworkCapabilities().hasCapability(
                 NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
-        boolean metered = !snapshot.getNetworkCapabilities().hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+        boolean metered = !(snapshot.getNetworkCapabilities().hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+                || snapshot.getNetworkCapabilities().hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED));
 
         final int oemManaged = getOemBitfield(snapshot.getNetworkCapabilities());
 
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index 08f75df..74506da 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -160,19 +160,19 @@
     }
 
     /**
-     * Template to match cellular networks with the given IMSI and {@code ratType}.
-     * Use {@link #NETWORK_TYPE_ALL} to include all network types when filtering.
-     * See {@code TelephonyManager.NETWORK_TYPE_*}.
+     * Template to match cellular networks with the given IMSI, {@code ratType} and
+     * {@code metered}. Use {@link #NETWORK_TYPE_ALL} to include all network types when
+     * filtering. See {@code TelephonyManager.NETWORK_TYPE_*}.
      */
     public static NetworkTemplate buildTemplateMobileWithRatType(@Nullable String subscriberId,
-            @NetworkType int ratType) {
+            @NetworkType int ratType, int metered) {
         if (TextUtils.isEmpty(subscriberId)) {
             return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
-                    METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+                    metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                     SUBSCRIBER_ID_MATCH_RULE_EXACT);
         }
         return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
-                METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
+                metered, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL,
                 SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
@@ -305,6 +305,7 @@
         }
     }
 
+    // TODO: Deprecate this constructor, mark it @UnsupportedAppUsage(maxTargetSdk = S)
     @UnsupportedAppUsage
     public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
         this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
@@ -312,9 +313,14 @@
 
     public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
             String networkId) {
-        this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
-                DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
-                SUBSCRIBER_ID_MATCH_RULE_EXACT);
+        // Older versions used to only match MATCH_MOBILE and MATCH_MOBILE_WILDCARD templates
+        // to metered networks. It is now possible to match mobile with any meteredness, but
+        // in order to preserve backward compatibility of @UnsupportedAppUsage methods, this
+        //constructor passes METERED_YES for these types.
+        this(matchRule, subscriberId, matchSubscriberIds, networkId,
+                (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
+                : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+                OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT);
     }
 
     // TODO: Remove it after updating all of the caller.
@@ -589,11 +595,7 @@
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
-            // Only metered mobile network would be matched regardless of metered filter.
-            // This is used to exclude non-metered APNs, e.g. IMS. See ag/908650.
-            // TODO: Respect metered filter and remove mMetered condition.
-            return (ident.mType == TYPE_MOBILE && ident.mMetered)
-                    && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+            return ident.mType == TYPE_MOBILE && !ArrayUtils.isEmpty(mMatchSubscriberIds)
                     && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId)
                     && matchesCollapsedRatType(ident);
         }
@@ -707,8 +709,7 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return (ident.mType == TYPE_MOBILE && ident.mMetered)
-                    && matchesCollapsedRatType(ident);
+            return ident.mType == TYPE_MOBILE && matchesCollapsedRatType(ident);
         }
     }
 
diff --git a/core/java/android/net/SntpClient.java b/core/java/android/net/SntpClient.java
index f6852e6..0eb4cf3 100644
--- a/core/java/android/net/SntpClient.java
+++ b/core/java/android/net/SntpClient.java
@@ -17,16 +17,26 @@
 package android.net;
 
 import android.compat.annotation.UnsupportedAppUsage;
+import android.net.sntp.Duration64;
+import android.net.sntp.Timestamp64;
 import android.os.SystemClock;
 import android.util.Log;
+import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.TrafficStatsConstants;
 
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.Arrays;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Random;
+import java.util.function.Supplier;
 
 /**
  * {@hide}
@@ -60,17 +70,21 @@
     private static final int NTP_STRATUM_DEATH = 0;
     private static final int NTP_STRATUM_MAX = 15;
 
-    // Number of seconds between Jan 1, 1900 and Jan 1, 1970
-    // 70 years plus 17 leap days
-    private static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
+    // The source of the current system clock time, replaceable for testing.
+    private final Supplier<Instant> mSystemTimeSupplier;
 
-    // system time computed from NTP server response
+    private final Random mRandom;
+
+    // The last offset calculated from an NTP server response
+    private long mClockOffset;
+
+    // The last system time computed from an NTP server response
     private long mNtpTime;
 
-    // value of SystemClock.elapsedRealtime() corresponding to mNtpTime
+    // The value of SystemClock.elapsedRealtime() corresponding to mNtpTime / mClockOffset
     private long mNtpTimeReference;
 
-    // round trip time in milliseconds
+    // The round trip (network) time in milliseconds
     private long mRoundTripTime;
 
     private static class InvalidServerReplyException extends Exception {
@@ -81,6 +95,13 @@
 
     @UnsupportedAppUsage
     public SntpClient() {
+        this(Instant::now, defaultRandom());
+    }
+
+    @VisibleForTesting
+    public SntpClient(Supplier<Instant> systemTimeSupplier, Random random) {
+        mSystemTimeSupplier = Objects.requireNonNull(systemTimeSupplier);
+        mRandom = Objects.requireNonNull(random);
     }
 
     /**
@@ -126,9 +147,13 @@
             buffer[0] = NTP_MODE_CLIENT | (NTP_VERSION << 3);
 
             // get current time and write it to the request packet
-            final long requestTime = System.currentTimeMillis();
+            final Instant requestTime = mSystemTimeSupplier.get();
+            final Timestamp64 requestTimestamp = Timestamp64.fromInstant(requestTime);
+
+            final Timestamp64 randomizedRequestTimestamp =
+                    requestTimestamp.randomizeSubMillis(mRandom);
             final long requestTicks = SystemClock.elapsedRealtime();
-            writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, requestTime);
+            writeTimeStamp(buffer, TRANSMIT_TIME_OFFSET, randomizedRequestTimestamp);
 
             socket.send(request);
 
@@ -136,42 +161,44 @@
             DatagramPacket response = new DatagramPacket(buffer, buffer.length);
             socket.receive(response);
             final long responseTicks = SystemClock.elapsedRealtime();
-            final long responseTime = requestTime + (responseTicks - requestTicks);
+            final Instant responseTime = requestTime.plusMillis(responseTicks - requestTicks);
+            final Timestamp64 responseTimestamp = Timestamp64.fromInstant(responseTime);
 
             // extract the results
             final byte leap = (byte) ((buffer[0] >> 6) & 0x3);
             final byte mode = (byte) (buffer[0] & 0x7);
             final int stratum = (int) (buffer[1] & 0xff);
-            final long originateTime = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
-            final long receiveTime = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
-            final long transmitTime = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
-            final long referenceTime = readTimeStamp(buffer, REFERENCE_TIME_OFFSET);
+            final Timestamp64 referenceTimestamp = readTimeStamp(buffer, REFERENCE_TIME_OFFSET);
+            final Timestamp64 originateTimestamp = readTimeStamp(buffer, ORIGINATE_TIME_OFFSET);
+            final Timestamp64 receiveTimestamp = readTimeStamp(buffer, RECEIVE_TIME_OFFSET);
+            final Timestamp64 transmitTimestamp = readTimeStamp(buffer, TRANSMIT_TIME_OFFSET);
 
             /* Do validation according to RFC */
-            // TODO: validate originateTime == requestTime.
-            checkValidServerReply(leap, mode, stratum, transmitTime, referenceTime);
+            checkValidServerReply(leap, mode, stratum, transmitTimestamp, referenceTimestamp,
+                    randomizedRequestTimestamp, originateTimestamp);
 
-            long roundTripTime = responseTicks - requestTicks - (transmitTime - receiveTime);
-            // receiveTime = originateTime + transit + skew
-            // responseTime = transmitTime + transit - skew
-            // clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2
-            //             = ((originateTime + transit + skew - originateTime) +
-            //                (transmitTime - (transmitTime + transit - skew)))/2
-            //             = ((transit + skew) + (transmitTime - transmitTime - transit + skew))/2
-            //             = (transit + skew - transit + skew)/2
-            //             = (2 * skew)/2 = skew
-            long clockOffset = ((receiveTime - originateTime) + (transmitTime - responseTime))/2;
-            EventLogTags.writeNtpSuccess(address.toString(), roundTripTime, clockOffset);
+            long totalTransactionDurationMillis = responseTicks - requestTicks;
+            long serverDurationMillis =
+                    Duration64.between(receiveTimestamp, transmitTimestamp).toDuration().toMillis();
+            long roundTripTimeMillis = totalTransactionDurationMillis - serverDurationMillis;
+
+            Duration clockOffsetDuration = calculateClockOffset(requestTimestamp,
+                    receiveTimestamp, transmitTimestamp, responseTimestamp);
+            long clockOffsetMillis = clockOffsetDuration.toMillis();
+
+            EventLogTags.writeNtpSuccess(
+                    address.toString(), roundTripTimeMillis, clockOffsetMillis);
             if (DBG) {
-                Log.d(TAG, "round trip: " + roundTripTime + "ms, " +
-                        "clock offset: " + clockOffset + "ms");
+                Log.d(TAG, "round trip: " + roundTripTimeMillis + "ms, "
+                        + "clock offset: " + clockOffsetMillis + "ms");
             }
 
             // save our results - use the times on this side of the network latency
             // (response rather than request time)
-            mNtpTime = responseTime + clockOffset;
+            mClockOffset = clockOffsetMillis;
+            mNtpTime = responseTime.plus(clockOffsetDuration).toEpochMilli();
             mNtpTimeReference = responseTicks;
-            mRoundTripTime = roundTripTime;
+            mRoundTripTime = roundTripTimeMillis;
         } catch (Exception e) {
             EventLogTags.writeNtpFailure(address.toString(), e.toString());
             if (DBG) Log.d(TAG, "request time failed: " + e);
@@ -186,6 +213,28 @@
         return true;
     }
 
+    /** Performs the NTP clock offset calculation. */
+    @VisibleForTesting
+    public static Duration calculateClockOffset(Timestamp64 clientRequestTimestamp,
+            Timestamp64 serverReceiveTimestamp, Timestamp64 serverTransmitTimestamp,
+            Timestamp64 clientResponseTimestamp) {
+        // According to RFC4330:
+        // t is the system clock offset (the adjustment we are trying to find)
+        // t = ((T2 - T1) + (T3 - T4)) / 2
+        //
+        // Which is:
+        // t = (([server]receiveTimestamp - [client]requestTimestamp)
+        //       + ([server]transmitTimestamp - [client]responseTimestamp)) / 2
+        //
+        // See the NTP spec and tests: the numeric types used are deliberate:
+        // + Duration64.between() uses 64-bit arithmetic (32-bit for the seconds).
+        // + plus() / dividedBy() use Duration, which isn't the double precision floating point
+        //   used in NTPv4, but is good enough.
+        return Duration64.between(clientRequestTimestamp, serverReceiveTimestamp)
+                .plus(Duration64.between(clientResponseTimestamp, serverTransmitTimestamp))
+                .dividedBy(2);
+    }
+
     @Deprecated
     @UnsupportedAppUsage
     public boolean requestTime(String host, int timeout) {
@@ -194,6 +243,14 @@
     }
 
     /**
+     * Returns the offset calculated to apply to the client clock to arrive at {@link #getNtpTime()}
+     */
+    @VisibleForTesting
+    public long getClockOffset() {
+        return mClockOffset;
+    }
+
+    /**
      * Returns the time computed from the NTP transaction.
      *
      * @return time value computed from NTP server response.
@@ -225,8 +282,9 @@
     }
 
     private static void checkValidServerReply(
-            byte leap, byte mode, int stratum, long transmitTime, long referenceTime)
-            throws InvalidServerReplyException {
+            byte leap, byte mode, int stratum, Timestamp64 transmitTimestamp,
+            Timestamp64 referenceTimestamp, Timestamp64 randomizedRequestTimestamp,
+            Timestamp64 originateTimestamp) throws InvalidServerReplyException {
         if (leap == NTP_LEAP_NOSYNC) {
             throw new InvalidServerReplyException("unsynchronized server");
         }
@@ -236,73 +294,68 @@
         if ((stratum == NTP_STRATUM_DEATH) || (stratum > NTP_STRATUM_MAX)) {
             throw new InvalidServerReplyException("untrusted stratum: " + stratum);
         }
-        if (transmitTime == 0) {
-            throw new InvalidServerReplyException("zero transmitTime");
+        if (!randomizedRequestTimestamp.equals(originateTimestamp)) {
+            throw new InvalidServerReplyException(
+                    "originateTimestamp != randomizedRequestTimestamp");
         }
-        if (referenceTime == 0) {
-            throw new InvalidServerReplyException("zero reference timestamp");
+        if (transmitTimestamp.equals(Timestamp64.ZERO)) {
+            throw new InvalidServerReplyException("zero transmitTimestamp");
+        }
+        if (referenceTimestamp.equals(Timestamp64.ZERO)) {
+            throw new InvalidServerReplyException("zero referenceTimestamp");
         }
     }
 
     /**
      * Reads an unsigned 32 bit big endian number from the given offset in the buffer.
      */
-    private long read32(byte[] buffer, int offset) {
-        byte b0 = buffer[offset];
-        byte b1 = buffer[offset+1];
-        byte b2 = buffer[offset+2];
-        byte b3 = buffer[offset+3];
+    private long readUnsigned32(byte[] buffer, int offset) {
+        int i0 = buffer[offset++] & 0xFF;
+        int i1 = buffer[offset++] & 0xFF;
+        int i2 = buffer[offset++] & 0xFF;
+        int i3 = buffer[offset] & 0xFF;
 
-        // convert signed bytes to unsigned values
-        int i0 = ((b0 & 0x80) == 0x80 ? (b0 & 0x7F) + 0x80 : b0);
-        int i1 = ((b1 & 0x80) == 0x80 ? (b1 & 0x7F) + 0x80 : b1);
-        int i2 = ((b2 & 0x80) == 0x80 ? (b2 & 0x7F) + 0x80 : b2);
-        int i3 = ((b3 & 0x80) == 0x80 ? (b3 & 0x7F) + 0x80 : b3);
-
-        return ((long)i0 << 24) + ((long)i1 << 16) + ((long)i2 << 8) + (long)i3;
+        int bits = (i0 << 24) | (i1 << 16) | (i2 << 8) | i3;
+        return bits & 0xFFFF_FFFFL;
     }
 
     /**
-     * Reads the NTP time stamp at the given offset in the buffer and returns
-     * it as a system time (milliseconds since January 1, 1970).
+     * Reads the NTP time stamp from the given offset in the buffer.
      */
-    private long readTimeStamp(byte[] buffer, int offset) {
-        long seconds = read32(buffer, offset);
-        long fraction = read32(buffer, offset + 4);
-        // Special case: zero means zero.
-        if (seconds == 0 && fraction == 0) {
-            return 0;
-        }
-        return ((seconds - OFFSET_1900_TO_1970) * 1000) + ((fraction * 1000L) / 0x100000000L);
+    private Timestamp64 readTimeStamp(byte[] buffer, int offset) {
+        long seconds = readUnsigned32(buffer, offset);
+        int fractionBits = (int) readUnsigned32(buffer, offset + 4);
+        return Timestamp64.fromComponents(seconds, fractionBits);
     }
 
     /**
-     * Writes system time (milliseconds since January 1, 1970) as an NTP time stamp
-     * at the given offset in the buffer.
+     * Writes the NTP time stamp at the given offset in the buffer.
      */
-    private void writeTimeStamp(byte[] buffer, int offset, long time) {
-        // Special case: zero means zero.
-        if (time == 0) {
-            Arrays.fill(buffer, offset, offset + 8, (byte) 0x00);
-            return;
-        }
-
-        long seconds = time / 1000L;
-        long milliseconds = time - seconds * 1000L;
-        seconds += OFFSET_1900_TO_1970;
-
+    private void writeTimeStamp(byte[] buffer, int offset, Timestamp64 timestamp) {
+        long seconds = timestamp.getEraSeconds();
         // write seconds in big endian format
-        buffer[offset++] = (byte)(seconds >> 24);
-        buffer[offset++] = (byte)(seconds >> 16);
-        buffer[offset++] = (byte)(seconds >> 8);
-        buffer[offset++] = (byte)(seconds >> 0);
+        buffer[offset++] = (byte) (seconds >>> 24);
+        buffer[offset++] = (byte) (seconds >>> 16);
+        buffer[offset++] = (byte) (seconds >>> 8);
+        buffer[offset++] = (byte) (seconds);
 
-        long fraction = milliseconds * 0x100000000L / 1000L;
+        int fractionBits = timestamp.getFractionBits();
         // write fraction in big endian format
-        buffer[offset++] = (byte)(fraction >> 24);
-        buffer[offset++] = (byte)(fraction >> 16);
-        buffer[offset++] = (byte)(fraction >> 8);
-        // low order bits should be random data
-        buffer[offset++] = (byte)(Math.random() * 255.0);
+        buffer[offset++] = (byte) (fractionBits >>> 24);
+        buffer[offset++] = (byte) (fractionBits >>> 16);
+        buffer[offset++] = (byte) (fractionBits >>> 8);
+        buffer[offset] = (byte) (fractionBits);
+    }
+
+    private static Random defaultRandom() {
+        Random random;
+        try {
+            random = SecureRandom.getInstanceStrong();
+        } catch (NoSuchAlgorithmException e) {
+            // This should never happen.
+            Slog.wtf(TAG, "Unable to access SecureRandom", e);
+            random = new Random(System.currentTimeMillis());
+        }
+        return random;
     }
 }
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index 8c13ef9..a379c33 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -16,5 +16,24 @@
     {
       "path": "frameworks/opt/net/wifi"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.net"
+        },
+        {
+          "include-annotation": "android.platform.test.annotations.Presubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
   ]
 }
diff --git a/core/java/android/net/sntp/Duration64.java b/core/java/android/net/sntp/Duration64.java
new file mode 100644
index 0000000..7f29cdb
--- /dev/null
+++ b/core/java/android/net/sntp/Duration64.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.sntp;
+
+import java.time.Duration;
+
+/**
+ * A type similar to {@link Timestamp64} but used when calculating the difference between two
+ * timestamps. As such, it is a signed type, but still uses 64-bits in total and so can only
+ * represent half the magnitude of {@link Timestamp64}.
+ *
+ * <p>See <a href="https://www.eecis.udel.edu/~mills/time.html">4. Time Difference Calculations</a>.
+ *
+ * @hide
+ */
+public final class Duration64 {
+
+    public static final Duration64 ZERO = new Duration64(0);
+    private final long mBits;
+
+    private Duration64(long bits) {
+        this.mBits = bits;
+    }
+
+    /**
+     * Returns the difference between two 64-bit NTP timestamps as a {@link Duration64}, as
+     * described in the NTP spec. The times represented by the timestamps have to be within {@link
+     * Timestamp64#MAX_SECONDS_IN_ERA} (~68 years) of each other for the calculation to produce a
+     * correct answer.
+     */
+    public static Duration64 between(Timestamp64 startInclusive, Timestamp64 endExclusive) {
+        long oneBits = (startInclusive.getEraSeconds() << 32)
+                | (startInclusive.getFractionBits() & 0xFFFF_FFFFL);
+        long twoBits = (endExclusive.getEraSeconds() << 32)
+                | (endExclusive.getFractionBits() & 0xFFFF_FFFFL);
+        long resultBits = twoBits - oneBits;
+        return new Duration64(resultBits);
+    }
+
+    /**
+     * Add two {@link Duration64} instances together. This performs the calculation in {@link
+     * Duration} and returns a {@link Duration} to increase the magnitude of accepted arguments,
+     * since {@link Duration64} only supports signed 32-bit seconds. The use of {@link Duration}
+     * limits precision to nanoseconds.
+     */
+    public Duration plus(Duration64 other) {
+        // From https://www.eecis.udel.edu/~mills/time.html:
+        // "The offset and delay calculations require sums and differences of these raw timestamp
+        // differences that can span no more than from 34 years in the future to 34 years in the
+        // past without overflow. This is a fundamental limitation in 64-bit integer calculations.
+        //
+        // In the NTPv4 reference implementation, all calculations involving offset and delay values
+        // use 64-bit floating double arithmetic, with the exception of raw timestamp subtraction,
+        // as mentioned above. The raw timestamp differences are then converted to 64-bit floating
+        // double format without loss of precision or chance of overflow in subsequent
+        // calculations."
+        //
+        // Here, we use Duration instead, which provides sufficient range, but loses precision below
+        // nanos.
+        return this.toDuration().plus(other.toDuration());
+    }
+
+    /**
+     * Returns a {@link Duration64} equivalent of the supplied duration, if the magnitude can be
+     * represented. Because {@link Duration64} uses a fixed point type for sub-second values it
+     * cannot represent all nanosecond values precisely and so the conversion can be lossy.
+     *
+     * @throws IllegalArgumentException if the supplied duration is too big to be represented
+     */
+    public static Duration64 fromDuration(Duration duration) {
+        long seconds = duration.getSeconds();
+        if (seconds < Integer.MIN_VALUE || seconds > Integer.MAX_VALUE) {
+            throw new IllegalArgumentException();
+        }
+        long bits = (seconds << 32)
+                | (Timestamp64.nanosToFractionBits(duration.getNano()) & 0xFFFF_FFFFL);
+        return new Duration64(bits);
+    }
+
+    /**
+     * Returns a {@link Duration} equivalent of this duration. Because {@link Duration64} uses a
+     * fixed point type for sub-second values it can values smaller than nanosecond precision and so
+     * the conversion can be lossy.
+     */
+    public Duration toDuration() {
+        int seconds = getSeconds();
+        int nanos = getNanos();
+        return Duration.ofSeconds(seconds, nanos);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Duration64 that = (Duration64) o;
+        return mBits == that.mBits;
+    }
+
+    @Override
+    public int hashCode() {
+        return java.util.Objects.hash(mBits);
+    }
+
+    @Override
+    public String toString() {
+        Duration duration = toDuration();
+        return Long.toHexString(mBits)
+                + "(" + duration.getSeconds() + "s " + duration.getNano() + "ns)";
+    }
+
+    /**
+     * Returns the <em>signed</em> seconds in this duration.
+     */
+    public int getSeconds() {
+        return (int) (mBits >> 32);
+    }
+
+    /**
+     * Returns the <em>unsigned</em> nanoseconds in this duration (truncated).
+     */
+    public int getNanos() {
+        return Timestamp64.fractionBitsToNanos((int) (mBits & 0xFFFF_FFFFL));
+    }
+}
diff --git a/core/java/android/net/sntp/Timestamp64.java b/core/java/android/net/sntp/Timestamp64.java
new file mode 100644
index 0000000..8ddfd77
--- /dev/null
+++ b/core/java/android/net/sntp/Timestamp64.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.sntp;
+
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Random;
+
+/**
+ * The 64-bit type ("timestamp") that NTP uses to represent a point in time. It only holds the
+ * lowest 32-bits of the number of seconds since 1900-01-01 00:00:00. Consequently, to turn an
+ * instance into an unambiguous point in time the era number must be known. Era zero runs from
+ * 1900-01-01 00:00:00 to a date in 2036.
+ *
+ * It stores sub-second values using a 32-bit fixed point type, so it can resolve values smaller
+ * than a nanosecond, but is imprecise (i.e. it truncates).
+ *
+ * See also <a href=https://www.eecis.udel.edu/~mills/y2k.html>NTP docs</a>.
+ *
+ * @hide
+ */
+public final class Timestamp64 {
+
+    public static final Timestamp64 ZERO = fromComponents(0, 0);
+    static final int SUB_MILLIS_BITS_TO_RANDOMIZE = 32 - 10;
+
+    // Number of seconds between Jan 1, 1900 and Jan 1, 1970
+    // 70 years plus 17 leap days
+    static final long OFFSET_1900_TO_1970 = ((365L * 70L) + 17L) * 24L * 60L * 60L;
+    static final long MAX_SECONDS_IN_ERA = 0xFFFF_FFFFL;
+    static final long SECONDS_IN_ERA = MAX_SECONDS_IN_ERA + 1;
+
+    static final int NANOS_PER_SECOND = 1_000_000_000;
+
+    /** Creates a {@link Timestamp64} from the seconds and fraction components. */
+    public static Timestamp64 fromComponents(long eraSeconds, int fractionBits) {
+        return new Timestamp64(eraSeconds, fractionBits);
+    }
+
+    /** Creates a {@link Timestamp64} by decoding a string in the form "e4dc720c.4d4fc9eb". */
+    public static Timestamp64 fromString(String string) {
+        final int requiredLength = 17;
+        if (string.length() != requiredLength || string.charAt(8) != '.') {
+            throw new IllegalArgumentException(string);
+        }
+        String eraSecondsString = string.substring(0, 8);
+        String fractionString = string.substring(9);
+        long eraSeconds = Long.parseLong(eraSecondsString, 16);
+
+        // Use parseLong() because the type is unsigned. Integer.parseInt() will reject 0x70000000
+        // or above as being out of range.
+        long fractionBitsAsLong = Long.parseLong(fractionString, 16);
+        if (fractionBitsAsLong < 0 || fractionBitsAsLong > 0xFFFFFFFFL) {
+            throw new IllegalArgumentException("Invalid fractionBits:" + fractionString);
+        }
+        return new Timestamp64(eraSeconds, (int) fractionBitsAsLong);
+    }
+
+    /**
+     * Converts an {@link Instant} into a {@link Timestamp64}. This is lossy: Timestamp64 only
+     * contains the number of seconds in a given era, but the era is not stored. Also, sub-second
+     * values are not stored precisely.
+     */
+    public static Timestamp64 fromInstant(Instant instant) {
+        long ntpEraSeconds = instant.getEpochSecond() + OFFSET_1900_TO_1970;
+        if (ntpEraSeconds < 0) {
+            ntpEraSeconds = SECONDS_IN_ERA - (-ntpEraSeconds % SECONDS_IN_ERA);
+        }
+        ntpEraSeconds %= SECONDS_IN_ERA;
+
+        long nanos = instant.getNano();
+        int fractionBits = nanosToFractionBits(nanos);
+
+        return new Timestamp64(ntpEraSeconds, fractionBits);
+    }
+
+    private final long mEraSeconds;
+    private final int mFractionBits;
+
+    private Timestamp64(long eraSeconds, int fractionBits) {
+        if (eraSeconds < 0 || eraSeconds > MAX_SECONDS_IN_ERA) {
+            throw new IllegalArgumentException(
+                    "Invalid parameters. seconds=" + eraSeconds + ", fraction=" + fractionBits);
+        }
+        this.mEraSeconds = eraSeconds;
+        this.mFractionBits = fractionBits;
+    }
+
+    /** Returns the number of seconds in the NTP era. */
+    public long getEraSeconds() {
+        return mEraSeconds;
+    }
+
+    /** Returns the fraction of a second as 32-bit, unsigned fixed-point bits. */
+    public int getFractionBits() {
+        return mFractionBits;
+    }
+
+    @Override
+    public String toString() {
+        return TextUtils.formatSimple("%08x.%08x", mEraSeconds, mFractionBits);
+    }
+
+    /** Returns the instant represented by this value in the specified NTP era. */
+    public Instant toInstant(int ntpEra) {
+        long secondsSinceEpoch = mEraSeconds - OFFSET_1900_TO_1970;
+        secondsSinceEpoch += ntpEra * SECONDS_IN_ERA;
+
+        int nanos = fractionBitsToNanos(mFractionBits);
+        return Instant.ofEpochSecond(secondsSinceEpoch, nanos);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Timestamp64 that = (Timestamp64) o;
+        return mEraSeconds == that.mEraSeconds && mFractionBits == that.mFractionBits;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mEraSeconds, mFractionBits);
+    }
+
+    static int fractionBitsToNanos(int fractionBits) {
+        long fractionBitsLong = fractionBits & 0xFFFF_FFFFL;
+        return (int) ((fractionBitsLong * NANOS_PER_SECOND) >>> 32);
+    }
+
+    static int nanosToFractionBits(long nanos) {
+        if (nanos > NANOS_PER_SECOND) {
+            throw new IllegalArgumentException();
+        }
+        return (int) ((nanos << 32) / NANOS_PER_SECOND);
+    }
+
+    /**
+     * Randomizes the fraction bits that represent sub-millisecond values. i.e. the randomization
+     * won't change the number of milliseconds represented after truncation. This is used to
+     * implement the part of the NTP spec that calls for clients with millisecond accuracy clocks
+     * to send randomized LSB values rather than zeros.
+     */
+    public Timestamp64 randomizeSubMillis(Random random) {
+        int randomizedFractionBits =
+                randomizeLowestBits(random, this.mFractionBits, SUB_MILLIS_BITS_TO_RANDOMIZE);
+        return new Timestamp64(mEraSeconds, randomizedFractionBits);
+    }
+
+    /**
+     * Randomizes the specified number of LSBs in {@code value} by using replacement bits from
+     * {@code Random.getNextInt()}.
+     */
+    @VisibleForTesting
+    public static int randomizeLowestBits(Random random, int value, int bitsToRandomize) {
+        if (bitsToRandomize < 1 || bitsToRandomize >= Integer.SIZE) {
+            // There's no point in randomizing all bits or none of the bits.
+            throw new IllegalArgumentException(Integer.toString(bitsToRandomize));
+        }
+
+        int upperBitMask = 0xFFFF_FFFF << bitsToRandomize;
+        int lowerBitMask = ~upperBitMask;
+
+        int randomValue = random.nextInt();
+        return (value & upperBitMask) | (randomValue & lowerBitMask);
+    }
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 635f581..b677b69 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -313,7 +313,7 @@
      * @hide
      */
     @CriticalNative
-    public static final native boolean isHandlingTransaction();
+    public static final native boolean isDirectlyHandlingTransaction();
 
     /**
      * Return the Linux uid assigned to the process that sent the transaction
@@ -323,7 +323,7 @@
      *        executing an incoming transaction.
      */
     public static final int getCallingUidOrThrow() {
-        if (!isHandlingTransaction()) {
+        if (!isDirectlyHandlingTransaction()) {
             throw new IllegalStateException(
                   "Thread is not in a binder transcation");
         }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index ab2c8c0..1468d9f 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.AppOpsManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -3094,14 +3095,24 @@
      * Parcelables.
      */
     @Nullable
-    public final ArrayList readArrayList(@Nullable ClassLoader loader) {
-        int N = readInt();
-        if (N < 0) {
-            return null;
-        }
-        ArrayList l = new ArrayList(N);
-        readListInternal(l, N, loader, /* clazz */ null);
-        return l;
+    public ArrayList readArrayList(@Nullable ClassLoader loader) {
+        return readArrayListInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readArrayList(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    public <T> ArrayList<T> readArrayList(@Nullable ClassLoader loader,
+            @NonNull Class<? extends T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readArrayListInternal(loader, clazz);
     }
 
     /**
@@ -3111,14 +3122,23 @@
      * Parcelables.
      */
     @Nullable
-    public final Object[] readArray(@Nullable ClassLoader loader) {
-        int N = readInt();
-        if (N < 0) {
-            return null;
-        }
-        Object[] l = new Object[N];
-        readArrayInternal(l, N, loader);
-        return l;
+    public Object[] readArray(@Nullable ClassLoader loader) {
+        return readArrayInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readArray(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @Nullable
+    public <T> T[] readArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readArrayInternal(loader, clazz);
     }
 
     /**
@@ -3128,14 +3148,23 @@
      * Parcelables.
      */
     @Nullable
-    public final <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
-        int N = readInt();
-        if (N < 0) {
-            return null;
-        }
-        SparseArray sa = new SparseArray(N);
-        readSparseArrayInternal(sa, N, loader);
-        return sa;
+    public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader) {
+        return readSparseArrayInternal(loader, /* clazz */ null);
+    }
+
+    /**
+     * Same as {@link #readSparseArray(ClassLoader)} but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @Nullable
+    public <T> SparseArray<T> readSparseArray(@Nullable ClassLoader loader,
+            @NonNull Class<? extends T> clazz) {
+        Objects.requireNonNull(clazz);
+        return readSparseArrayInternal(loader, clazz);
     }
 
     /**
@@ -3851,7 +3880,7 @@
                     "Parcel " + this + ": Unmarshalling unknown type code " + type
                             + " at offset " + off);
         }
-        if (clazz != null && !clazz.isInstance(object)) {
+        if (object != null && clazz != null && !clazz.isInstance(object)) {
             throw new BadParcelableException("Unparcelled object " + object
                     + " is not an instance of required class " + clazz.getName()
                     + " provided in the parameter");
@@ -3910,7 +3939,6 @@
     }
 
     /**
-     *
      * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
      */
     @SuppressWarnings("unchecked")
@@ -3969,7 +3997,7 @@
      * as the required type.
      *
      * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
-     * is not an instance of that class or any of its children class or there there was an error
+     * is not an instance of that class or any of its children classes or there there was an error
      * trying to read the {@link Parcelable.Creator}.
      */
     @Nullable
@@ -4092,17 +4120,25 @@
         return p;
     }
 
-    /** @hide */
+    /**
+     * Same as {@link #readParcelableArray(ClassLoader)}  but accepts {@code clazz} parameter as
+     * the type required for each item.
+     *
+     * @throws BadParcelableException Throws BadParcelableException if the item to be deserialized
+     * is not an instance of that class or any of its children classes or there was an error
+     * trying to instantiate an element.
+     */
+    @SuppressLint({"ArrayReturn", "NullableCollection"})
+    @SuppressWarnings("unchecked")
     @Nullable
-    public final <T extends Parcelable> T[] readParcelableArray(@Nullable ClassLoader loader,
-            @NonNull Class<T> clazz) {
-        int N = readInt();
-        if (N < 0) {
+    public <T> T[] readParcelableArray(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+        int n = readInt();
+        if (n < 0) {
             return null;
         }
-        T[] p = (T[]) Array.newInstance(clazz, N);
-        for (int i = 0; i < N; i++) {
-            p[i] = readParcelable(loader);
+        T[] p = (T[]) Array.newInstance(clazz, n);
+        for (int i = 0; i < n; i++) {
+            p[i] = readParcelableInternal(loader, clazz);
         }
         return p;
     }
@@ -4320,9 +4356,12 @@
         return result;
     }
 
-    private void readListInternal(@NonNull List outVal, int n,
-            @Nullable ClassLoader loader) {
-        readListInternal(outVal, n, loader, null);
+    /**
+     * The method is replaced by {@link #readListInternal(List, int, ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
+    private void readListInternal(@NonNull List outVal, int n, @Nullable ClassLoader loader) {
+        readListInternal(outVal, n, loader,  /* clazz */ null);
     }
 
     /**
@@ -4338,26 +4377,88 @@
         }
     }
 
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @SuppressLint({"ConcreteCollection", "NullableCollection"})
+    @Nullable
+    private <T> ArrayList<T> readArrayListInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? extends T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        ArrayList<T> l = new ArrayList<>(n);
+        readListInternal(l, n, loader, clazz);
+        return l;
+    }
+
+    /**
+     * The method is replaced by {@link #readArrayInternal(ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
     private void readArrayInternal(@NonNull Object[] outVal, int N,
             @Nullable ClassLoader loader) {
         for (int i = 0; i < N; i++) {
-            Object value = readValue(loader);
-            //Log.d(TAG, "Unmarshalling value=" + value);
+            Object value = readValue(loader, /* clazz */ null);
             outVal[i] = value;
         }
     }
 
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private <T> T[] readArrayInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        T[] outVal = (T[]) ((clazz == null) ? new Object[n] : Array.newInstance(clazz, n));
+
+        for (int i = 0; i < n; i++) {
+            T value = readValue(loader, clazz);
+            outVal[i] = value;
+        }
+        return outVal;
+    }
+
+    /**
+     * The method is replaced by {@link #readSparseArray(ClassLoader, Class)}, however
+     * we are keeping this unused method here to allow unsupported app usages.
+     */
     private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
             @Nullable ClassLoader loader) {
         while (N > 0) {
             int key = readInt();
             Object value = readValue(loader);
-            //Log.i(TAG, "Unmarshalling key=" + key + " value=" + value);
             outVal.append(key, value);
             N--;
         }
     }
 
+    /**
+     * @param clazz The type of the object expected or {@code null} for performing no checks.
+     */
+    @Nullable
+    private <T> SparseArray<T> readSparseArrayInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? extends T> clazz) {
+        int n = readInt();
+        if (n < 0) {
+            return null;
+        }
+        SparseArray<T> outVal = new SparseArray<>(n);
+
+        while (n > 0) {
+            int key = readInt();
+            T value = readValue(loader, clazz);
+            outVal.append(key, value);
+            n--;
+        }
+        return outVal;
+    }
+
 
     private void readSparseBooleanArrayInternal(@NonNull SparseBooleanArray outVal, int N) {
         while (N > 0) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 0f0f123..eda3699 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -37,6 +37,7 @@
 import android.content.ContextWrapper;
 import android.content.CursorEntityIterator;
 import android.content.Entity;
+import android.content.Entity.NamedContentValues;
 import android.content.EntityIterator;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -55,6 +56,7 @@
 import android.telecom.PhoneAccountHandle;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.Pair;
 import android.view.View;
 
@@ -64,7 +66,9 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -5135,6 +5139,8 @@
      */
     public final static class RawContactsEntity
             implements BaseColumns, DataColumns, RawContactsColumns {
+        private static final String TAG = "ContactsContract.RawContactsEntity";
+
         /**
          * This utility class cannot be instantiated
          */
@@ -5187,6 +5193,73 @@
          * <P>Type: INTEGER</P>
          */
         public static final String DATA_ID = "data_id";
+
+        /**
+         * Query raw contacts entity by a contact ID, which can potentially be a corp profile
+         * contact ID
+         *
+         * @param context A context to get the ContentResolver from
+         * @param contactId Contact ID, which can potentialy be a corp profile contact ID.
+         *
+         * @return A map from a mimetype to a List of the entity content values.
+         * {@hide}
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+        public static @NonNull Map<String, List<ContentValues>> queryRawContactEntity(
+                @NonNull ContentResolver contentResolver, long contactId) {
+            Uri uri = RawContactsEntity.CONTENT_URI;
+            long realContactId = contactId;
+
+            if (Contacts.isEnterpriseContactId(contactId)) {
+                uri = RawContactsEntity.CORP_CONTENT_URI;
+                realContactId = contactId - Contacts.ENTERPRISE_CONTACT_ID_BASE;
+            }
+            final Map<String, List<ContentValues>> contentValuesListMap =
+                    new HashMap<String, List<ContentValues>>();
+            // The resolver may return the entity iterator with no data. It is possible.
+            // e.g. If all the data in the contact of the given contact id are not exportable ones,
+            //      they are hidden from the view of this method, though contact id itself exists.
+            EntityIterator entityIterator = null;
+            try {
+                final String selection = Data.CONTACT_ID + "=?";
+                final String[] selectionArgs = new String[] {String.valueOf(realContactId)};
+
+                entityIterator = RawContacts.newEntityIterator(contentResolver.query(
+                            uri, null, selection, selectionArgs, null));
+
+                if (entityIterator == null) {
+                    Log.e(TAG, "EntityIterator is null");
+                    return contentValuesListMap;
+                }
+
+                if (!entityIterator.hasNext()) {
+                    Log.w(TAG, "Data does not exist. contactId: " + realContactId);
+                    return contentValuesListMap;
+                }
+
+                while (entityIterator.hasNext()) {
+                    Entity entity = entityIterator.next();
+                    for (NamedContentValues namedContentValues : entity.getSubValues()) {
+                        ContentValues contentValues = namedContentValues.values;
+                        String key = contentValues.getAsString(Data.MIMETYPE);
+                        if (key != null) {
+                            List<ContentValues> contentValuesList = contentValuesListMap.get(key);
+                            if (contentValuesList == null) {
+                                contentValuesList = new ArrayList<ContentValues>();
+                                contentValuesListMap.put(key, contentValuesList);
+                            }
+                            contentValuesList.add(contentValues);
+                        }
+                    }
+                }
+            } finally {
+                if (entityIterator != null) {
+                    entityIterator.close();
+                }
+            }
+            return contentValuesListMap;
+        }
     }
 
     /**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index f5f5eb8..25e3a4f 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -298,6 +298,14 @@
     public static final String NAMESPACE_NETD_NATIVE = "netd_native";
 
     /**
+     * Namespace for all Android NNAPI related features.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_NNAPI_NATIVE = "nnapi_native";
+
+    /**
      * Namespace for features related to the Package Manager Service.
      *
      * @hide
diff --git a/core/java/android/security/attestationverification/OWNERS b/core/java/android/security/attestationverification/OWNERS
new file mode 100644
index 0000000..80a1f44
--- /dev/null
+++ b/core/java/android/security/attestationverification/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 1111194
+
+dlm@google.com
+dkrahn@google.com
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index dd4de0a..3028a6d 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -691,6 +691,11 @@
          * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
         void onMessageWaitingIndicatorChanged(boolean mwi);
@@ -710,6 +715,11 @@
          * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
         void onCallForwardingIndicatorChanged(boolean cfi);
@@ -868,6 +878,10 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
          * @param callState {@link PreciseCallState}
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -910,6 +924,10 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
          * @param imsReasonInfo {@link ImsReasonInfo} contains details on why IMS call failed.
          */
         @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -932,9 +950,9 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
-         * <p>Requires permission {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}
-         * or the calling app has carrier privileges
-         * (see {@link TelephonyManager#hasCarrierPrivileges}).
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
          *
          * @param dataConnectionState {@link PreciseDataConnectionState}
          */
@@ -1063,6 +1081,10 @@
          * given subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
          * @param emergencyNumberList Map associating all active subscriptions on the device with
          *                            the list of emergency numbers originating from that
          *                            subscription.
@@ -1157,6 +1179,11 @@
          *              For example, it could be the current active opportunistic subscription
          *              in use, or the subscription user selected as default data subscription in
          *              DSDS mode.
+         *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PHONE_STATE}.
+         *
          */
         @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
         void onActiveDataSubscriptionIdChanged(int subId);
@@ -1225,6 +1252,11 @@
          * <p>Because registration failures are ephemeral, this callback is not sticky.
          * Registrants will not receive the most recent past value when registering.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} and
+         * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+         *
          * @param cellIdentity        the CellIdentity, which must include the globally unique
          *                            identifier
          *                            for the cell (for example, all components of the CGI or ECGI).
@@ -1308,6 +1340,10 @@
          * subscription ID. Otherwise, this callback applies to
          * {@link SubscriptionManager#getDefaultSubscriptionId()}.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
          * @param callAttributes the call attributes
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -1324,6 +1360,11 @@
          * <p>Barring info is provided for all services applicable to the current camped/registered
          * cell, for the registered PLMN and current access class/access category.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE} and
+         * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+         *
          * @param barringInfo for all services on the current cell.
          * @see android.telephony.BarringInfo
          */
@@ -1341,6 +1382,10 @@
         /**
          * Callback invoked when the current physical channel configuration has changed
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
          * @param configs List of the current {@link PhysicalChannelConfig}s
          */
         @RequiresPermission(Manifest.permission.READ_PRECISE_PHONE_STATE)
@@ -1357,6 +1402,10 @@
         /**
          * Callback invoked when the data enabled changes.
          *
+         * The calling app should have carrier privileges
+         * (see {@link TelephonyManager#hasCarrierPrivileges}) if it does not have the
+         * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE}.
+         *
          * @param enabled {@code true} if data is enabled, otherwise disabled.
          * @param reason  Reason for data enabled/disabled.
          *                See {@link TelephonyManager.DataEnabledReason}.
diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java
index 3f3c9bd..7c39118 100644
--- a/core/java/com/android/internal/content/om/OverlayConfig.java
+++ b/core/java/com/android/internal/content/om/OverlayConfig.java
@@ -25,17 +25,22 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.apex.ApexInfo;
+import com.android.apex.XmlParser;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.om.OverlayConfigParser.OverlayPartition;
 import com.android.internal.content.om.OverlayConfigParser.ParsedConfiguration;
 import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.TriConsumer;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
-import java.util.function.BiConsumer;
+import java.util.List;
 import java.util.function.Supplier;
 
 /**
@@ -73,7 +78,7 @@
     public interface PackageProvider {
 
         /** Performs the given action for each package. */
-        void forEachPackage(BiConsumer<ParsingPackageRead, Boolean> p);
+        void forEachPackage(TriConsumer<ParsingPackageRead, Boolean, File> p);
     }
 
     private static final Comparator<ParsedConfiguration> sStaticOverlayComparator = (c1, c2) -> {
@@ -115,6 +120,8 @@
                             p)));
         }
 
+        ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions);
+
         boolean foundConfigFile = false;
         ArrayList<ParsedOverlayInfo> packageManagerOverlayInfos = null;
 
@@ -123,7 +130,9 @@
             final OverlayPartition partition = partitions.get(i);
             final OverlayScanner scanner = (scannerFactory == null) ? null : scannerFactory.get();
             final ArrayList<ParsedConfiguration> partitionOverlays =
-                    OverlayConfigParser.getConfigurations(partition, scanner);
+                    OverlayConfigParser.getConfigurations(partition, scanner,
+                            activeApexesPerPartition.getOrDefault(partition.type,
+                                    Collections.emptyList()));
             if (partitionOverlays != null) {
                 foundConfigFile = true;
                 overlays.addAll(partitionOverlays);
@@ -145,7 +154,8 @@
                 // Filter out overlays not present in the partition.
                 partitionOverlayInfos = new ArrayList<>(packageManagerOverlayInfos);
                 for (int j = partitionOverlayInfos.size() - 1; j >= 0; j--) {
-                    if (!partition.containsFile(partitionOverlayInfos.get(j).path)) {
+                    if (!partition.containsFile(partitionOverlayInfos.get(j)
+                            .getOriginalPartitionPath())) {
                         partitionOverlayInfos.remove(j);
                     }
                 }
@@ -292,16 +302,49 @@
     private static ArrayList<ParsedOverlayInfo> getOverlayPackageInfos(
             @NonNull PackageProvider packageManager) {
         final ArrayList<ParsedOverlayInfo> overlays = new ArrayList<>();
-        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem) -> {
+        packageManager.forEachPackage((ParsingPackageRead p, Boolean isSystem,
+                @Nullable File preInstalledApexPath) -> {
             if (p.getOverlayTarget() != null && isSystem) {
                 overlays.add(new ParsedOverlayInfo(p.getPackageName(), p.getOverlayTarget(),
                         p.getTargetSdkVersion(), p.isOverlayIsStatic(), p.getOverlayPriority(),
-                        new File(p.getBaseApkPath())));
+                        new File(p.getBaseApkPath()), preInstalledApexPath));
             }
         });
         return overlays;
     }
 
+    /** Returns a map of PartitionType to List of active APEX module names. */
+    @NonNull
+    private static ArrayMap<Integer, List<String>> getActiveApexes(
+            @NonNull List<OverlayPartition> partitions) {
+        // An Overlay in an APEX, which is an update of an APEX in a given partition,
+        // is considered as belonging to that partition.
+        ArrayMap<Integer, List<String>> result = new ArrayMap<>();
+        for (OverlayPartition partition : partitions) {
+            result.put(partition.type, new ArrayList<String>());
+        }
+        // Read from apex-info-list because ApexManager is not accessible to zygote.
+        File apexInfoList = new File("/apex/apex-info-list.xml");
+        if (apexInfoList.exists() && apexInfoList.canRead()) {
+            try (FileInputStream stream = new FileInputStream(apexInfoList)) {
+                List<ApexInfo> apexInfos = XmlParser.readApexInfoList(stream).getApexInfo();
+                for (ApexInfo info : apexInfos) {
+                    if (info.getIsActive()) {
+                        for (OverlayPartition partition : partitions) {
+                            if (partition.containsPath(info.getPreinstalledModulePath())) {
+                                result.get(partition.type).add(info.getModuleName());
+                                break;
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Error reading apex-info-list: " + e);
+            }
+        }
+        return result;
+    }
+
     /** Represents a single call to idmap create-multiple. */
     @VisibleForTesting
     public static class IdmapInvocation {
diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java
index a86e595..d48c2e7 100644
--- a/core/java/com/android/internal/content/om/OverlayConfigParser.java
+++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java
@@ -40,6 +40,7 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Responsible for parsing configurations of Runtime Resource Overlays that control mutability,
@@ -192,13 +193,19 @@
      */
     @Nullable
     static ArrayList<ParsedConfiguration> getConfigurations(
-            @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner) {
-        if (partition.getOverlayFolder() == null) {
-            return null;
+            @NonNull OverlayPartition partition, @Nullable OverlayScanner scanner,
+            @NonNull List<String> activeApexes) {
+        if (scanner != null) {
+            if (partition.getOverlayFolder() != null) {
+                scanner.scanDir(partition.getOverlayFolder());
+            }
+            for (String apex : activeApexes) {
+                scanner.scanDir(new File("/apex/" + apex + "/overlay/"));
+            }
         }
 
-        if (scanner != null) {
-            scanner.scanDir(partition.getOverlayFolder());
+        if (partition.getOverlayFolder() == null) {
+            return null;
         }
 
         final File configFile = new File(partition.getOverlayFolder(), CONFIG_DEFAULT_FILENAME);
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index 6b5cb8d..138d1ce 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -47,23 +47,38 @@
         public final boolean isStatic;
         public final int priority;
         public final File path;
+        @Nullable public final File preInstalledApexPath;
 
         public ParsedOverlayInfo(String packageName, String targetPackageName,
-                int targetSdkVersion, boolean isStatic, int priority, File path) {
+                int targetSdkVersion, boolean isStatic, int priority, File path,
+                @Nullable File preInstalledApexPath) {
             this.packageName = packageName;
             this.targetPackageName = targetPackageName;
             this.targetSdkVersion = targetSdkVersion;
             this.isStatic = isStatic;
             this.priority = priority;
             this.path = path;
+            this.preInstalledApexPath = preInstalledApexPath;
         }
 
         @Override
         public String toString() {
             return getClass().getSimpleName() + String.format("{packageName=%s"
                             + ", targetPackageName=%s, targetSdkVersion=%s, isStatic=%s"
-                            + ", priority=%s, path=%s}",
-                    packageName, targetPackageName, targetSdkVersion, isStatic, priority, path);
+                            + ", priority=%s, path=%s, preInstalledApexPath=%s}",
+                    packageName, targetPackageName, targetSdkVersion, isStatic,
+                    priority, path, preInstalledApexPath);
+        }
+
+        /**
+         * Retrieves the path of the overlay in its original installation partition.
+         *
+         * An Overlay in an APEX, which is an update of an APEX in a given partition,
+         * is considered as belonging to that partition.
+         */
+        @NonNull
+        public File getOriginalPartitionPath() {
+            return preInstalledApexPath != null ? preInstalledApexPath : path;
         }
     }
 
@@ -138,6 +153,6 @@
         return apkLite.getTargetPackageName() == null ? null :
                 new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
                         apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
-                        apkLite.getOverlayPriority(), new File(apkLite.getPath()));
+                        apkLite.getOverlayPriority(), new File(apkLite.getPath()), null);
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8c63f38..630851d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12143,7 +12143,7 @@
             rxTimeMs = info.getControllerRxTimeMillis();
             txTimeMs = info.getControllerTxTimeMillis();
             energy = info.getControllerEnergyUsed();
-            if (info.getUidTraffic() != null) {
+            if (!info.getUidTraffic().isEmpty()) {
                 for (UidTraffic traffic : info.getUidTraffic()) {
                     uidRxBytes.put(traffic.getUid(), traffic.getRxBytes());
                     uidTxBytes.put(traffic.getUid(), traffic.getTxBytes());
@@ -12294,10 +12294,10 @@
         long totalTxBytes = 0;
         long totalRxBytes = 0;
 
-        final UidTraffic[] uidTraffic = info.getUidTraffic();
-        final int numUids = uidTraffic != null ? uidTraffic.length : 0;
+        final List<UidTraffic> uidTraffic = info.getUidTraffic();
+        final int numUids = uidTraffic.size();
         for (int i = 0; i < numUids; i++) {
-            final UidTraffic traffic = uidTraffic[i];
+            final UidTraffic traffic = uidTraffic.get(i);
             final long rxBytes = traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(
                     traffic.getUid());
             final long txBytes = traffic.getTxBytes() - mLastBluetoothActivityInfo.uidTxBytes.get(
@@ -12320,7 +12320,7 @@
         if ((totalTxBytes != 0 || totalRxBytes != 0) && (leftOverRxTimeMs != 0
                 || leftOverTxTimeMs != 0)) {
             for (int i = 0; i < numUids; i++) {
-                final UidTraffic traffic = uidTraffic[i];
+                final UidTraffic traffic = uidTraffic.get(i);
                 final int uid = traffic.getUid();
                 final long rxBytes =
                         traffic.getRxBytes() - mLastBluetoothActivityInfo.uidRxBytes.get(uid);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 0d33807..be9aaaf 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -641,7 +641,7 @@
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
     if (parcel != NULL) {
         bool result;
-        status_t err = parcel->hasFileDescriptorsInRange(offset, length, result);
+        status_t err = parcel->hasFileDescriptorsInRange(offset, length, &result);
         if (err != NO_ERROR) {
             signalExceptionForError(env, clazz, err);
             return JNI_FALSE;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 793b4eb..61b91dd 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -959,8 +959,7 @@
     return IPCThreadState::self()->getCallingUid();
 }
 
-static jboolean android_os_Binder_isHandlingTransaction()
-{
+static jboolean android_os_Binder_isDirectlyHandlingTransaction() {
     return getCurrentServingCall() == BinderCallType::BINDER;
 }
 
@@ -1056,6 +1055,7 @@
 
 // ----------------------------------------------------------------------------
 
+// clang-format off
 static const JNINativeMethod gBinderMethods[] = {
      /* name, signature, funcPtr */
     // @CriticalNative
@@ -1063,7 +1063,7 @@
     // @CriticalNative
     { "getCallingUid", "()I", (void*)android_os_Binder_getCallingUid },
     // @CriticalNative
-    { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction },
+    { "isDirectlyHandlingTransaction", "()Z", (void*)android_os_Binder_isDirectlyHandlingTransaction },
     // @CriticalNative
     { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity },
     // @CriticalNative
@@ -1088,6 +1088,7 @@
     { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
     { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
 };
+// clang-format on
 
 const char* const kBinderPathName = "android/os/Binder";
 
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 6f5cc53..40f6e4f 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -138,6 +138,14 @@
         return true;
     }
 
+    // Allow Runtime Resource Overlays inside APEXes.
+    static const char* kOverlayPathSuffix = "/overlay";
+    if (android::base::StartsWith(path, kApexPrefix) &&
+        android::base::EndsWith(android::base::Dirname(path), kOverlayPathSuffix) &&
+        android::base::EndsWith(path, kApkSuffix) && path.find("/../") == std::string::npos) {
+        return true;
+    }
+
     static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
     static const char* kOverlayIdmapSuffix = ".apk@idmap";
     if (android::base::StartsWith(path, kOverlayIdmapPrefix) &&
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index bd89578..1418dbe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1976,6 +1976,14 @@
         android:label="@string/permlab_uwb_ranging"
         android:protectionLevel="dangerous" />
 
+    <!-- Required to be able to advertise and connect to nearby devices via Wi-Fi.
+         <p>Protection level: dangerous -->
+    <permission android:name="android.permission.NEARBY_WIFI_DEVICES"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:description="@string/permdesc_nearby_wifi_devices"
+                android:label="@string/permlab_nearby_wifi_devices"
+                android:protectionLevel="dangerous" />
+
     <!-- @SystemApi @TestApi Allows an application to suspend other apps, which will prevent the
          user from using them until they are unsuspended.
          @hide
diff --git a/core/res/OWNERS b/core/res/OWNERS
index 684202b..165dcad 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -28,3 +28,6 @@
 
 # Multiuser
 per-file res/xml/config_user_types.xml = file:/MULTIUSER_OWNERS
+
+# Car
+per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS
\ No newline at end of file
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a99a220..a350d14 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1488,6 +1488,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
     <string name="permdesc_uwb_ranging">Allow the app to determine relative position between nearby Ultra-Wideband devices</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=50]-->
+    <string name="permlab_nearby_wifi_devices">interact with nearby Wi\u2011Fi devices</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=120]-->
+    <string name="permdesc_nearby_wifi_devices">Allows the app to advertise, connect, and determine the relative position of nearby Wi\u2011Fi devices</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_preferredPaymentInfo">Preferred NFC Payment Service Information</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f1c66c5..4c20ae1 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -105,6 +105,9 @@
          http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
     <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
 
+    <!-- Egypt: 4 digits, known codes listed -->
+    <shortcode country="eg" pattern="\\d{4}" free="1499" />
+
     <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
          http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
     <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}|22791|222145|22189" />
@@ -196,7 +199,7 @@
     <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
 
     <!-- Nigeria -->
-    <shortcode country="ng" pattern="\\d{1,5}" free="2441|55019" />
+    <shortcode country="ng" pattern="\\d{1,5}" free="2441|55020" />
 
     <!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
     <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
diff --git a/core/tests/companiontests/Android.bp b/core/tests/companiontests/Android.bp
new file mode 100644
index 0000000..d31b8f4
--- /dev/null
+++ b/core/tests/companiontests/Android.bp
@@ -0,0 +1,21 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "CompanionTests",
+    // Include all test java files.
+    srcs: ["src/**/*.java"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: ["junit"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/core/tests/companiontests/AndroidManifest.xml b/core/tests/companiontests/AndroidManifest.xml
new file mode 100644
index 0000000..f436d97
--- /dev/null
+++ b/core/tests/companiontests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.companion.tests"
+          android:sharedUserId="android.uid.system" >
+
+    <application >
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="android.companion.CompanionTestRunner"
+            android:targetPackage="com.android.companion.tests"
+            android:label="Companion Tests" />
+
+</manifest>
diff --git a/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java b/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java
new file mode 100644
index 0000000..1ddbbd8
--- /dev/null
+++ b/core/tests/companiontests/src/android/companion/BluetoothDeviceFilterUtilsTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.os.ParcelUuid;
+import android.test.InstrumentationTestCase;
+
+public class BluetoothDeviceFilterUtilsTest extends InstrumentationTestCase {
+    private static final String TAG = "BluetoothDeviceFilterUtilsTest";
+
+    private final ParcelUuid mServiceUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mNonMatchingDeviceUuid =
+            ParcelUuid.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMatchingDeviceUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMaskUuid =
+            ParcelUuid.fromString("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+    private final ParcelUuid mMatchingMaskUuid =
+            ParcelUuid.fromString("F0FFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testUuidsMaskedEquals() {
+        assertFalse(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mNonMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMaskUuid.getUuid()));
+
+        assertTrue(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMaskUuid.getUuid()));
+
+        assertTrue(BluetoothDeviceFilterUtils.uuidsMaskedEquals(
+                mNonMatchingDeviceUuid.getUuid(),
+                mServiceUuid.getUuid(),
+                mMatchingMaskUuid.getUuid()));
+    }
+}
diff --git a/core/tests/companiontests/src/android/companion/CompanionTestRunner.java b/core/tests/companiontests/src/android/companion/CompanionTestRunner.java
new file mode 100644
index 0000000..caa2c68
--- /dev/null
+++ b/core/tests/companiontests/src/android/companion/CompanionTestRunner.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import junit.framework.TestSuite;
+
+
+/**
+ * Instrumentation test runner for Companion tests.
+ */
+public class CompanionTestRunner extends InstrumentationTestRunner {
+    private static final String TAG = "CompanionTestRunner";
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        suite.addTestSuite(BluetoothDeviceFilterUtilsTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return CompanionTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle arguments) {
+        super.onCreate(arguments);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index bf9978c..b400b9b 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -22,7 +22,10 @@
 
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
+import android.net.sntp.Duration64;
+import android.net.sntp.Timestamp64;
 import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -38,7 +41,13 @@
 import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.SocketException;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.Arrays;
+import java.util.Random;
+import java.util.function.Supplier;
 
 @RunWith(AndroidJUnit4.class)
 public class SntpClientTest {
@@ -54,41 +63,232 @@
     //
     // Server, Leap indicator:  (0), Stratum 2 (secondary reference), poll 6 (64s), precision -20
     // Root Delay: 0.005447, Root dispersion: 0.002716, Reference-ID: 221.253.71.41
-    //   Reference Timestamp:  3653932102.507969856 (2015/10/15 14:08:22)
-    //   Originator Timestamp: 3653932113.576327741 (2015/10/15 14:08:33)
-    //   Receive Timestamp:    3653932113.581012725 (2015/10/15 14:08:33)
-    //   Transmit Timestamp:   3653932113.581012725 (2015/10/15 14:08:33)
+    //   Reference Timestamp:
+    //     d9ca9446.820a5000 / ERA0: 2015-10-15 21:08:22 UTC / ERA1: 2151-11-22 03:36:38 UTC
+    //   Originator Timestamp:
+    //     d9ca9451.938a3771 / ERA0: 2015-10-15 21:08:33 UTC / ERA1: 2151-11-22 03:36:49 UTC
+    //   Receive Timestamp:
+    //     d9ca9451.94bd3fff / ERA0: 2015-10-15 21:08:33 UTC / ERA1: 2151-11-22 03:36:49 UTC
+    //   Transmit Timestamp:
+    //     d9ca9451.94bd4001 / ERA0: 2015-10-15 21:08:33 UTC / ERA1: 2151-11-22 03:36:49 UTC
+    //
     //     Originator - Receive Timestamp:  +0.004684958
     //     Originator - Transmit Timestamp: +0.004684958
-    private static final String WORKING_VERSION4 =
-            "240206ec" +
-            "00000165" +
-            "000000b2" +
-            "ddfd4729" +
-            "d9ca9446820a5000" +
-            "d9ca9451938a3771" +
-            "d9ca945194bd3fff" +
-            "d9ca945194bd4001";
+    private static final String LATE_ERA_RESPONSE =
+            "240206ec"
+                    + "00000165"
+                    + "000000b2"
+                    + "ddfd4729"
+                    + "d9ca9446820a5000"
+                    + "d9ca9451938a3771"
+                    + "d9ca945194bd3fff"
+                    + "d9ca945194bd4001";
+
+    /** This is the actual UTC time in the server if it is in ERA0 */
+    private static final Instant LATE_ERA0_SERVER_TIME =
+            calculateIdealServerTime("d9ca9451.94bd3fff", "d9ca9451.94bd4001", 0);
+
+    /**
+     * This is the Unix epoch time matches the originate timestamp from {@link #LATE_ERA_RESPONSE}
+     * when interpreted as an ERA0 timestamp.
+     */
+    private static final Instant LATE_ERA0_REQUEST_TIME =
+            Timestamp64.fromString("d9ca9451.938a3771").toInstant(0);
+
+    // A tweaked version of the ERA0 response to represent an ERA 1 response.
+    //
+    // Server, Leap indicator:  (0), Stratum 2 (secondary reference), poll 6 (64s), precision -20
+    // Root Delay: 0.005447, Root dispersion: 0.002716, Reference-ID: 221.253.71.41
+    //   Reference Timestamp:
+    //     1db2d246.820a5000 / ERA0: 1915-10-16 21:08:22 UTC / ERA1: 2051-11-22 03:36:38 UTC
+    //   Originate Timestamp:
+    //     1db2d251.938a3771 / ERA0: 1915-10-16 21:08:33 UTC / ERA1: 2051-11-22 03:36:49 UTC
+    //   Receive Timestamp:
+    //     1db2d251.94bd3fff / ERA0: 1915-10-16 21:08:33 UTC / ERA1: 2051-11-22 03:36:49 UTC
+    //   Transmit Timestamp:
+    //     1db2d251.94bd4001 / ERA0: 1915-10-16 21:08:33 UTC / ERA1: 2051-11-22 03:36:49 UTC
+    //
+    //     Originate - Receive Timestamp:  +0.004684958
+    //     Originate - Transmit Timestamp: +0.004684958
+    private static final String EARLY_ERA_RESPONSE =
+            "240206ec"
+                    + "00000165"
+                    + "000000b2"
+                    + "ddfd4729"
+                    + "1db2d246820a5000"
+                    + "1db2d251938a3771"
+                    + "1db2d25194bd3fff"
+                    + "1db2d25194bd4001";
+
+    /** This is the actual UTC time in the server if it is in ERA0 */
+    private static final Instant EARLY_ERA1_SERVER_TIME =
+            calculateIdealServerTime("1db2d251.94bd3fff", "1db2d251.94bd4001", 1);
+
+    /**
+     * This is the Unix epoch time matches the originate timestamp from {@link #EARLY_ERA_RESPONSE}
+     * when interpreted as an ERA1 timestamp.
+     */
+    private static final Instant EARLY_ERA1_REQUEST_TIME =
+            Timestamp64.fromString("1db2d251.938a3771").toInstant(1);
 
     private SntpTestServer mServer;
     private SntpClient mClient;
     private Network mNetwork;
+    private Supplier<Instant> mSystemTimeSupplier;
+    private Random mRandom;
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() throws Exception {
+        mServer = new SntpTestServer();
+
         // A mock network has NETID_UNSET, which allows the test to run, with a loopback server,
         // even w/o external networking.
         mNetwork = mock(Network.class, CALLS_REAL_METHODS);
-        mServer = new SntpTestServer();
-        mClient = new SntpClient();
+        mRandom = mock(Random.class);
+
+        mSystemTimeSupplier = mock(Supplier.class);
+        // Returning zero means the "randomized" bottom bits of the clients transmit timestamp /
+        // server's originate timestamp will be zeros.
+        when(mRandom.nextInt()).thenReturn(0);
+        mClient = new SntpClient(mSystemTimeSupplier, mRandom);
     }
 
+    /** Tests when the client and server are in ERA0. b/199481251. */
     @Test
-    public void testBasicWorkingSntpClientQuery() throws Exception {
-        mServer.setServerReply(HexEncoding.decode(WORKING_VERSION4.toCharArray(), false));
+    public void testRequestTime_era0ClientEra0RServer() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        mServer.setServerReply(HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false));
         assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
         assertEquals(1, mServer.numRepliesSent());
+
+        checkRequestTimeCalcs(LATE_ERA0_REQUEST_TIME, LATE_ERA0_SERVER_TIME, mClient);
+    }
+
+    /** Tests when the client is behind the server and in the previous ERA. b/199481251. */
+    @Test
+    public void testRequestTime_era0ClientEra1Server() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        mServer.setServerReply(HexEncoding.decode(EARLY_ERA_RESPONSE.toCharArray(), false));
+        assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
+        assertEquals(1, mServer.numRequestsReceived());
+        assertEquals(1, mServer.numRepliesSent());
+
+        checkRequestTimeCalcs(LATE_ERA0_REQUEST_TIME, EARLY_ERA1_SERVER_TIME, mClient);
+
+    }
+
+    /** Tests when the client is ahead of the server and in the next ERA. b/199481251. */
+    @Test
+    public void testRequestTime_era1ClientEra0Server() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(EARLY_ERA1_REQUEST_TIME);
+
+        mServer.setServerReply(HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false));
+        assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
+        assertEquals(1, mServer.numRequestsReceived());
+        assertEquals(1, mServer.numRepliesSent());
+
+        checkRequestTimeCalcs(EARLY_ERA1_REQUEST_TIME, LATE_ERA0_SERVER_TIME, mClient);
+    }
+
+    /** Tests when the client and server are in ERA1. b/199481251. */
+    @Test
+    public void testRequestTime_era1ClientEra1Server() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(EARLY_ERA1_REQUEST_TIME);
+
+        mServer.setServerReply(HexEncoding.decode(EARLY_ERA_RESPONSE.toCharArray(), false));
+        assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
+        assertEquals(1, mServer.numRequestsReceived());
+        assertEquals(1, mServer.numRepliesSent());
+
+        checkRequestTimeCalcs(EARLY_ERA1_REQUEST_TIME, EARLY_ERA1_SERVER_TIME, mClient);
+    }
+
+    private static void checkRequestTimeCalcs(
+            Instant clientTime, Instant serverTime, SntpClient client) {
+        // The tests don't attempt to control the elapsed time tracking, which influences the
+        // round trip time (i.e. time spent in due to the network), but they control everything
+        // else, so assertions are allowed some slop and round trip time just has to be >= 0.
+        assertTrue("getRoundTripTime()=" + client.getRoundTripTime(),
+                client.getRoundTripTime() >= 0);
+
+        // Calculate the ideal offset if nothing took any time.
+        long expectedOffset = serverTime.toEpochMilli() - clientTime.toEpochMilli();
+        long allowedSlop = (client.getRoundTripTime() / 2) + 1; // +1 to allow for truncation loss.
+        assertNearlyEquals(expectedOffset, client.getClockOffset(), allowedSlop);
+        assertNearlyEquals(clientTime.toEpochMilli() + expectedOffset,
+                client.getNtpTime(), allowedSlop);
+    }
+
+    /**
+     * Unit tests for the low-level offset calculations. More targeted / easier to write than the
+     * end-to-end tests above that simulate the server. b/199481251.
+     */
+    @Test
+    public void testCalculateClockOffset() {
+        Instant era0Time1 = utcInstant(2021, 10, 5, 2, 2, 2, 2);
+        // Confirm what happens when the client and server are completely in sync.
+        checkCalculateClockOffset(era0Time1, era0Time1);
+
+        Instant era0Time2 = utcInstant(2021, 10, 6, 1, 1, 1, 1);
+        checkCalculateClockOffset(era0Time1, era0Time2);
+        checkCalculateClockOffset(era0Time2, era0Time1);
+
+        Instant era1Time1 = utcInstant(2061, 10, 5, 2, 2, 2, 2);
+        checkCalculateClockOffset(era1Time1, era1Time1);
+
+        Instant era1Time2 = utcInstant(2061, 10, 6, 1, 1, 1, 1);
+        checkCalculateClockOffset(era1Time1, era1Time2);
+        checkCalculateClockOffset(era1Time2, era1Time1);
+
+        // Cross-era calcs (requires they are still within 68 years of each other).
+        checkCalculateClockOffset(era0Time1, era1Time1);
+        checkCalculateClockOffset(era1Time1, era0Time1);
+    }
+
+    private void checkCalculateClockOffset(Instant clientTime, Instant serverTime) {
+        // The expected (ideal) offset is the difference between the client and server clocks. NTP
+        // assumes delays are symmetric, i.e. that the server time is between server
+        // receive/transmit time, client time is between request/response time, and send networking
+        // delay == receive networking delay.
+        Duration expectedOffset = Duration.between(clientTime, serverTime);
+
+        // Try simulating various round trip delays, including zero.
+        for (long totalElapsedTimeMillis : Arrays.asList(0, 20, 200, 2000, 20000)) {
+            // Simulate that a 10% of the elapsed time is due to time spent in the server, the rest
+            // is network / client processing time.
+            long simulatedServerElapsedTimeMillis = totalElapsedTimeMillis / 10;
+            long simulatedClientElapsedTimeMillis = totalElapsedTimeMillis;
+
+            // Create some symmetrical timestamps.
+            Timestamp64 clientRequestTimestamp = Timestamp64.fromInstant(
+                    clientTime.minusMillis(simulatedClientElapsedTimeMillis / 2));
+            Timestamp64 clientResponseTimestamp = Timestamp64.fromInstant(
+                    clientTime.plusMillis(simulatedClientElapsedTimeMillis / 2));
+            Timestamp64 serverReceiveTimestamp = Timestamp64.fromInstant(
+                    serverTime.minusMillis(simulatedServerElapsedTimeMillis / 2));
+            Timestamp64 serverTransmitTimestamp = Timestamp64.fromInstant(
+                    serverTime.plusMillis(simulatedServerElapsedTimeMillis / 2));
+
+            Duration actualOffset = SntpClient.calculateClockOffset(
+                    clientRequestTimestamp, serverReceiveTimestamp,
+                    serverTransmitTimestamp, clientResponseTimestamp);
+
+            // We allow up to 1ms variation because NTP types are lossy and the simulated elapsed
+            // time millis may not divide exactly.
+            int allowedSlopMillis = 1;
+            assertNearlyEquals(
+                    expectedOffset.toMillis(), actualOffset.toMillis(), allowedSlopMillis);
+        }
+    }
+
+    private static Instant utcInstant(
+            int year, int monthOfYear, int day, int hour, int minute, int second, int nanos) {
+        return LocalDateTime.of(year, monthOfYear, day, hour, minute, second, nanos)
+                .toInstant(ZoneOffset.UTC);
     }
 
     @Test
@@ -98,6 +298,8 @@
 
     @Test
     public void testTimeoutFailure() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
         mServer.clearServerReply();
         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
         assertEquals(1, mServer.numRequestsReceived());
@@ -106,7 +308,9 @@
 
     @Test
     public void testIgnoreLeapNoSync() throws Exception {
-        final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        final byte[] reply = HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false);
         reply[0] |= (byte) 0xc0;
         mServer.setServerReply(reply);
         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
@@ -116,7 +320,9 @@
 
     @Test
     public void testAcceptOnlyServerAndBroadcastModes() throws Exception {
-        final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        final byte[] reply = HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false);
         for (int i = 0; i <= 7; i++) {
             final String logMsg = "mode: " + i;
             reply[0] &= (byte) 0xf8;
@@ -140,10 +346,12 @@
 
     @Test
     public void testAcceptableStrataOnly() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
         final int STRATUM_MIN = 1;
         final int STRATUM_MAX = 15;
 
-        final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+        final byte[] reply = HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false);
         for (int i = 0; i < 256; i++) {
             final String logMsg = "stratum: " + i;
             reply[1] = (byte) i;
@@ -162,7 +370,9 @@
 
     @Test
     public void testZeroTransmitTime() throws Exception {
-        final byte[] reply = HexEncoding.decode(WORKING_VERSION4.toCharArray(), false);
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        final byte[] reply = HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false);
         Arrays.fill(reply, TRANSMIT_TIME_OFFSET, TRANSMIT_TIME_OFFSET + 8, (byte) 0x00);
         mServer.setServerReply(reply);
         assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
@@ -170,6 +380,19 @@
         assertEquals(1, mServer.numRepliesSent());
     }
 
+    @Test
+    public void testNonMatchingOriginateTime() throws Exception {
+        when(mSystemTimeSupplier.get()).thenReturn(LATE_ERA0_REQUEST_TIME);
+
+        final byte[] reply = HexEncoding.decode(LATE_ERA_RESPONSE.toCharArray(), false);
+        mServer.setServerReply(reply);
+        mServer.setGenerateValidOriginateTimestamp(false);
+
+        assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500, mNetwork));
+        assertEquals(1, mServer.numRequestsReceived());
+        assertEquals(1, mServer.numRepliesSent());
+    }
+
 
     private static class SntpTestServer {
         private final Object mLock = new Object();
@@ -177,6 +400,7 @@
         private final InetAddress mAddress;
         private final int mPort;
         private byte[] mReply;
+        private boolean mGenerateValidOriginateTimestamp = true;
         private int mRcvd;
         private int mSent;
         private Thread mListeningThread;
@@ -201,10 +425,16 @@
                         synchronized (mLock) {
                             mRcvd++;
                             if (mReply == null) { continue; }
-                            // Copy transmit timestamp into originate timestamp.
-                            // TODO: bounds checking.
-                            System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
-                                             mReply, ORIGINATE_TIME_OFFSET, 8);
+                            if (mGenerateValidOriginateTimestamp) {
+                                // Copy the transmit timestamp into originate timestamp: This is
+                                // validated by well-behaved clients.
+                                System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
+                                        mReply, ORIGINATE_TIME_OFFSET, 8);
+                            } else {
+                                // Fill it with junk instead.
+                                Arrays.fill(mReply, ORIGINATE_TIME_OFFSET,
+                                        ORIGINATE_TIME_OFFSET + 8, (byte) 0xFF);
+                            }
                             ntpMsg.setData(mReply);
                             ntpMsg.setLength(mReply.length);
                             try {
@@ -245,9 +475,38 @@
             }
         }
 
+        /**
+         * Controls the test server's behavior of copying the client's transmit timestamp into the
+         * response's originate timestamp (which is required of a real server).
+         */
+        public void setGenerateValidOriginateTimestamp(boolean enabled) {
+            synchronized (mLock) {
+                mGenerateValidOriginateTimestamp = enabled;
+            }
+        }
+
         public InetAddress getAddress() { return mAddress; }
         public int getPort() { return mPort; }
         public int numRequestsReceived() { synchronized (mLock) { return mRcvd; } }
         public int numRepliesSent() { synchronized (mLock) { return mSent; } }
     }
+
+    /**
+     * Generates the "real" server time assuming it is exactly between the receive and transmit
+     * timestamp and in the NTP era specified.
+     */
+    private static Instant calculateIdealServerTime(String receiveTimestampString,
+            String transmitTimestampString, int era) {
+        Timestamp64 receiveTimestamp = Timestamp64.fromString(receiveTimestampString);
+        Timestamp64 transmitTimestamp = Timestamp64.fromString(transmitTimestampString);
+        Duration serverProcessingTime =
+                Duration64.between(receiveTimestamp, transmitTimestamp).toDuration();
+        return receiveTimestamp.toInstant(era)
+                .plusMillis(serverProcessingTime.dividedBy(2).toMillis());
+    }
+
+    private static void assertNearlyEquals(long expected, long actual, long allowedSlop) {
+        assertTrue("expected=" + expected + ", actual=" + actual + ", allowedSlop=" + allowedSlop,
+                actual >= expected - allowedSlop && actual <= expected + allowedSlop);
+    }
 }
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
new file mode 100644
index 0000000..60b69f6
--- /dev/null
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.sntp;
+
+import static android.net.sntp.Timestamp64.NANOS_PER_SECOND;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+@RunWith(AndroidJUnit4.class)
+public class Duration64Test {
+
+    @Test
+    public void testBetween_rangeChecks() {
+        long maxDuration64Seconds = Timestamp64.MAX_SECONDS_IN_ERA / 2;
+
+        Timestamp64 zeroNoFrac = Timestamp64.fromComponents(0, 0);
+        assertEquals(Duration64.ZERO, Duration64.between(zeroNoFrac, zeroNoFrac));
+
+        {
+            Timestamp64 ceilNoFrac = Timestamp64.fromComponents(maxDuration64Seconds, 0);
+            assertEquals(Duration64.ZERO, Duration64.between(ceilNoFrac, ceilNoFrac));
+
+            long expectedNanos = maxDuration64Seconds * NANOS_PER_SECOND;
+            assertEquals(Duration.ofNanos(expectedNanos),
+                    Duration64.between(zeroNoFrac, ceilNoFrac).toDuration());
+            assertEquals(Duration.ofNanos(-expectedNanos),
+                    Duration64.between(ceilNoFrac, zeroNoFrac).toDuration());
+        }
+
+        {
+            // This value is the largest fraction of a second representable. It is 1-(1/2^32)), and
+            // so numerically larger than 999_999_999 nanos.
+            int fractionBits = 0xFFFF_FFFF;
+            Timestamp64 ceilWithFrac = Timestamp64
+                    .fromComponents(maxDuration64Seconds, fractionBits);
+            assertEquals(Duration64.ZERO, Duration64.between(ceilWithFrac, ceilWithFrac));
+
+            long expectedNanos = maxDuration64Seconds * NANOS_PER_SECOND + 999_999_999;
+            assertEquals(
+                    Duration.ofNanos(expectedNanos),
+                    Duration64.between(zeroNoFrac, ceilWithFrac).toDuration());
+            // The -1 nanos demonstrates asymmetry due to the way Duration64 has different
+            // precision / range of sub-second fractions.
+            assertEquals(
+                    Duration.ofNanos(-expectedNanos - 1),
+                    Duration64.between(ceilWithFrac, zeroNoFrac).toDuration());
+        }
+    }
+
+    @Test
+    public void testBetween_smallSecondsOnly() {
+        long expectedNanos = 5L * NANOS_PER_SECOND;
+        assertEquals(Duration.ofNanos(expectedNanos),
+                Duration64.between(Timestamp64.fromComponents(5, 0),
+                        Timestamp64.fromComponents(10, 0))
+                        .toDuration());
+        assertEquals(Duration.ofNanos(-expectedNanos),
+                Duration64.between(Timestamp64.fromComponents(10, 0),
+                        Timestamp64.fromComponents(5, 0))
+                        .toDuration());
+    }
+
+    @Test
+    public void testBetween_smallSecondsAndFraction() {
+        // Choose a nanos values we know can be represented exactly with fixed point binary (1/2
+        // second, 1/4 second, etc.).
+        {
+            long expectedNanos = 5L * NANOS_PER_SECOND + 500_000_000L;
+            int fractionHalfSecond = 0x8000_0000;
+            assertEquals(Duration.ofNanos(expectedNanos),
+                    Duration64.between(
+                            Timestamp64.fromComponents(5, 0),
+                            Timestamp64.fromComponents(10, fractionHalfSecond)).toDuration());
+            assertEquals(Duration.ofNanos(-expectedNanos),
+                    Duration64.between(
+                            Timestamp64.fromComponents(10, fractionHalfSecond),
+                            Timestamp64.fromComponents(5, 0)).toDuration());
+        }
+
+        {
+            long expectedNanos = 5L * NANOS_PER_SECOND + 250_000_000L;
+            int fractionHalfSecond = 0x8000_0000;
+            int fractionQuarterSecond = 0x4000_0000;
+
+            assertEquals(Duration.ofNanos(expectedNanos),
+                    Duration64.between(
+                            Timestamp64.fromComponents(5, fractionQuarterSecond),
+                            Timestamp64.fromComponents(10, fractionHalfSecond)).toDuration());
+            assertEquals(Duration.ofNanos(-expectedNanos),
+                    Duration64.between(
+                            Timestamp64.fromComponents(10, fractionHalfSecond),
+                            Timestamp64.fromComponents(5, fractionQuarterSecond)).toDuration());
+        }
+
+    }
+
+    @Test
+    public void testBetween_sameEra0() {
+        int arbitraryEra0Year = 2021;
+        Instant one = utcInstant(arbitraryEra0Year, 1, 1, 0, 0, 0, 500);
+        assertNtpEraOfInstant(one, 0);
+
+        checkDuration64Behavior(one, one);
+
+        Instant two = utcInstant(arbitraryEra0Year + 1, 1, 1, 0, 0, 0, 250);
+        assertNtpEraOfInstant(two, 0);
+
+        checkDuration64Behavior(one, two);
+        checkDuration64Behavior(two, one);
+    }
+
+    @Test
+    public void testBetween_sameEra1() {
+        int arbitraryEra1Year = 2037;
+        Instant one = utcInstant(arbitraryEra1Year, 1, 1, 0, 0, 0, 500);
+        assertNtpEraOfInstant(one, 1);
+
+        checkDuration64Behavior(one, one);
+
+        Instant two = utcInstant(arbitraryEra1Year + 1, 1, 1, 0, 0, 0, 250);
+        assertNtpEraOfInstant(two, 1);
+
+        checkDuration64Behavior(one, two);
+        checkDuration64Behavior(two, one);
+    }
+
+    /**
+     * Tests that two timestamps can originate from times in different eras, and the works
+     * calculation still works providing the two times aren't more than 68 years apart (half of the
+     * 136 years representable using an unsigned 32-bit seconds representation).
+     */
+    @Test
+    public void testBetween_adjacentEras() {
+        int yearsSeparation = 68;
+
+        // This year just needs to be < 68 years before the end of NTP timestamp era 0.
+        int arbitraryYearInEra0 = 2021;
+
+        Instant one = utcInstant(arbitraryYearInEra0, 1, 1, 0, 0, 0, 500);
+        assertNtpEraOfInstant(one, 0);
+
+        checkDuration64Behavior(one, one);
+
+        Instant two = utcInstant(arbitraryYearInEra0 + yearsSeparation, 1, 1, 0, 0, 0, 250);
+        assertNtpEraOfInstant(two, 1);
+
+        checkDuration64Behavior(one, two);
+        checkDuration64Behavior(two, one);
+    }
+
+    /**
+     * This test confirms that duration calculations fail in the expected fashion if two
+     * Timestamp64s are more than 2^31 seconds apart.
+     *
+     * <p>The types / math specified by NTP for timestamps deliberately takes place in 64-bit signed
+     * arithmetic for the bits used to represent timestamps (32-bit unsigned integer seconds,
+     * 32-bits fixed point for fraction of seconds). Timestamps can therefore represent ~136 years
+     * of seconds.
+     * When subtracting one timestamp from another, we end up with a signed 32-bit seconds value.
+     * This means the max duration representable is ~68 years before numbers will over or underflow.
+     * i.e. the client and server are in the same or adjacent NTP eras and the difference in their
+     * clocks isn't more than ~68 years. >= ~68 years and things break down.
+     */
+    @Test
+    public void testBetween_tooFarApart() {
+        int tooManyYearsSeparation = 68 + 1;
+
+        Instant one = utcInstant(2021, 1, 1, 0, 0, 0, 500);
+        assertNtpEraOfInstant(one, 0);
+        Instant two = utcInstant(2021 + tooManyYearsSeparation, 1, 1, 0, 0, 0, 250);
+        assertNtpEraOfInstant(two, 1);
+
+        checkDuration64OverflowBehavior(one, two);
+        checkDuration64OverflowBehavior(two, one);
+    }
+
+    private static void checkDuration64Behavior(Instant one, Instant two) {
+        // This is the answer if we perform the arithmetic in a lossless fashion.
+        Duration expectedDuration = Duration.between(one, two);
+        Duration64 expectedDuration64 = Duration64.fromDuration(expectedDuration);
+
+        // Sub-second precision is limited in Timestamp64, so we can lose 1ms.
+        assertEqualsOrSlightlyLessThan(
+                expectedDuration.toMillis(), expectedDuration64.toDuration().toMillis());
+
+        Timestamp64 one64 = Timestamp64.fromInstant(one);
+        Timestamp64 two64 = Timestamp64.fromInstant(two);
+
+        // This is the answer if we perform the arithmetic in a lossy fashion.
+        Duration64 actualDuration64 = Duration64.between(one64, two64);
+        assertEquals(expectedDuration64.getSeconds(), actualDuration64.getSeconds());
+        assertEqualsOrSlightlyLessThan(expectedDuration64.getNanos(), actualDuration64.getNanos());
+    }
+
+    private static void checkDuration64OverflowBehavior(Instant one, Instant two) {
+        // This is the answer if we perform the arithmetic in a lossless fashion.
+        Duration trueDuration = Duration.between(one, two);
+
+        // Confirm the maths is expected to overflow / underflow.
+        assertTrue(trueDuration.getSeconds() > Integer.MAX_VALUE / 2
+                || trueDuration.getSeconds() < Integer.MIN_VALUE / 2);
+
+        // Now perform the arithmetic as specified for NTP: do subtraction using the 64-bit
+        // timestamp.
+        Timestamp64 one64 = Timestamp64.fromInstant(one);
+        Timestamp64 two64 = Timestamp64.fromInstant(two);
+
+        Duration64 actualDuration64 = Duration64.between(one64, two64);
+        assertNotEquals(trueDuration.getSeconds(), actualDuration64.getSeconds());
+    }
+
+    /**
+     * Asserts the instant provided is in the specified NTP timestamp era. Used to confirm /
+     * document values picked for tests have the properties needed.
+     */
+    private static void assertNtpEraOfInstant(Instant one, int ntpEra) {
+        long expectedSeconds = one.getEpochSecond();
+
+        // The conversion to Timestamp64 is lossy (it loses the era). We then supply the expected
+        // era. If the era was correct, we will end up with the value we started with (modulo nano
+        // precision loss). If the era is wrong, we won't.
+        Instant roundtrippedInstant = Timestamp64.fromInstant(one).toInstant(ntpEra);
+
+        long actualSeconds = roundtrippedInstant.getEpochSecond();
+        assertEquals(expectedSeconds, actualSeconds);
+    }
+
+    /**
+     * Used to account for the fact that NTP types used 32-bit fixed point storage, so cannot store
+     * all values precisely. The value we get out will always be the value we put in, or one that is
+     * one unit smaller (due to truncation).
+     */
+    private static void assertEqualsOrSlightlyLessThan(long expected, long actual) {
+        assertTrue("expected=" + expected + ", actual=" + actual,
+                expected == actual || expected == actual - 1);
+    }
+
+    private static Instant utcInstant(
+            int year, int monthOfYear, int day, int hour, int minute, int second, int nanos) {
+        return LocalDateTime.of(year, monthOfYear, day, hour, minute, second, nanos)
+                .toInstant(ZoneOffset.UTC);
+    }
+}
diff --git a/core/tests/coretests/src/android/net/sntp/PredictableRandom.java b/core/tests/coretests/src/android/net/sntp/PredictableRandom.java
new file mode 100644
index 0000000..bb2922b
--- /dev/null
+++ b/core/tests/coretests/src/android/net/sntp/PredictableRandom.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.sntp;
+
+import java.util.Random;
+
+class PredictableRandom extends Random {
+    private int[] mIntSequence = new int[] { 1 };
+    private int mIntPos = 0;
+
+    public void setIntSequence(int[] intSequence) {
+        this.mIntSequence = intSequence;
+    }
+
+    @Override
+    public int nextInt() {
+        int value = mIntSequence[mIntPos++];
+        mIntPos %= mIntSequence.length;
+        return value;
+    }
+}
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
new file mode 100644
index 0000000..1b1c500
--- /dev/null
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.sntp;
+
+import static android.net.sntp.Timestamp64.NANOS_PER_SECOND;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class Timestamp64Test {
+
+    @Test
+    public void testFromComponents() {
+        long minNtpEraSeconds = 0;
+        long maxNtpEraSeconds = 0xFFFFFFFFL;
+
+        expectIllegalArgumentException(() -> Timestamp64.fromComponents(minNtpEraSeconds - 1, 0));
+        expectIllegalArgumentException(() -> Timestamp64.fromComponents(maxNtpEraSeconds + 1, 0));
+
+        assertComponentCreation(minNtpEraSeconds, 0);
+        assertComponentCreation(maxNtpEraSeconds, 0);
+        assertComponentCreation(maxNtpEraSeconds, Integer.MIN_VALUE);
+        assertComponentCreation(maxNtpEraSeconds, Integer.MAX_VALUE);
+    }
+
+    private static void assertComponentCreation(long ntpEraSeconds, int fractionBits) {
+        Timestamp64 value = Timestamp64.fromComponents(ntpEraSeconds, fractionBits);
+        assertEquals(ntpEraSeconds, value.getEraSeconds());
+        assertEquals(fractionBits, value.getFractionBits());
+    }
+
+    @Test
+    public void testEqualsAndHashcode() {
+        assertEqualsAndHashcode(0, 0);
+        assertEqualsAndHashcode(1, 0);
+        assertEqualsAndHashcode(0, 1);
+    }
+
+    private static void assertEqualsAndHashcode(int eraSeconds, int fractionBits) {
+        Timestamp64 one = Timestamp64.fromComponents(eraSeconds, fractionBits);
+        Timestamp64 two = Timestamp64.fromComponents(eraSeconds, fractionBits);
+        assertEquals(one, two);
+        assertEquals(one.hashCode(), two.hashCode());
+    }
+
+    @Test
+    public void testStringForm() {
+        expectIllegalArgumentException(() -> Timestamp64.fromString(""));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("."));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("1234567812345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678?12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678..12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("1.12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12.12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("123456.12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("1234567.12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678.1"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678.12"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678.123456"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678.1234567"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("X2345678.12345678"));
+        expectIllegalArgumentException(() -> Timestamp64.fromString("12345678.X2345678"));
+
+        assertStringCreation("00000000.00000000", 0, 0);
+        assertStringCreation("00000001.00000001", 1, 1);
+        assertStringCreation("ffffffff.ffffffff", 0xFFFFFFFFL, 0xFFFFFFFF);
+    }
+
+    private static void assertStringCreation(
+            String string, long expectedSeconds, int expectedFractionBits) {
+        Timestamp64 timestamp64 = Timestamp64.fromString(string);
+        assertEquals(string, timestamp64.toString());
+        assertEquals(expectedSeconds, timestamp64.getEraSeconds());
+        assertEquals(expectedFractionBits, timestamp64.getFractionBits());
+    }
+
+    @Test
+    public void testStringForm_lenientHexCasing() {
+        Timestamp64 mixedCaseValue = Timestamp64.fromString("AaBbCcDd.EeFf1234");
+        assertEquals(0xAABBCCDDL, mixedCaseValue.getEraSeconds());
+        assertEquals(0xEEFF1234, mixedCaseValue.getFractionBits());
+    }
+
+    @Test
+    public void testFromInstant_secondsHandling() {
+        final int era0 = 0;
+        final int eraNeg1 = -1;
+        final int eraNeg2 = -2;
+        final int era1 = 1;
+
+        assertInstantCreationOnlySeconds(-Timestamp64.OFFSET_1900_TO_1970, 0, era0);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 - Timestamp64.SECONDS_IN_ERA, 0, eraNeg1);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 + Timestamp64.SECONDS_IN_ERA, 0, era1);
+
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 - 1, Timestamp64.MAX_SECONDS_IN_ERA, -1);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 - Timestamp64.SECONDS_IN_ERA - 1,
+                Timestamp64.MAX_SECONDS_IN_ERA, eraNeg2);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 + Timestamp64.SECONDS_IN_ERA - 1,
+                Timestamp64.MAX_SECONDS_IN_ERA, era0);
+
+        assertInstantCreationOnlySeconds(-Timestamp64.OFFSET_1900_TO_1970 + 1, 1, era0);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 - Timestamp64.SECONDS_IN_ERA + 1, 1, eraNeg1);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.OFFSET_1900_TO_1970 + Timestamp64.SECONDS_IN_ERA + 1, 1, era1);
+
+        assertInstantCreationOnlySeconds(0, Timestamp64.OFFSET_1900_TO_1970, era0);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.SECONDS_IN_ERA, Timestamp64.OFFSET_1900_TO_1970, eraNeg1);
+        assertInstantCreationOnlySeconds(
+                Timestamp64.SECONDS_IN_ERA, Timestamp64.OFFSET_1900_TO_1970, era1);
+
+        assertInstantCreationOnlySeconds(1, Timestamp64.OFFSET_1900_TO_1970 + 1, era0);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.SECONDS_IN_ERA + 1, Timestamp64.OFFSET_1900_TO_1970 + 1, eraNeg1);
+        assertInstantCreationOnlySeconds(
+                Timestamp64.SECONDS_IN_ERA + 1, Timestamp64.OFFSET_1900_TO_1970 + 1, era1);
+
+        assertInstantCreationOnlySeconds(-1, Timestamp64.OFFSET_1900_TO_1970 - 1, era0);
+        assertInstantCreationOnlySeconds(
+                -Timestamp64.SECONDS_IN_ERA - 1, Timestamp64.OFFSET_1900_TO_1970 - 1, eraNeg1);
+        assertInstantCreationOnlySeconds(
+                Timestamp64.SECONDS_IN_ERA - 1, Timestamp64.OFFSET_1900_TO_1970 - 1, era1);
+    }
+
+    private static void assertInstantCreationOnlySeconds(
+            long epochSeconds, long expectedNtpEraSeconds, int ntpEra) {
+        int nanosOfSecond = 0;
+        Instant instant = Instant.ofEpochSecond(epochSeconds, nanosOfSecond);
+        Timestamp64 timestamp = Timestamp64.fromInstant(instant);
+        assertEquals(expectedNtpEraSeconds, timestamp.getEraSeconds());
+
+        int expectedFractionBits = 0;
+        assertEquals(expectedFractionBits, timestamp.getFractionBits());
+
+        // Confirm the Instant can be round-tripped if we know the era. Also assumes the nanos can
+        // be stored precisely; 0 can be.
+        Instant roundTrip = timestamp.toInstant(ntpEra);
+        assertEquals(instant, roundTrip);
+    }
+
+    @Test
+    public void testFromInstant_fractionHandling() {
+        // Try some values we know can be represented exactly.
+        assertInstantCreationOnlyFractionExact(0x0, 0);
+        assertInstantCreationOnlyFractionExact(0x80000000, 500_000_000L);
+        assertInstantCreationOnlyFractionExact(0x40000000, 250_000_000L);
+
+        // Test the limits of precision.
+        assertInstantCreationOnlyFractionExact(0x00000006, 1L);
+        assertInstantCreationOnlyFractionExact(0x00000005, 1L);
+        assertInstantCreationOnlyFractionExact(0x00000004, 0L);
+        assertInstantCreationOnlyFractionExact(0x00000002, 0L);
+        assertInstantCreationOnlyFractionExact(0x00000001, 0L);
+
+        // Confirm nanosecond storage / precision is within 1ns.
+        final boolean exhaustive = false;
+        for (int i = 0; i < NANOS_PER_SECOND; i++) {
+            Instant instant = Instant.ofEpochSecond(0, i);
+            Instant roundTripped = Timestamp64.fromInstant(instant).toInstant(0);
+            assertNanosWithTruncationAllowed(i, roundTripped);
+            if (!exhaustive) {
+                i += 999_999;
+            }
+        }
+    }
+
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
+    private static void assertInstantCreationOnlyFractionExact(
+            int fractionBits, long expectedNanos) {
+        Timestamp64 timestamp64 = Timestamp64.fromComponents(0, fractionBits);
+
+        final int ntpEra = 0;
+        Instant instant = timestamp64.toInstant(ntpEra);
+
+        assertEquals(expectedNanos, instant.getNano());
+    }
+
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
+    private static void assertNanosWithTruncationAllowed(long expectedNanos, Instant instant) {
+        // Allow for < 1ns difference due to truncation.
+        long actualNanos = instant.getNano();
+        assertTrue("expectedNanos=" + expectedNanos + ",  actualNanos=" + actualNanos,
+                actualNanos == expectedNanos || actualNanos == expectedNanos - 1);
+    }
+
+    @SuppressWarnings("JavaInstantGetSecondsGetNano")
+    @Test
+    public void testMillisRandomizationConstant() {
+        // Mathematically, we can say that to represent 1000 different values, we need 10 binary
+        // digits (2^10 = 1024). The same is true whether we're dealing with integers or fractions.
+        // Unfortunately, for fractions those 1024 values do not correspond to discrete decimal
+        // values. Discrete millisecond values as fractions (e.g. 0.001 - 0.999) cannot be
+        // represented exactly except where the value can also be represented as some combination of
+        // powers of -2. When we convert back and forth, we truncate, so millisecond decimal
+        // fraction N represented as a binary fraction will always be equal to or lower than N. If
+        // we are truncating correctly it will never be as low as (N-0.001). N -> [N-0.001, N].
+
+        // We need to keep 10 bits to hold millis (inaccurately, since there are numbers that
+        // cannot be represented exactly), leaving us able to randomize the remaining 22 bits of the
+        // fraction part without significantly affecting the number represented.
+        assertEquals(22, Timestamp64.SUB_MILLIS_BITS_TO_RANDOMIZE);
+
+        // Brute force proof that randomization logic will keep the timestamp within the range
+        // [N-0.001, N] where x is in milliseconds.
+        int smallFractionRandomizedLow = 0;
+        int smallFractionRandomizedHigh = 0b00000000_00111111_11111111_11111111;
+        int largeFractionRandomizedLow = 0b11111111_11000000_00000000_00000000;
+        int largeFractionRandomizedHigh = 0b11111111_11111111_11111111_11111111;
+
+        long smallLowNanos = Timestamp64.fromComponents(
+                0, smallFractionRandomizedLow).toInstant(0).getNano();
+        long smallHighNanos = Timestamp64.fromComponents(
+                0, smallFractionRandomizedHigh).toInstant(0).getNano();
+        long smallDelta = smallHighNanos - smallLowNanos;
+        long millisInNanos = 1_000_000_000 / 1_000;
+        assertTrue(smallDelta >= 0 && smallDelta < millisInNanos);
+
+        long largeLowNanos = Timestamp64.fromComponents(
+                0, largeFractionRandomizedLow).toInstant(0).getNano();
+        long largeHighNanos = Timestamp64.fromComponents(
+                0, largeFractionRandomizedHigh).toInstant(0).getNano();
+        long largeDelta = largeHighNanos - largeLowNanos;
+        assertTrue(largeDelta >= 0 && largeDelta < millisInNanos);
+
+        PredictableRandom random = new PredictableRandom();
+        random.setIntSequence(new int[] { 0xFFFF_FFFF });
+        Timestamp64 zero = Timestamp64.fromComponents(0, 0);
+        Timestamp64 zeroWithFractionRandomized = zero.randomizeSubMillis(random);
+        assertEquals(zero.getEraSeconds(), zeroWithFractionRandomized.getEraSeconds());
+        assertEquals(smallFractionRandomizedHigh, zeroWithFractionRandomized.getFractionBits());
+    }
+
+    @Test
+    public void testRandomizeLowestBits() {
+        Random random = new Random(1);
+        {
+            int fractionBits = 0;
+            expectIllegalArgumentException(
+                    () -> Timestamp64.randomizeLowestBits(random, fractionBits, -1));
+            expectIllegalArgumentException(
+                    () -> Timestamp64.randomizeLowestBits(random, fractionBits, 0));
+            expectIllegalArgumentException(
+                    () -> Timestamp64.randomizeLowestBits(random, fractionBits, Integer.SIZE));
+            expectIllegalArgumentException(
+                    () -> Timestamp64.randomizeLowestBits(random, fractionBits, Integer.SIZE + 1));
+        }
+
+        // Check the behavior looks correct from a probabilistic point of view.
+        for (int input : new int[] { 0, 0xFFFFFFFF }) {
+            for (int bitCount = 1; bitCount < Integer.SIZE; bitCount++) {
+                int upperBitMask = 0xFFFFFFFF << bitCount;
+                int expectedUpperBits = input & upperBitMask;
+
+                Set<Integer> values = new HashSet<>();
+                values.add(input);
+
+                int trials = 100;
+                for (int i = 0; i < trials; i++) {
+                    int outputFractionBits =
+                            Timestamp64.randomizeLowestBits(random, input, bitCount);
+
+                    // Record the output value for later analysis.
+                    values.add(outputFractionBits);
+
+                    // Check upper bits did not change.
+                    assertEquals(expectedUpperBits, outputFractionBits & upperBitMask);
+                }
+
+                // It's possible to be more rigorous here, perhaps with a histogram. As bitCount
+                // rises, values.size() quickly trend towards the value of trials + 1. For now, this
+                // mostly just guards against a no-op implementation.
+                assertTrue(bitCount + ":" + values.size(), values.size() > 1);
+            }
+        }
+    }
+
+    private static void expectIllegalArgumentException(Runnable r) {
+        try {
+            r.run();
+            fail();
+        } catch (IllegalArgumentException e) {
+            // Expected
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
index c50c818..e2c40d8 100644
--- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
+++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigIterationRule.java
@@ -28,6 +28,7 @@
 import com.android.internal.content.om.OverlayConfig.PackageProvider;
 import com.android.internal.content.om.OverlayScanner;
 import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo;
+import com.android.internal.util.function.TriConsumer;
 
 import org.junit.Assert;
 import org.junit.rules.TestRule;
@@ -39,7 +40,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.Map;
-import java.util.function.BiConsumer;
 import java.util.function.Supplier;
 
 /**
@@ -73,7 +73,7 @@
             final File canonicalPath = new File(path.getCanonicalPath());
             mOverlayStubResults.put(canonicalPath, new ParsedOverlayInfo(
                     packageName, targetPackage, targetSdkVersion, isStatic, priority,
-                    canonicalPath));
+                    canonicalPath, null));
         } catch (IOException e) {
             Assert.fail("Failed to add overlay " + e);
         }
@@ -135,8 +135,8 @@
                 mIteration = Iteration.SYSTEM_SERVER;
                 doAnswer((InvocationOnMock invocation) -> {
                     final Object[] args = invocation.getArguments();
-                    final BiConsumer<ParsingPackageRead, Boolean> f =
-                            (BiConsumer<ParsingPackageRead, Boolean>) args[0];
+                    final TriConsumer<ParsingPackageRead, Boolean, File> f =
+                            (TriConsumer<ParsingPackageRead, Boolean, File>) args[0];
                     for (Map.Entry<File, ParsedOverlayInfo> overlay :
                             mOverlayStubResults.entrySet()) {
                         final ParsingPackageRead a = Mockito.mock(ParsingPackageRead.class);
@@ -147,7 +147,8 @@
                         when(a.isOverlayIsStatic()).thenReturn(info.isStatic);
                         when(a.getOverlayPriority()).thenReturn(info.priority);
                         when(a.getBaseApkPath()).thenReturn(info.path.getPath());
-                        f.accept(a, !info.path.getPath().contains("data/overlay"));
+                        f.accept(a, !info.path.getPath().contains("data/overlay"),
+                                /*preInstalledApexPath=*/null);
                     }
                     return null;
                 }).when(mPkgProvider).forEachPackage(any());
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index 5c84794..d361da9 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -33,6 +33,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class BluetoothPowerCalculatorTest {
@@ -105,10 +107,10 @@
         final BluetoothActivityEnergyInfo info = new BluetoothActivityEnergyInfo(1000,
                 BluetoothActivityEnergyInfo.BT_STACK_STATE_STATE_ACTIVE, 7000, 5000, 0,
                 reportedEnergyUc);
-        info.setUidTraffic(new UidTraffic[]{
-                new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000),
-                new UidTraffic(APP_UID, 3000, 4000)
-        });
+        info.setUidTraffic(new ArrayList<UidTraffic>(){{
+                add(new UidTraffic(Process.BLUETOOTH_UID, 1000, 2000));
+                add(new UidTraffic(APP_UID, 3000, 4000));
+            }});
         mStatsRule.getBatteryStats().updateBluetoothStateLocked(info,
                 consumedEnergyUc, 1000, 1000);
     }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index c068963..ed8cdb9 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5203,21 +5203,6 @@
         }
     }
 
-    /**
-     * @hide
-     * Notifies AudioService that it is connected to an A2DP device that supports absolute volume,
-     * so that AudioService can send volume change events to the A2DP device, rather than handling
-     * them.
-     */
-    public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
-        final IAudioService service = getService();
-        try {
-            service.avrcpSupportsAbsoluteVolume(address, support);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
      /**
       * {@hide}
       */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7191280..dd44fdf 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -171,8 +171,6 @@
 
     int getEncodedSurroundMode(int targetSdkVersion);
 
-    oneway void avrcpSupportsAbsoluteVolume(String address, boolean support);
-
     void setSpeakerphoneOn(IBinder cb, boolean on);
 
     boolean isSpeakerphoneOn();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 49477b9..374cc75 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -182,10 +182,15 @@
         public String mName;
         public int mValue;
         public boolean mDefault;
+        public boolean mInternal;
         public Feature(String name, int value, boolean def) {
+            this(name, value, def, false /* internal */);
+        }
+        public Feature(String name, int value, boolean def, boolean internal) {
             mName = name;
             mValue = value;
             mDefault = def;
+            mInternal = internal;
         }
     }
 
@@ -579,6 +584,11 @@
         public static final String FEATURE_LowLatency = "low-latency";
 
         /**
+         * Do not include in REGULAR_CODECS list in MediaCodecList.
+         */
+        private static final String FEATURE_SpecialCodec = "special-codec";
+
+        /**
          * <b>video encoder only</b>: codec supports quantization parameter bounds.
          * @see MediaFormat#KEY_VIDEO_QP_MAX
          * @see MediaFormat#KEY_VIDEO_QP_MIN
@@ -616,6 +626,8 @@
             new Feature(FEATURE_MultipleFrames,   (1 << 5), false),
             new Feature(FEATURE_DynamicTimestamp, (1 << 6), false),
             new Feature(FEATURE_LowLatency,       (1 << 7), true),
+            // feature to exclude codec from REGULAR codec list
+            new Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
         };
 
         private static final Feature[] encoderFeatures = {
@@ -623,6 +635,8 @@
             new Feature(FEATURE_MultipleFrames, (1 << 1), false),
             new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
             new Feature(FEATURE_QpBounds, (1 << 3), false),
+            // feature to exclude codec from REGULAR codec list
+            new Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
         };
 
         /** @hide */
@@ -630,7 +644,9 @@
             Feature[] features = getValidFeatures();
             String[] res = new String[features.length];
             for (int i = 0; i < res.length; i++) {
-                res[i] = features[i].mName;
+                if (!features[i].mInternal) {
+                    res[i] = features[i].mName;
+                }
             }
             return res;
         }
@@ -778,6 +794,10 @@
 
             // check feature support
             for (Feature feat: getValidFeatures()) {
+                if (feat.mInternal) {
+                    continue;
+                }
+
                 Integer yesNo = (Integer)map.get(MediaFormat.KEY_FEATURE_ + feat.mName);
                 if (yesNo == null) {
                     continue;
@@ -1091,7 +1111,9 @@
                     mFlagsRequired |= feat.mValue;
                 }
                 mFlagsSupported |= feat.mValue;
-                mDefaultFormat.setInteger(key, 1);
+                if (!feat.mInternal) {
+                    mDefaultFormat.setInteger(key, 1);
+                }
                 // TODO restrict features by mFlagsVerified once all codecs reliably verify them
             }
         }
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 7e9d2d8..9c9e83b 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -106,7 +106,7 @@
     @IntDef({
             TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET,
             TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE,
-            TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID,
+            TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID, TYPE_BLE_HEADSET,
             TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Type {}
@@ -202,6 +202,14 @@
     public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID;
 
     /**
+     * A route type describing a BLE HEADSET.
+     *
+     * @see #getType
+     * @hide
+     */
+    public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET;
+
+    /**
      * A route type indicating the presentation of the media is happening on a TV.
      *
      * @see #getType
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index bc73f6a..c775b6f 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -183,8 +183,8 @@
         "libmedia",
         "libnativehelper",
         "libutils",
-        "tv_tuner_aidl_interface-ndk_platform",
-        "tv_tuner_resource_manager_aidl_interface-ndk_platform",
+        "tv_tuner_aidl_interface-ndk",
+        "tv_tuner_resource_manager_aidl_interface-ndk",
     ],
 
     static_libs: [
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
index c633113..2736870 100644
--- a/packages/PackageInstaller/OWNERS
+++ b/packages/PackageInstaller/OWNERS
@@ -1,7 +1,5 @@
 svetoslavganov@google.com
-toddke@google.com
-patb@google.com
-suprabh@google.com
+include /PACKAGE_MANAGER_OWNERS
 
 # For automotive related changes
 rogerxue@google.com
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
index a0e28ba..5b2e1e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/OWNERS
@@ -1,5 +1,6 @@
 # Default reviewers for this and subdirectories.
 takaoka@google.com
 yukawa@google.com
+wilsonwu@google.com
 
 # Emergency approvers in case the above are not available
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 3c43f4a6..54230c8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -21,6 +21,7 @@
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
 import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_BLE_HEADSET;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -481,6 +482,7 @@
                 break;
             case TYPE_HEARING_AID:
             case TYPE_BLUETOOTH_A2DP:
+            case TYPE_BLE_HEADSET:
                 final BluetoothDevice device =
                         BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
                 final CachedBluetoothDevice cachedDevice =
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 22001c9..215e2a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -38,6 +38,7 @@
 import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
+import com.android.settingslib.bluetooth.LeAudioProfile;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -440,6 +441,7 @@
     private boolean isActiveDevice(CachedBluetoothDevice device) {
         boolean isActiveDeviceA2dp = false;
         boolean isActiveDeviceHearingAid = false;
+        boolean isActiveLeAudio = false;
         final A2dpProfile a2dpProfile = mLocalBluetoothManager.getProfileManager().getA2dpProfile();
         if (a2dpProfile != null) {
             isActiveDeviceA2dp = device.getDevice().equals(a2dpProfile.getActiveDevice());
@@ -453,7 +455,15 @@
             }
         }
 
-        return isActiveDeviceA2dp || isActiveDeviceHearingAid;
+        if (!isActiveDeviceA2dp && !isActiveDeviceHearingAid) {
+            final LeAudioProfile leAudioProfile = mLocalBluetoothManager.getProfileManager()
+                    .getLeAudioProfile();
+            if (leAudioProfile != null) {
+                isActiveLeAudio = leAudioProfile.getActiveDevices().contains(device.getDevice());
+            }
+        }
+
+        return isActiveDeviceA2dp || isActiveDeviceHearingAid || isActiveLeAudio;
     }
 
     private Collection<DeviceCallback> getCallbacks() {
@@ -526,7 +536,7 @@
                 if (cachedDevice != null) {
                     if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
                             && !cachedDevice.isConnected()
-                            && isA2dpOrHearingAidDevice(cachedDevice)) {
+                            && isMediaDevice(cachedDevice)) {
                         deviceCount++;
                         cachedBluetoothDeviceList.add(cachedDevice);
                         if (deviceCount >= MAX_DISCONNECTED_DEVICE_NUM) {
@@ -550,9 +560,10 @@
             return new ArrayList<>(mDisconnectedMediaDevices);
         }
 
-        private boolean isA2dpOrHearingAidDevice(CachedBluetoothDevice device) {
+        private boolean isMediaDevice(CachedBluetoothDevice device) {
             for (LocalBluetoothProfile profile : device.getConnectableProfiles()) {
-                if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile) {
+                if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile ||
+                        profile instanceof LeAudioProfile) {
                     return true;
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index f21c359..a49d7f6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -29,6 +29,7 @@
 import static android.media.MediaRoute2Info.TYPE_USB_HEADSET;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static android.media.MediaRoute2Info.TYPE_BLE_HEADSET;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -122,6 +123,7 @@
                 break;
             case TYPE_HEARING_AID:
             case TYPE_BLUETOOTH_A2DP:
+            case TYPE_BLE_HEADSET:
                 mType = MediaDeviceType.TYPE_BLUETOOTH_DEVICE;
                 break;
             case TYPE_UNKNOWN:
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
index e887c450..f50802a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/BluetoothMediaDeviceTest.java
@@ -52,6 +52,7 @@
 
         when(mDevice.isActiveDevice(BluetoothProfile.A2DP)).thenReturn(true);
         when(mDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
+        when(mDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
 
         mBluetoothMediaDevice = new BluetoothMediaDevice(mContext, mDevice, null, null, null);
     }
diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS
index 6c61d4b9..5ade971 100644
--- a/packages/SettingsProvider/OWNERS
+++ b/packages/SettingsProvider/OWNERS
@@ -2,6 +2,4 @@
 hackbod@google.com
 narayan@google.com
 svetoslavganov@google.com
-schfan@google.com
-toddke@google.com
-patb@google.com
+include /PACKAGE_MANAGER_OWNERS
diff --git a/services/core/java/android/content/pm/OWNERS b/services/core/java/android/content/pm/OWNERS
index 5eed0b5..3993140 100644
--- a/services/core/java/android/content/pm/OWNERS
+++ b/services/core/java/android/content/pm/OWNERS
@@ -1 +1 @@
-include /core/java/android/content/pm/OWNERS
\ No newline at end of file
+include /PACKAGE_MANAGER_OWNERS
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 1e608f5..0146aa8 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -81,8 +81,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.concurrent.atomic.AtomicReference;
@@ -1435,11 +1433,7 @@
      */
     public static final class HealthServiceWrapper {
         private static final String TAG = "HealthServiceWrapper";
-        public static final String INSTANCE_HEALTHD = "backup";
         public static final String INSTANCE_VENDOR = "default";
-        // All interesting instances, sorted by priority high -> low.
-        private static final List<String> sAllInstances =
-                Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
 
         private final IServiceNotification mNotification = new Notification();
         private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
@@ -1471,8 +1465,8 @@
         }
 
         /**
-         * Start monitoring registration of new IHealth services. Only instances that are in
-         * {@code sAllInstances} and in device / framework manifest are used. This function should
+         * Start monitoring registration of new IHealth services. Only instance
+         * {@link #INSTANCE_VENDOR} and in device / framework manifest are used. This function should
          * only be called once.
          *
          * mCallback.onRegistration() is called synchronously (aka in init thread) before
@@ -1481,7 +1475,7 @@
          * @throws RemoteException transaction error when talking to IServiceManager
          * @throws NoSuchElementException if one of the following cases:
          *         - No service manager;
-         *         - none of {@code sAllInstances} are in manifests (i.e. not
+         *         - {@link #INSTANCE_VENDOR} is not in manifests (i.e. not
          *           available on this device), or none of these instances are available to current
          *           process.
          * @throws NullPointerException when supplier is null
@@ -1499,26 +1493,23 @@
 
             // Initialize mLastService and call callback for the first time (in init thread)
             IHealth newService = null;
-            for (String name : sAllInstances) {
-                traceBegin("HealthInitGetService_" + name);
-                try {
-                    newService = healthSupplier.get(name);
-                } catch (NoSuchElementException ex) {
-                    /* ignored, handled below */
-                } finally {
-                    traceEnd();
-                }
-                if (newService != null) {
-                    mInstanceName = name;
-                    mLastService.set(newService);
-                    break;
-                }
+            traceBegin("HealthInitGetService_" + INSTANCE_VENDOR);
+            try {
+                newService = healthSupplier.get(INSTANCE_VENDOR);
+            } catch (NoSuchElementException ex) {
+                /* ignored, handled below */
+            } finally {
+                traceEnd();
+            }
+            if (newService != null) {
+                mInstanceName = INSTANCE_VENDOR;
+                mLastService.set(newService);
             }
 
             if (mInstanceName == null || newService == null) {
                 throw new NoSuchElementException(String.format(
-                        "No IHealth service instance among %s is available. Perhaps no permission?",
-                        sAllInstances.toString()));
+                        "IHealth service instance %s isn't available. Perhaps no permission?",
+                        INSTANCE_VENDOR));
             }
 
             if (callback != null) {
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 95dc667..b641377 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -16,6 +16,9 @@
 # ServiceWatcher
 per-file ServiceWatcher.java = sooniln@google.com
 
+# Health
+per-file BatteryService.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
+
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Battery* = file:/BATTERY_STATS_OWNERS
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c11c4de..43bd9e7 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1029,7 +1029,6 @@
             return;
         }
 
-        int phoneId = getPhoneIdFromSubId(subId);
         synchronized (mRecords) {
             // register
             IBinder b = callback.asBinder();
@@ -1052,21 +1051,24 @@
             // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
             // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                if (DBG) {
+                    log("invalid subscription id, use default id");
+                }
                 r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
             } else {//APP specify subID
                 r.subId = subId;
             }
-            r.phoneId = phoneId;
+            r.phoneId = getPhoneIdFromSubId(r.subId);
             r.eventList = events;
 
             if (DBG) {
-                log("listen:  Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
+                log("listen:  Register r=" + r + " r.subId=" + r.subId + " r.phoneId=" + r.phoneId);
             }
-            if (notifyNow && validatePhoneId(phoneId)) {
+            if (notifyNow && validatePhoneId(r.phoneId)) {
                 if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){
                     try {
-                        if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
-                        ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+                        if (VDBG) log("listen: call onSSC state=" + mServiceState[r.phoneId]);
+                        ServiceState rawSs = new ServiceState(mServiceState[r.phoneId]);
                         if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                             r.callback.onServiceStateChanged(rawSs);
                         } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
@@ -1082,8 +1084,8 @@
                 }
                 if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
                     try {
-                        if (mSignalStrength[phoneId] != null) {
-                            int gsmSignalStrength = mSignalStrength[phoneId]
+                        if (mSignalStrength[r.phoneId] != null) {
+                            int gsmSignalStrength = mSignalStrength[r.phoneId]
                                     .getGsmSignalStrength();
                             r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
                                     : gsmSignalStrength));
@@ -1096,7 +1098,7 @@
                         TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
                     try {
                         r.callback.onMessageWaitingIndicatorChanged(
-                                mMessageWaiting[phoneId]);
+                                mMessageWaiting[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1105,7 +1107,7 @@
                         TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
                     try {
                         r.callback.onCallForwardingIndicatorChanged(
-                                mCallForwarding[phoneId]);
+                                mCallForwarding[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1113,11 +1115,11 @@
                 if (validateEventAndUserLocked(
                         r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
                     try {
-                        if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
+                        if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[r.phoneId]);
                         if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                 && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
                             // null will be translated to empty CellLocation object in client.
-                            r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
+                            r.callback.onCellLocationChanged(mCellIdentity[r.phoneId]);
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1125,38 +1127,38 @@
                 }
                 if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)) {
                     try {
-                        r.callback.onLegacyCallStateChanged(mCallState[phoneId],
-                                getCallIncomingNumber(r, phoneId));
+                        r.callback.onLegacyCallStateChanged(mCallState[r.phoneId],
+                                getCallIncomingNumber(r, r.phoneId));
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
                     try {
-                        r.callback.onCallStateChanged(mCallState[phoneId]);
+                        r.callback.onCallStateChanged(mCallState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
                     try {
-                        r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
-                                mDataConnectionNetworkType[phoneId]);
+                        r.callback.onDataConnectionStateChanged(mDataConnectionState[r.phoneId],
+                                mDataConnectionNetworkType[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) {
                     try {
-                        r.callback.onDataActivity(mDataActivity[phoneId]);
+                        r.callback.onDataActivity(mDataActivity[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
                     try {
-                        if (mSignalStrength[phoneId] != null) {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                        if (mSignalStrength[r.phoneId] != null) {
+                            r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1166,8 +1168,8 @@
                         TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
                     updateReportSignalStrengthDecision(r.subId);
                     try {
-                        if (mSignalStrength[phoneId] != null) {
-                            r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                        if (mSignalStrength[r.phoneId] != null) {
+                            r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1176,11 +1178,13 @@
                 if (validateEventAndUserLocked(
                         r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
                     try {
-                        if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
-                                + mCellInfo.get(phoneId));
+                        if (DBG_LOC) {
+                            log("listen: mCellInfo[" + r.phoneId + "] = "
+                                    + mCellInfo.get(r.phoneId));
+                        }
                         if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                 && checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
-                            r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+                            r.callback.onCellInfoChanged(mCellInfo.get(r.phoneId));
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1188,22 +1192,22 @@
                 }
                 if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) {
                     try {
-                        r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+                        r.callback.onPreciseCallStateChanged(mPreciseCallState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
                     try {
-                        r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
-                                mCallPreciseDisconnectCause[phoneId]);
+                        r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[r.phoneId],
+                                mCallPreciseDisconnectCause[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
                     try {
-                        r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
+                        r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(r.phoneId));
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1212,7 +1216,7 @@
                         TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
                     try {
                         for (PreciseDataConnectionState pdcs
-                                : mPreciseDataConnectionStates.get(phoneId).values()) {
+                                : mPreciseDataConnectionStates.get(r.phoneId).values()) {
                             r.callback.onPreciseDataConnectionStateChanged(pdcs);
                         }
                     } catch (RemoteException ex) {
@@ -1229,29 +1233,29 @@
                 if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
                     try {
                         r.callback.onVoiceActivationStateChanged(
-                                mVoiceActivationState[phoneId]);
+                                mVoiceActivationState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
                     try {
-                        r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+                        r.callback.onDataActivationStateChanged(mDataActivationState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
                     try {
-                        r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+                        r.callback.onUserMobileDataStateChanged(mUserMobileDataState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
                     try {
-                        if (mTelephonyDisplayInfos[phoneId] != null) {
-                            r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
+                        if (mTelephonyDisplayInfos[r.phoneId] != null) {
+                            r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[r.phoneId]);
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1288,20 +1292,20 @@
                 }
                 if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) {
                     try {
-                        r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
+                        r.callback.onSrvccStateChanged(mSrvccState[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
                     try {
-                        r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
+                        r.callback.onCallAttributesChanged(mCallAttributes[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
                 }
                 if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) {
-                    BarringInfo barringInfo = mBarringInfo.get(phoneId);
+                    BarringInfo barringInfo = mBarringInfo.get(r.phoneId);
                     BarringInfo biNoLocation = barringInfo != null
                             ? barringInfo.createLocationInfoSanitizedCopy() : null;
                     if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
@@ -1319,8 +1323,8 @@
                         r.callback.onPhysicalChannelConfigChanged(
                                 shouldSanitizeLocationForPhysicalChannelConfig(r)
                                         ? getLocationSanitizedConfigs(
-                                                mPhysicalChannelConfigs.get(phoneId))
-                                        : mPhysicalChannelConfigs.get(phoneId));
+                                                mPhysicalChannelConfigs.get(r.phoneId))
+                                        : mPhysicalChannelConfigs.get(r.phoneId));
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1329,7 +1333,7 @@
                         TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) {
                     try {
                         r.callback.onDataEnabledChanged(
-                                mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
+                                mIsDataEnabled[r.phoneId], mDataEnabledReason[r.phoneId]);
                     } catch (RemoteException ex) {
                         remove(r.binder);
                     }
@@ -1337,9 +1341,9 @@
                 if (events.contains(
                         TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
                     try {
-                        if (mLinkCapacityEstimateLists.get(phoneId) != null) {
+                        if (mLinkCapacityEstimateLists.get(r.phoneId) != null) {
                             r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists
-                                    .get(phoneId));
+                                    .get(r.phoneId));
                         }
                     } catch (RemoteException ex) {
                         remove(r.binder);
@@ -1581,7 +1585,7 @@
                     }
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
 
                         try {
                             ServiceState stateToSend;
@@ -1643,7 +1647,7 @@
                         if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
                                 && r.matchTelephonyCallbackEvent(
                                         TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
-                                && idMatch(r.subId, subId, phoneId)) {
+                                && idMatch(r, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
@@ -1654,7 +1658,7 @@
                         if ((activationType == SIM_ACTIVATION_TYPE_DATA)
                                 && r.matchTelephonyCallbackEvent(
                                         TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)
-                                && idMatch(r.subId, subId, phoneId)) {
+                                && idMatch(r, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
@@ -1696,7 +1700,7 @@
                             TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
                             || r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG) {
                                 log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
@@ -1710,7 +1714,7 @@
                     }
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                             int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -1757,7 +1761,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onCarrierNetworkChange(active);
                         } catch (RemoteException ex) {
@@ -1789,7 +1793,7 @@
                 for (Record r : mRecords) {
                     if (validateEventAndUserLocked(
                             r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)
+                            && idMatch(r, subId, phoneId)
                             && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
                         try {
@@ -1823,7 +1827,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onMessageWaitingIndicatorChanged(mwi);
                         } catch (RemoteException ex) {
@@ -1850,7 +1854,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onUserMobileDataStateChanged(state);
                         } catch (RemoteException ex) {
@@ -1889,7 +1893,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
-                            && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (!mConfigurationProvider.isDisplayInfoNrAdvancedSupported(
                                     r.callingPackage, Binder.getCallingUserHandle())) {
@@ -1940,7 +1944,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onCallForwardingIndicatorChanged(cfi);
                         } catch (RemoteException ex) {
@@ -1969,7 +1973,7 @@
                     // Notify by correct subId.
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onDataActivity(state);
                         } catch (RemoteException ex) {
@@ -2017,7 +2021,7 @@
                     for (Record r : mRecords) {
                         if (r.matchTelephonyCallbackEvent(
                                 TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
-                                && idMatch(r.subId, subId, phoneId)) {
+                                && idMatch(r, subId, phoneId)) {
                             try {
                                 if (DBG) {
                                     log("Notify data connection state changed on sub: " + subId);
@@ -2042,7 +2046,7 @@
                     for (Record r : mRecords) {
                         if (r.matchTelephonyCallbackEvent(
                                 TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
-                                && idMatch(r.subId, subId, phoneId)) {
+                                && idMatch(r, subId, phoneId)) {
                             try {
                                 r.callback.onPreciseDataConnectionStateChanged(preciseState);
                             } catch (RemoteException ex) {
@@ -2089,7 +2093,7 @@
                 for (Record r : mRecords) {
                     if (validateEventAndUserLocked(
                             r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)
+                            && idMatch(r, subId, phoneId)
                             && (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
                                     && checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
                         try {
@@ -2143,7 +2147,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
                         } catch (RemoteException ex) {
@@ -2152,7 +2156,7 @@
                     }
                     if (notifyCallAttributes && r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
                         } catch (RemoteException ex) {
@@ -2177,7 +2181,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
                                     mCallPreciseDisconnectCause[phoneId]);
@@ -2202,7 +2206,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyImsCallDisconnectCause: mImsReasonInfo="
@@ -2234,7 +2238,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r);
@@ -2263,7 +2267,7 @@
                     }
                     if ((r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_OEM_HOOK_RAW))
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onOemHookRawEvent(rawData);
                         } catch (RemoteException ex) {
@@ -2343,7 +2347,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onRadioPowerStateChanged(state);
                         } catch (RemoteException ex) {
@@ -2372,7 +2376,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
                             if (VDBG) {
@@ -2459,7 +2463,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
                         } catch (RemoteException ex) {
@@ -2490,7 +2494,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_REGISTRATION_FAILURE)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onRegistrationFailed(
                                     checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
@@ -2533,7 +2537,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_BARRING_INFO_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyBarringInfo: mBarringInfo="
@@ -2578,7 +2582,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (DBG_LOC) {
                                 log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
@@ -2645,7 +2649,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onDataEnabledChanged(enabled, reason);
                         } catch (RemoteException ex) {
@@ -2680,7 +2684,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             if (VDBG) {
                                 log("notifyAllowedNetworkTypesChanged: reason= " + reason
@@ -2722,7 +2726,7 @@
                 for (Record r : mRecords) {
                     if (r.matchTelephonyCallbackEvent(
                             TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)
-                            && idMatch(r.subId, subId, phoneId)) {
+                            && idMatch(r, subId, phoneId)) {
                         try {
                             r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList);
                         } catch (RemoteException ex) {
@@ -3172,33 +3176,24 @@
     }
 
     /**
-     * If the registrant specified a subId, then we should only notify it if subIds match.
-     * If the registrant registered with DEFAULT subId, we should notify only when the related subId
-     * is default subId (which could be INVALID if there's no default subId).
+     * Match the sub id or phone id of the event to the record
      *
-     * This should be the correct way to check record ID match. in idMatch the record's phoneId is
-     * speculated based on subId passed by the registrant so it's not a good reference.
-     * But to avoid triggering potential regression only replace idMatch with it when an issue with
-     * idMatch is reported. Eventually this should replace all instances of idMatch.
+     * We follow the rules below:
+     * 1) If sub id of the event is invalid, phone id should be used.
+     * 2) The event on default sub should be notified to the records
+     * which register the default sub id.
+     * 3) Sub id should be exactly matched for all other cases.
      */
-    private boolean idMatchWithoutDefaultPhoneCheck(int subIdInRecord, int subIdToNotify) {
-        if (subIdInRecord == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
-            return (subIdToNotify == mDefaultSubId);
-        } else {
-            return (subIdInRecord == subIdToNotify);
-        }
-    }
+    boolean idMatch(Record r, int subId, int phoneId) {
 
-    boolean idMatch(int rSubId, int subId, int phoneId) {
-
-        if(subId < 0) {
-            // Invalid case, we need compare phoneId with default one.
-            return (mDefaultPhoneId == phoneId);
+        if (subId < 0) {
+            // Invalid case, we need compare phoneId.
+            return (r.phoneId == phoneId);
         }
-        if(rSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+        if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             return (subId == mDefaultSubId);
         } else {
-            return (rSubId == subId);
+            return (r.subId == subId);
         }
     }
 
diff --git a/services/core/java/com/android/server/VpnManagerService.java b/services/core/java/com/android/server/VpnManagerService.java
index a03425c..b48e21e 100644
--- a/services/core/java/com/android/server/VpnManagerService.java
+++ b/services/core/java/com/android/server/VpnManagerService.java
@@ -681,7 +681,7 @@
         intentFilter = new IntentFilter();
         intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
         mUserAllContext.registerReceiver(
-                mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
+                mIntentReceiver, intentFilter, NETWORK_STACK, mHandler, Context.RECEIVER_EXPORTED);
     }
 
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a2fec27..a29a49c 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4906,7 +4906,9 @@
             sr.setProcess(null, null, 0, null);
             sr.isolatedProc = null;
             sr.executeNesting = 0;
-            sr.forceClearTracker();
+            synchronized (mAm.mProcessStats.mLock) {
+                sr.forceClearTracker();
+            }
             if (mDestroyingServices.remove(sr)) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
             }
@@ -5056,7 +5058,9 @@
             i--;
             ServiceRecord sr = mDestroyingServices.get(i);
             if (sr.app == app) {
-                sr.forceClearTracker();
+                synchronized (mAm.mProcessStats.mLock) {
+                    sr.forceClearTracker();
+                }
                 mDestroyingServices.remove(i);
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "killServices remove destroying " + sr);
             }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 2f20efb..3ccacd8 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -87,6 +87,7 @@
         DeviceConfig.NAMESPACE_LMKD_NATIVE,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
         DeviceConfig.NAMESPACE_NETD_NATIVE,
+        DeviceConfig.NAMESPACE_NNAPI_NATIVE,
         DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index ddbb320..c383f51 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -22,6 +22,7 @@
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothLeAudio;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -503,6 +504,18 @@
         }
     }
 
+    /*package*/ static final class BleVolumeInfo {
+        final int mIndex;
+        final int mMaxIndex;
+        final int mStreamType;
+
+        BleVolumeInfo(int index, int maxIndex, int streamType) {
+            mIndex = index;
+            mMaxIndex = maxIndex;
+            mStreamType = streamType;
+        }
+    };
+
     /*package*/ static final class BtDeviceConnectionInfo {
         final @NonNull BluetoothDevice mDevice;
         final @AudioService.BtProfileConnectionState int mState;
@@ -711,6 +724,11 @@
         sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
     }
 
+     /*package*/ void postSetLeAudioVolumeIndex(int index, int maxIndex, int streamType) {
+        BleVolumeInfo info = new BleVolumeInfo(index, maxIndex, streamType);
+        sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info);
+    }
+
     /*package*/ void postSetModeOwnerPid(int pid, int mode) {
         sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode);
     }
@@ -851,6 +869,10 @@
         return mAudioService.getVssVolumeForDevice(streamType, device);
     }
 
+    /*package*/ int getMaxVssVolumeForStream(int streamType) {
+        return mAudioService.getMaxVssVolumeForStream(streamType);
+    }
+
     /*package*/ int getDeviceForStream(int streamType) {
         return mAudioService.getDeviceForStream(streamType);
     }
@@ -962,6 +984,10 @@
         sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
     }
 
+    /*package*/ void postDisconnectLeAudio() {
+        sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE);
+    }
+
     /*package*/ void postDisconnectHeadset() {
         sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
     }
@@ -983,6 +1009,11 @@
                 hearingAidProfile);
     }
 
+    /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) {
+        sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE,
+                leAudioProfile);
+    }
+
     /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) {
         sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client);
     }
@@ -1321,6 +1352,12 @@
                         mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
                     }
                     break;
+                case MSG_II_SET_LE_AUDIO_OUT_VOLUME: {
+                    final BleVolumeInfo info = (BleVolumeInfo) msg.obj;
+                    synchronized (mDeviceStateLock) {
+                        mBtHelper.setLeAudioVolume(info.mIndex, info.mMaxIndex, info.mStreamType);
+                    }
+                } break;
                 case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
                     synchronized (mDeviceStateLock) {
                         mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
@@ -1384,6 +1421,11 @@
                         }
                     }
                     break;
+                case MSG_DISCONNECT_BT_LE_AUDIO:
+                    synchronized(mDeviceStateLock) {
+                        mDeviceInventory.disconnectLeAudio();
+                    }
+                    break;
                 case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
                     synchronized (mDeviceStateLock) {
                         mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
@@ -1399,6 +1441,12 @@
                         mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
                     }
                     break;
+
+                case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO:
+                    synchronized(mDeviceStateLock) {
+                        mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj);
+                    }
+                    break;
                 case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
                     synchronized (mSetModeLock) {
                         synchronized (mDeviceStateLock) {
@@ -1586,6 +1634,11 @@
     private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43;
     private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44;
     private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45;
+    // process set volume for Le Audio, obj is BleVolumeInfo
+    private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46;
+
+    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47;
+    private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48;
 
     private static boolean isMessageHandledUnderWakelock(int msgId) {
         switch(msgId) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 64e620e..6c3c736 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -887,6 +887,28 @@
         }
     }
 
+     /*package*/ void disconnectLeAudio() {
+        synchronized (mDevicesLock) {
+            final ArraySet<String> toRemove = new ArraySet<>();
+            // Disconnect ALL DEVICE_OUT_BLE_HEADSET devices
+            mConnectedDevices.values().forEach(deviceInfo -> {
+                if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) {
+                    toRemove.add(deviceInfo.mDeviceAddress);
+                }
+            });
+            new MediaMetrics.Item(mMetricsId + "disconnectLeAudio")
+                    .record();
+            if (toRemove.size() > 0) {
+                final int delay = checkSendBecomingNoisyIntentInt(
+                        AudioSystem.DEVICE_OUT_BLE_HEADSET, 0, AudioSystem.DEVICE_NONE);
+                toRemove.stream().forEach(deviceAddress ->
+                        makeLeAudioDeviceUnavailable(deviceAddress,
+                            AudioSystem.DEVICE_OUT_BLE_HEADSET)
+                );
+            }
+        }
+    }
+
     // must be called before removing the device from mConnectedDevices
     // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
     // from AudioSystem
@@ -1195,6 +1217,10 @@
             return;
         }
 
+        final int leAudioVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
+                                    AudioSystem.DEVICE_OUT_BLE_HEADSET);
+        final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
+        mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
         mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
     }
 
@@ -1243,6 +1269,7 @@
         BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
         BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE);
         BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
+        BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET);
         BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
         BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
         BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bf5f4c2..d75f21c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -334,6 +334,10 @@
         return mStreamStates[stream].getIndex(device);
     }
 
+    /*package*/ int getMaxVssVolumeForStream(int stream) {
+        return mStreamStates[stream].getMaxIndex();
+    }
+
     private SettingsObserver mSettingsObserver;
 
     private AtomicInteger mMode = new AtomicInteger(AudioSystem.MODE_NORMAL);
@@ -2952,6 +2956,16 @@
                 mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10);
             }
 
+            if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+                    && streamType == getBluetoothContextualVolumeStream()) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+                            + newIndex + " stream=" + streamType);
+                }
+                mDeviceBroker.postSetLeAudioVolumeIndex(newIndex,
+                    mStreamStates[streamType].getMaxIndex(), streamType);
+            }
+
             // Check if volume update should be send to Hearing Aid
             if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
                 // only modify the hearing aid attenuation when the stream to modify matches
@@ -3580,6 +3594,16 @@
                 mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10);
             }
 
+            if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET
+                    && streamType == getBluetoothContextualVolumeStream()) {
+                if (DEBUG_VOL) {
+                    Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index="
+                            + index + " stream=" + streamType);
+                }
+                mDeviceBroker.postSetLeAudioVolumeIndex(index,
+                    mStreamStates[streamType].getMaxIndex(), streamType);
+            }
+
             if (device == AudioSystem.DEVICE_OUT_HEARING_AID
                     && streamType == getBluetoothContextualVolumeStream()) {
                 Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
@@ -6112,6 +6136,11 @@
         if (pkgName == null) {
             pkgName = "";
         }
+        if (device.getType() == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
+            avrcpSupportsAbsoluteVolume(device.getAddress(),
+                    deviceVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+            return;
+        }
 
         int audioSystemDeviceOut = AudioDeviceInfo.convertDeviceTypeToInternalDevice(
                 device.getType());
@@ -7768,7 +7797,7 @@
         }
     }
 
-    public void avrcpSupportsAbsoluteVolume(String address, boolean support) {
+    private void avrcpSupportsAbsoluteVolume(String address, boolean support) {
         // address is not used for now, but may be used when multiple a2dp devices are supported
         sVolumeLogger.log(new AudioEventLogger.StringEvent("avrcpSupportsAbsoluteVolume addr="
                 + address + " support=" + support));
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 0eb5a5d..3137fa5 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -155,6 +155,7 @@
         static final int VOL_MODE_CHANGE_HEARING_AID = 7;
         static final int VOL_SET_GROUP_VOL = 8;
         static final int VOL_MUTE_STREAM_INT = 9;
+        static final int VOL_SET_LE_AUDIO_VOL = 10;
 
         final int mOp;
         final int mStream;
@@ -310,6 +311,13 @@
                             .set(MediaMetrics.Property.INDEX, mVal1)
                             .record();
                     return;
+                case VOL_SET_LE_AUDIO_VOL:
+                    new MediaMetrics.Item(mMetricsId)
+                            .set(MediaMetrics.Property.EVENT, "setLeAudioVolume")
+                            .set(MediaMetrics.Property.INDEX, mVal1)
+                            .set(MediaMetrics.Property.MAX_INDEX, mVal2)
+                            .record();
+                    return;
                 case VOL_SET_AVRCP_VOL:
                     new MediaMetrics.Item(mMetricsId)
                             .set(MediaMetrics.Property.EVENT, "setAvrcpVolume")
@@ -382,6 +390,11 @@
                             .append(" index:").append(mVal1)
                             .append(" gain dB:").append(mVal2)
                             .toString();
+                case VOL_SET_LE_AUDIO_VOL:
+                    return new StringBuilder("setLeAudioVolume:")
+                            .append(" index:").append(mVal1)
+                            .append(" gain dB:").append(mVal2)
+                            .toString();
                 case VOL_SET_AVRCP_VOL:
                     return new StringBuilder("setAvrcpVolume:")
                             .append(" index:").append(mVal1)
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 52e8edf..c924fde 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -25,6 +25,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.Intent;
 import android.media.AudioDeviceAttributes;
@@ -63,6 +64,8 @@
 
     private @Nullable BluetoothHearingAid mHearingAid;
 
+    private @Nullable BluetoothLeAudio mLeAudio;
+
     // Reference to BluetoothA2dp to query for AbsoluteVolume.
     private @Nullable BluetoothA2dp mA2dp;
 
@@ -106,6 +109,8 @@
     private static final int SCO_MODE_MAX = 2;
 
     private static final int BT_HEARING_AID_GAIN_MIN = -128;
+    private static final int BT_LE_AUDIO_MIN_VOL = 0;
+    private static final int BT_LE_AUDIO_MAX_VOL = 255;
 
     /**
      * Returns a string representation of the scoAudioMode.
@@ -235,6 +240,8 @@
                     mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
             adapter.getProfileProxy(mDeviceBroker.getContext(),
                     mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
+            adapter.getProfileProxy(mDeviceBroker.getContext(),
+                    mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO);
         }
     }
 
@@ -389,6 +396,26 @@
         return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
     }
 
+    /*package*/ synchronized void setLeAudioVolume(int index, int maxIndex, int streamType) {
+        if (mLeAudio == null) {
+            if (AudioService.DEBUG_VOL) {
+                Log.i(TAG, "setLeAudioVolume: null mLeAudio");
+            }
+            return;
+        }
+        /* leaudio expect volume value in range 0 to 255
+         */
+        int volume = (index * (BT_LE_AUDIO_MAX_VOL - BT_LE_AUDIO_MIN_VOL)) / maxIndex ;
+
+        if (AudioService.DEBUG_VOL) {
+            Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
+                    + index + " volume=" + volume);
+        }
+        AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
+                AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
+        mLeAudio.setVolume(volume);
+    }
+
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
         if (mHearingAid == null) {
             if (AudioService.DEBUG_VOL) {
@@ -428,6 +455,7 @@
         mDeviceBroker.postDisconnectA2dpSink();
         mDeviceBroker.postDisconnectHeadset();
         mDeviceBroker.postDisconnectHearingAid();
+        mDeviceBroker.postDisconnectLeAudio();
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
@@ -488,6 +516,23 @@
                 /*eventSource*/ "mBluetoothProfileServiceListener");
     }
 
+    /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) {
+        mLeAudio = leAudio;
+        final List<BluetoothDevice> deviceList = mLeAudio.getConnectedDevices();
+        if (deviceList.isEmpty()) {
+            return;
+        }
+
+        final BluetoothDevice btDevice = deviceList.get(0);
+        final @BluetoothProfile.BtProfileState int state =
+        mLeAudio.getConnectionState(btDevice);
+        mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(
+                btDevice, state,
+                /*suppressNoisyIntent*/ false,
+                /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/
+                /*eventSource*/ "mBluetoothProfileServiceListener");
+    }
+
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
@@ -655,6 +700,13 @@
                             mDeviceBroker.postBtHearingAidProfileConnected(
                                     (BluetoothHearingAid) proxy);
                             break;
+
+                        case BluetoothProfile.LE_AUDIO:
+                            AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
+                                    "BT profile service: connecting LE_AUDIO profile"));
+                            mDeviceBroker.postBtLeAudioProfileConnected(
+                                    (BluetoothLeAudio) proxy);
+                            break;
                         default:
                             break;
                     }
@@ -677,6 +729,9 @@
                         case BluetoothProfile.HEARING_AID:
                             mDeviceBroker.postDisconnectHearingAid();
                             break;
+                        case BluetoothProfile.LE_AUDIO:
+                            mDeviceBroker.postDisconnectLeAudio();
+                            break;
 
                         default:
                             break;
@@ -899,6 +954,7 @@
         pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
         pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
         pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
+        pw.println("\n" + prefix + "mLeAudio: " + mLeAudio);
         pw.println(prefix + "mA2dp: " + mA2dp);
         pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
     }
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 091e6c4..a56a8ea 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -227,7 +227,7 @@
             subscriberId = tele.getSubscriberId();
             mNetworkTemplate = new NetworkTemplate(
                     NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
-                    null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
+                    null, NetworkStats.METERED_YES, NetworkStats.ROAMING_ALL,
                     NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL,
                     SUBSCRIBER_ID_MATCH_RULE_EXACT);
             mUsageCallback = new UsageCallback() {
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 565c9ae..243a336b 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1270,6 +1270,9 @@
             capsBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
         }
 
+        capsBuilder.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
+                ? Arrays.asList(mConfig.underlyingNetworks) : null);
+
         mNetworkCapabilities = capsBuilder.build();
         mNetworkAgent = new NetworkAgent(mContext, mLooper, NETWORKTYPE /* logtag */,
                 mNetworkCapabilities, lp,
@@ -1290,8 +1293,6 @@
         } finally {
             Binder.restoreCallingIdentity(token);
         }
-        mNetworkAgent.setUnderlyingNetworks((mConfig.underlyingNetworks != null)
-                ? Arrays.asList(mConfig.underlyingNetworks) : null);
         updateState(DetailedState.CONNECTED, "agentConnect");
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index d66d7ee..329a696 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -512,7 +512,10 @@
      */
     public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
         synchronized (mLock) {
-            mAlwaysRespectAppRequest = enabled;
+            if (mAlwaysRespectAppRequest != enabled) {
+                mAlwaysRespectAppRequest = enabled;
+                notifyDesiredDisplayModeSpecsChangedLocked();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index a2cb78d..c27293c 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -209,6 +209,12 @@
     void init() {
         assertRunOnServiceThread();
         mPreferredAddress = getPreferredAddress();
+        if (mHandler.hasMessages(MSG_DISABLE_DEVICE_TIMEOUT)) {
+            // Remove and trigger the queued message for clearing all actions when going to standby.
+            // This is necessary because the device may wake up before the message is triggered.
+            mHandler.removeMessages(MSG_DISABLE_DEVICE_TIMEOUT);
+            handleDisableDeviceTimeout();
+        }
         mPendingActionClearedCallback = null;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index e5a8a17d..a571dc0 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -3131,7 +3131,7 @@
                 Slog.v(TAG, "On standby-action cleared:" + device.mDeviceType);
                 devices.remove(device);
                 if (devices.isEmpty()) {
-                    onStandbyCompleted(standbyAction);
+                    onPendingActionsCleared(standbyAction);
                     // We will not clear local devices here, since some OEM/SOC will keep passing
                     // the received packets until the application processor enters to the sleep
                     // actually.
@@ -3193,10 +3193,17 @@
         mHdmiCecNetwork.clearLocalDevices();
     }
 
+    /**
+     * Normally called after all devices have cleared their pending actions, to execute the final
+     * phase of the standby flow.
+     *
+     * This can also be called during wakeup, when pending actions are cleared after failing to be
+     * cleared during standby. In this case, it does not execute the standby flow.
+     */
     @ServiceThreadOnly
-    private void onStandbyCompleted(int standbyAction) {
+    private void onPendingActionsCleared(int standbyAction) {
         assertRunOnServiceThread();
-        Slog.v(TAG, "onStandbyCompleted");
+        Slog.v(TAG, "onPendingActionsCleared");
 
         if (!mPowerStatusController.isPowerStatusTransientToStandby()) {
             return;
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 7afa81a..73de0f8 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -26,6 +26,7 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothLeAudio;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -56,6 +57,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
+    private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
 
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     // Maps hardware address to BluetoothRouteInfo
@@ -66,6 +68,8 @@
     BluetoothA2dp mA2dpProfile;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     BluetoothHearingAid mHearingAidProfile;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    BluetoothLeAudio mLeAudioProfile;
 
     // Route type -> volume map
     private final SparseIntArray mVolumeMap = new SparseIntArray();
@@ -108,6 +112,7 @@
     public void start(UserHandle user) {
         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
         mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
+        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
 
         // Bluetooth on/off broadcasts
         addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver());
@@ -119,6 +124,10 @@
                 deviceStateChangedReceiver);
         addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
                 deviceStateChangedReceiver);
+        addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED,
+                deviceStateChangedReceiver);
+        addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED,
+                deviceStateChangedReceiver);
 
         mContext.registerReceiverAsUser(mBroadcastReceiver, user,
                 mIntentFilter, null, null);
@@ -240,6 +249,8 @@
                 | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
                 | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
             routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
+        } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) {
+            routeType = MediaRoute2Info.TYPE_BLE_HEADSET;
         } else {
             return false;
         }
@@ -288,6 +299,12 @@
             routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
             type = MediaRoute2Info.TYPE_HEARING_AID;
         }
+        if (mLeAudioProfile != null
+                && mLeAudioProfile.getConnectedDevices().contains(device)) {
+            newBtRoute.connectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
+            routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device);
+            type = MediaRoute2Info.TYPE_BLE_HEADSET;
+        }
 
         // Current volume will be set when connected.
         newBtRoute.route = new MediaRoute2Info.Builder(routeId, deviceName)
@@ -358,11 +375,7 @@
         }
     }
 
-    private void addActiveHearingAidDevices(BluetoothDevice device) {
-        if (DEBUG) {
-            Log.d(TAG, "Setting active hearing aid devices. device=" + device);
-        }
-
+    private void addActiveDevices(BluetoothDevice device) {
         // Let the given device be the first active device
         BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
         addActiveRoute(activeBtRoute);
@@ -376,6 +389,21 @@
             }
         }
     }
+    private void addActiveHearingAidDevices(BluetoothDevice device) {
+        if (DEBUG) {
+            Log.d(TAG, "Setting active hearing aid devices. device=" + device);
+        }
+
+        addActiveDevices(device);
+    }
+
+    private void addActiveLeAudioDevices(BluetoothDevice device) {
+        if (DEBUG) {
+            Log.d(TAG, "Setting active le audio devices. device=" + device);
+        }
+
+        addActiveDevices(device);
+    }
 
     interface BluetoothRoutesUpdatedListener {
         void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
@@ -392,6 +420,11 @@
             if (connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
                 return MediaRoute2Info.TYPE_HEARING_AID;
             }
+
+            if (connectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) {
+                return MediaRoute2Info.TYPE_BLE_HEADSET;
+            }
+
             return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
         }
     }
@@ -410,6 +443,10 @@
                     mHearingAidProfile = (BluetoothHearingAid) proxy;
                     activeDevices = mHearingAidProfile.getActiveDevices();
                     break;
+                case BluetoothProfile.LE_AUDIO:
+                    mLeAudioProfile = (BluetoothLeAudio) proxy;
+                    activeDevices = mLeAudioProfile.getActiveDevices();
+                    break;
                 default:
                     return;
             }
@@ -434,6 +471,9 @@
                 case BluetoothProfile.HEARING_AID:
                     mHearingAidProfile = null;
                     break;
+                case BluetoothProfile.LE_AUDIO:
+                    mLeAudioProfile = null;
+                    break;
                 default:
                     return;
             }
@@ -490,12 +530,22 @@
                     }
                     notifyBluetoothRoutesUpdated();
                     break;
+                case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
+                    clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET);
+                    if (device != null) {
+                        addActiveLeAudioDevices(device);
+                    }
+                    notifyBluetoothRoutesUpdated();
+                    break;
                 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
                     handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
                     break;
                 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                     handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
                     break;
+                case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
+                    handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device);
+                    break;
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 91c40e0..1bdc9f3 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -3,11 +3,9 @@
 jsharkey@android.com
 jsharkey@google.com
 narayan@google.com
-patb@google.com
 svetoslavganov@android.com
 svetoslavganov@google.com
-toddke@android.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
 
 # apex support
 per-file ApexManager.java = dariofreni@google.com, ioffe@google.com, olilan@google.com
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f6acad0..827dfc0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7337,9 +7337,16 @@
 
             // Parse overlay configuration files to set default enable state, mutability, and
             // priority of system overlays.
+            final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
+            for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
+                for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+                    apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
+                }
+            }
             mOverlayConfig = OverlayConfig.initializeSystemInstance(
                     consumer -> mPmInternal.forEachPackage(
-                            pkg -> consumer.accept(pkg, pkg.isSystem())));
+                            pkg -> consumer.accept(pkg, pkg.isSystem(),
+                              apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
 
             // Prune any system packages that no longer exist.
             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index dab980a..7bea018 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -213,6 +213,7 @@
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
         NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.UWB_RANGING);
+        NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.NEARBY_WIFI_DEVICES);
     }
 
     private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
index e6f5826..41070f3 100644
--- a/services/core/java/com/android/server/security/OWNERS
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -1,3 +1,4 @@
 # Bug component: 36824
 
+per-file AttestationVerificationService.java = file:/core/java/android/security/attestationverification/OWNERS
 per-file FileIntegrityService.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
index 6366280..7ffff93 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/ConversionUtil.java
@@ -429,15 +429,7 @@
     private static @NonNull
     HidlMemory parcelFileDescriptorToHidlMemory(@Nullable ParcelFileDescriptor data, int dataSize) {
         if (dataSize > 0) {
-            // Extract a dup of the underlying FileDescriptor out of data.
-            FileDescriptor fd = new FileDescriptor();
-            try {
-                ParcelFileDescriptor dup = data.dup();
-                fd.setInt$(dup.detachFd());
-                return HidlMemoryUtil.fileDescriptorToHidlMemory(fd, dataSize);
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
+            return HidlMemoryUtil.fileDescriptorToHidlMemory(data.getFileDescriptor(), dataSize);
         } else {
             return HidlMemoryUtil.fileDescriptorToHidlMemory(null, 0);
         }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 61770ea..68b760a 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -29,6 +29,7 @@
 import static android.net.NetworkIdentity.OEM_PRIVATE;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.METERED_ALL;
+import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkTemplate.MATCH_ETHERNET;
 import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
@@ -1340,7 +1341,7 @@
     @Nullable private NetworkStats getUidNetworkStatsSnapshotForTransport(int transport) {
         final NetworkTemplate template = (transport == TRANSPORT_CELLULAR)
                 ? NetworkTemplate.buildTemplateMobileWithRatType(
-                /*subscriptionId=*/null, NETWORK_TYPE_ALL)
+                /*subscriptionId=*/null, NETWORK_TYPE_ALL, METERED_YES)
                 : NetworkTemplate.buildTemplateWifiWildcard();
         return getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
     }
@@ -1380,7 +1381,8 @@
         final List<NetworkStatsExt> ret = new ArrayList<>();
         for (final int ratType : getAllCollapsedRatTypes()) {
             final NetworkTemplate template =
-                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType);
+                    buildTemplateMobileWithRatType(subInfo.subscriberId, ratType,
+                    METERED_YES);
             final NetworkStats stats =
                     getUidNetworkStatsSnapshotForTemplate(template, /*includeTags=*/false);
             if (stats != null) {
@@ -1601,7 +1603,7 @@
 
     int pullBluetoothBytesTransferLocked(int atomTag, List<StatsEvent> pulledData) {
         BluetoothActivityEnergyInfo info = fetchBluetoothData();
-        if (info == null || info.getUidTraffic() == null) {
+        if (info == null) {
             return StatsManager.PULL_SKIP;
         }
         for (UidTraffic traffic : info.getUidTraffic()) {
@@ -2059,7 +2061,7 @@
         if (info == null) {
             return StatsManager.PULL_SKIP;
         }
-        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimeStamp(),
+        pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, info.getTimestampMillis(),
                 info.getBluetoothStackState(), info.getControllerTxTimeMillis(),
                 info.getControllerRxTimeMillis(), info.getControllerIdleTimeMillis(),
                 info.getControllerEnergyUsed()));
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 92e0845..f57a852 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1075,17 +1075,22 @@
             }
             if (shouldRecreateAudioPatch) {
                 mCommittedVolume = volume;
-                if (mAudioPatch != null) {
-                    mAudioManager.releaseAudioPatch(mAudioPatch);
-                }
-                mAudioManager.createAudioPatch(
+                // only recreate if  something was updated or audioPath is null
+                if (mAudioPatch == null || sinkUpdated ||sourceUpdated ) {
+                    if (mAudioPatch != null) {
+                        mAudioManager.releaseAudioPatch(mAudioPatch);
+                        audioPatchArray[0] = null;
+                    }
+                    mAudioManager.createAudioPatch(
                         audioPatchArray,
                         new AudioPortConfig[] { sourceConfig },
                         sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
-                mAudioPatch = audioPatchArray[0];
-                if (sourceGainConfig != null) {
-                    mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
+                    mAudioPatch = audioPatchArray[0];
                 }
+             }
+
+            if (sourceGainConfig != null) {
+                mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5540cc5..4857fb3 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1547,7 +1547,9 @@
                 // activity as it could lead to incorrect display metrics. For ex, IME services
                 // expect their config to match the config of the display with the IME window
                 // showing.
+                // If the configuration has been overridden by previous activity, empty it.
                 mIsActivityConfigOverrideAllowed = false;
+                unregisterActivityConfigurationListener();
                 break;
             default:
                 break;
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 2722b8d..710a304 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -165,7 +165,7 @@
         "android.hardware.power@1.1",
         "android.hardware.power-V2-cpp",
         "android.hardware.power.stats@1.0",
-        "android.hardware.power.stats-V1-ndk_platform",
+        "android.hardware.power.stats-V1-ndk",
         "android.hardware.thermal@1.0",
         "android.hardware.tv.input@1.0",
         "android.hardware.vibrator-V2-cpp",
@@ -178,7 +178,7 @@
         "android.frameworks.schedulerservice@1.0",
         "android.frameworks.sensorservice@1.0",
         "android.frameworks.stats@1.0",
-        "android.frameworks.stats-V1-ndk_platform",
+        "android.frameworks.stats-V1-ndk",
         "android.system.suspend.control-V1-cpp",
         "android.system.suspend.control.internal-cpp",
         "android.system.suspend-V1-ndk",
diff --git a/services/tests/PackageManagerServiceTests/OWNERS b/services/tests/PackageManagerServiceTests/OWNERS
index 182dfe8..86ae581 100644
--- a/services/tests/PackageManagerServiceTests/OWNERS
+++ b/services/tests/PackageManagerServiceTests/OWNERS
@@ -1,3 +1 @@
-chiuwinson@google.com
-patb@google.com
-toddke@google.com
+include /PACKAGE_MANAGER_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
index cb12ba7..a2ecbc3 100644
--- a/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BatteryServiceTest.java
@@ -47,7 +47,6 @@
     @Mock BatteryService.HealthServiceWrapper.IHealthSupplier mHealthServiceSupplier;
     BatteryService.HealthServiceWrapper mWrapper;
 
-    private static final String HEALTHD = BatteryService.HealthServiceWrapper.INSTANCE_HEALTHD;
     private static final String VENDOR = BatteryService.HealthServiceWrapper.INSTANCE_VENDOR;
 
     @Override
@@ -117,7 +116,7 @@
 
     @SmallTest
     public void testWrapPreferVendor() throws Exception {
-        initForInstances(VENDOR, HEALTHD);
+        initForInstances(VENDOR);
         mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
         waitHandlerThreadFinish();
         verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(VENDOR));
@@ -126,16 +125,6 @@
     }
 
     @SmallTest
-    public void testUseHealthd() throws Exception {
-        initForInstances(HEALTHD);
-        mWrapper.init(mCallback, mManagerSupplier, mHealthServiceSupplier);
-        waitHandlerThreadFinish();
-        verify(mCallback, times(1)).onRegistration(same(null), same(mMockedHal), eq(HEALTHD));
-        verify(mCallback, never()).onRegistration(same(mMockedHal), same(mMockedHal), anyString());
-        verify(mCallback, times(1)).onRegistration(same(mMockedHal), same(mMockedHal2), eq(HEALTHD));
-    }
-
-    @SmallTest
     public void testNoService() throws Exception {
         initForInstances("unrelated");
         try {
diff --git a/services/tests/servicestests/src/com/android/server/OWNERS b/services/tests/servicestests/src/com/android/server/OWNERS
index f1402ea..6a7d298 100644
--- a/services/tests/servicestests/src/com/android/server/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/OWNERS
@@ -3,4 +3,5 @@
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
 per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file BatteryServiceTest.java = file:platform/hardware/interfaces:/health/aidl/OWNERS
 per-file GestureLauncherServiceTest.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 255a612..a1bed29 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2945,7 +2945,12 @@
      * currently in use on the device for data transmission.
      *
      * If this object has been created with {@link #createForSubscriptionId}, applies to the given
-     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * subId. Otherwise, applies to {@link SubscriptionManager#getActiveDataSubscriptionId()}.
+     *
+     * Note: Before {@link SubscriptionManager#getActiveDataSubscriptionId()} was introduced in API
+     * level 30, it was applied to {@link SubscriptionManager#getDefaultDataSubscriptionId()} which
+     * may be different now from {@link SubscriptionManager#getActiveDataSubscriptionId()}, e.g.
+     * when opportunistic network is providing cellular internet connection to the user.
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index e890acb..9572154 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -36,6 +36,7 @@
 
 import java.util.Arrays;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
@@ -152,16 +153,9 @@
                     throw new RuntimeException(
                         "Failed to find NetworkScanInfo with id " + message.arg2);
                 }
-                NetworkScanCallback callback = nsi.mCallback;
-                Executor executor = nsi.mExecutor;
-                if (callback == null) {
-                    throw new RuntimeException(
-                        "Failed to find NetworkScanCallback with id " + message.arg2);
-                }
-                if (executor == null) {
-                    throw new RuntimeException(
-                        "Failed to find Executor with id " + message.arg2);
-                }
+
+                final NetworkScanCallback callback = nsi.mCallback;
+                final Executor executor = nsi.mExecutor;
 
                 switch (message.what) {
                     case CALLBACK_RESTRICTED_SCAN_RESULTS:
@@ -246,17 +240,24 @@
             NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
             String callingPackage, @Nullable String callingFeatureId) {
         try {
+            Objects.requireNonNull(request, "Request was null");
+            Objects.requireNonNull(callback, "Callback was null");
+            Objects.requireNonNull(executor, "Executor was null");
             final ITelephony telephony = getITelephony();
             if (telephony == null) return null;
 
-            int scanId = telephony.requestNetworkScan(
-                    subId, request, mMessenger, new Binder(), callingPackage,
-                    callingFeatureId);
-            if (scanId == INVALID_SCAN_ID) {
-                Rlog.e(TAG, "Failed to initiate network scan");
-                return null;
-            }
+            // The lock must be taken before calling requestNetworkScan because the resulting
+            // scanId can be invoked asynchronously on another thread at any time after
+            // requestNetworkScan invoked, leaving a critical section between that call and adding
+            // the record to the ScanInfo cache.
             synchronized (mScanInfo) {
+                int scanId = telephony.requestNetworkScan(
+                        subId, request, mMessenger, new Binder(), callingPackage,
+                        callingFeatureId);
+                if (scanId == INVALID_SCAN_ID) {
+                    Rlog.e(TAG, "Failed to initiate network scan");
+                    return null;
+                }
                 // We link to death whenever a scan is started to ensure that we are linked
                 // at the point that phone process death might matter.
                 // We never unlink because:
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index ec12040..5b44dba 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -43,6 +43,10 @@
     @VisibleForTesting
     static final int FPLMN_BYTE_SIZE = 3;
 
+    // ICCID used for tests by some OEMs
+    // TODO(b/159354974): Replace the constant here with UiccPortInfo.ICCID_REDACTED once ready
+    private static final String TEST_ICCID = "FFFFFFFFFFFFFFFFFFFF";
+
     // A table mapping from a number to a hex character for fast encoding hex strings.
     private static final char[] HEX_CHARS = {
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -923,6 +927,9 @@
      * Strip all the trailing 'F' characters of a string, e.g., an ICCID.
      */
     public static String stripTrailingFs(String s) {
+        if (TEST_ICCID.equals(s)) {
+            return s;
+        }
         return s == null ? null : s.replaceAll("(?i)f*$", "");
     }
 
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.bp b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
new file mode 100644
index 0000000..448d46f
--- /dev/null
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.bp
@@ -0,0 +1,60 @@
+//
+// Copyright 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_test_helper_library {
+    name: "DynamicCodeLoggerTestLibrary",
+    srcs: ["src/com/android/dcl/**/*.java"],
+
+}
+
+cc_library_shared {
+    name: "DynamicCodeLoggerNativeTestLibrary",
+    srcs: ["src/cpp/com_android_dcl_Jni.cpp"],
+    header_libs: ["jni_headers"],
+    sdk_version: "28",
+    stl: "c++_static",
+}
+
+cc_binary {
+    name: "DynamicCodeLoggerNativeExecutable",
+    srcs: ["src/cpp/test_executable.cpp"],
+}
+
+android_test {
+    name: "DynamicCodeLoggerIntegrationTests",
+
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+    certificate: "shared",
+    srcs: ["src/com/android/server/pm/**/*.java"],
+
+    static_libs: [
+        "androidx.test.rules",
+        "truth-prebuilt",
+    ],
+
+    compile_multilib: "both",
+    jni_libs: ["DynamicCodeLoggerNativeTestLibrary"],
+
+    java_resources: [
+        ":DynamicCodeLoggerTestLibrary",
+        ":DynamicCodeLoggerNativeExecutable",
+    ],
+}
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
deleted file mode 100644
index dab8304..0000000
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright 2017 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-# Build a tiny library that the test app can dynamically load
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerTestLibrary
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl)
-
-include $(BUILD_JAVA_LIBRARY)
-
-dynamiccodeloggertest_jar := $(LOCAL_BUILT_MODULE)
-
-
-# Also build a native library that the test app can dynamically load
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
-LOCAL_HEADER_LIBRARIES := jni_headers
-LOCAL_SDK_VERSION := 28
-LOCAL_NDK_STL_VARIANT := c++_static
-
-include $(BUILD_SHARED_LIBRARY)
-
-# And a standalone native executable that we can exec.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE := DynamicCodeLoggerNativeExecutable
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SRC_FILES := src/cpp/test_executable.cpp
-
-include $(BUILD_EXECUTABLE)
-
-dynamiccodeloggertest_executable := $(LOCAL_BUILT_MODULE)
-
-# Build the test app itself
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := DynamicCodeLoggerIntegrationTests
-LOCAL_SDK_VERSION := current
-LOCAL_COMPATIBILITY_SUITE := device-tests
-LOCAL_CERTIFICATE := shared
-LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/server/pm)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.rules \
-    truth-prebuilt \
-
-# Include both versions of the .so if we have 2 arch
-LOCAL_MULTILIB := both
-LOCAL_JNI_SHARED_LIBRARIES := \
-    DynamicCodeLoggerNativeTestLibrary \
-
-# This gets us the javalib.jar built by DynamicCodeLoggerTestLibrary above as well as the various
-# native binaries.
-LOCAL_JAVA_RESOURCE_FILES := \
-    $(dynamiccodeloggertest_jar) \
-    $(dynamiccodeloggertest_executable) \
-
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-include $(BUILD_PACKAGE)
diff --git a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
index 883c172..5430dee 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
+++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java
@@ -114,7 +114,8 @@
         // Obtained via "echo -n copied.jar | sha256sum"
         String expectedNameHash =
                 "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
-        String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
+        String expectedContentHash = copyAndHashResource(
+                "/DynamicCodeLoggerTestLibrary.jar", privateCopyFile);
 
         // Feed the jar to a class loader and make sure it contains what we expect.
         ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
@@ -135,7 +136,8 @@
         File privateCopyFile = privateFile("copied2.jar");
         String expectedNameHash =
                 "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
-        String expectedContentHash = copyAndHashResource("/javalib.jar", privateCopyFile);
+        String expectedContentHash = copyAndHashResource(
+                "/DynamicCodeLoggerTestLibrary.jar", privateCopyFile);
 
         // This time make sure an unknown class loader is an ancestor of the class loader we use.
         ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
diff --git a/tests/LockTaskTests/Android.bp b/tests/LockTaskTests/Android.bp
new file mode 100644
index 0000000..dce681e
--- /dev/null
+++ b/tests/LockTaskTests/Android.bp
@@ -0,0 +1,32 @@
+// 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 {
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+    name: "LockTaskTests",
+
+    privileged: true,
+
+    sdk_version: "current",
+    certificate: "platform",
+
+    srcs: [
+        "src/**/I*.aidl",
+        "src/**/*.java",
+    ],
+
+}
diff --git a/tests/LockTaskTests/Android.mk b/tests/LockTaskTests/Android.mk
deleted file mode 100644
index 5406ee1..0000000
--- a/tests/LockTaskTests/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(PRODUCT_OUT)/system/priv-app
-
-LOCAL_PACKAGE_NAME := LockTaskTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../NOTICE
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-
-LOCAL_SRC_FILES := $(call all-Iaidl-files-under, src) $(call all-java-files-under, src)
-
-include $(BUILD_PACKAGE)
-
-# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))