Merge changes from topic "cellbroadcastintents"

* changes:
  Do not modify passed in intent
  Add API to send background intent
diff --git a/Android.bp b/Android.bp
index 2af7ca4..ac1cd47 100644
--- a/Android.bp
+++ b/Android.bp
@@ -366,7 +366,9 @@
 
     sdk_version: "core_platform",
     libs: [
+        "app-compat-annotations",
         "ext",
+        "unsupportedappusage",
         "updatable_media_stubs",
     ],
 
@@ -426,7 +428,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: ["app-compat-annotations"],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -468,14 +469,16 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
     installable: false,
-    libs: ["app-compat-annotations"],
 }
 
 java_library {
     name: "framework-annotation-proc",
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
-    libs: ["app-compat-annotations"],
+    libs: [
+        "app-compat-annotations",
+        "unsupportedappusage",
+    ],
     installable: false,
     plugins: [
         "unsupportedappusage-annotation-processor",
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp
index 0aacebe..7536def 100644
--- a/apex/sdkext/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkext/derive_sdk/derive_sdk.cpp
@@ -68,7 +68,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("persist.com.android.sdkext.sdk_info", prop_value)) {
+    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
index 331ef21..d3b9397 100644
--- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
     }
 
     /**
diff --git a/api/current.txt b/api/current.txt
index 23b5bc6..d2d63af 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28903,6 +28903,7 @@
 
   public class NetworkRequest implements android.os.Parcelable {
     method public int describeContents();
+    method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
     method public boolean hasCapability(int);
     method public boolean hasTransport(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -45499,6 +45500,7 @@
     field public static final int TYPE_MCX = 1024; // 0x400
     field public static final int TYPE_MMS = 2; // 0x2
     field public static final int TYPE_SUPL = 4; // 0x4
+    field public static final int TYPE_XCAP = 2048; // 0x800
   }
 
   public static class ApnSetting.Builder {
@@ -46808,7 +46810,7 @@
     field public static final long WEEK_IN_MILLIS = 604800000L; // 0x240c8400L
     field public static final String YEAR_FORMAT = "%Y";
     field public static final String YEAR_FORMAT_TWO_DIGITS = "%g";
-    field public static final long YEAR_IN_MILLIS = 31449600000L; // 0x7528ad000L
+    field @Deprecated public static final long YEAR_IN_MILLIS = 31449600000L; // 0x7528ad000L
     field @Deprecated public static final int[] sameMonthTable;
     field @Deprecated public static final int[] sameYearTable;
   }
@@ -48245,6 +48247,13 @@
     ctor public Base64OutputStream(java.io.OutputStream, int);
   }
 
+  public final class CloseGuard {
+    ctor public CloseGuard();
+    method public void close();
+    method public void open(@NonNull String);
+    method public void warnIfOpen();
+  }
+
   @Deprecated public final class Config {
     field @Deprecated public static final boolean DEBUG = false;
     field @Deprecated public static final boolean LOGD = true;
diff --git a/api/system-current.txt b/api/system-current.txt
index 0639e63..bf0bfb6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1343,15 +1343,24 @@
 
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean cancelBondProcess();
+    method public boolean cancelPairing();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getBatteryLevel();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getMessageAccessPermission();
     method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public byte[] getMetadata(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getPhonebookAccessPermission();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getSimAccessPermission();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isBondingInitiatedLocally();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isConnected();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPin(@Nullable String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSilenceMode(boolean);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setSimAccessPermission(int);
     field public static final int ACCESS_ALLOWED = 1; // 0x1
     field public static final int ACCESS_REJECTED = 2; // 0x2
     field public static final int ACCESS_UNKNOWN = 0; // 0x0
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index c64e3d8..432a5fd 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -144,6 +144,16 @@
 ProtectedMember: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context):
     
 
+PublicTypedef: android.content.integrity.AtomicFormula.Key: Don't expose @IntDef: @Key must be hidden.
+
+PublicTypedef: android.content.integrity.AtomicFormula.Operator: Don't expose @IntDef: @Operator must be hidden.
+
+PublicTypedef: android.content.integrity.CompoundFormula.Connector: Don't expose @IntDef: @Connector must be hidden.
+
+PublicTypedef: android.content.integrity.Formula.Tag: Don't expose @IntDef: @Tag must be hidden.
+
+PublicTypedef: android.content.integrity.Rule.Effect: Don't expose @IntDef: @Effect must be hidden.
+
 
 SamShouldBeLast: android.accounts.AccountManager#addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler):
     
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index bf21ce7..3af392c 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -2444,6 +2444,12 @@
 ProtectedMember: android.view.ViewGroup#resetResolvedDrawables():
     
 
+PublicTypedef: android.os.HwParcel.Status: Don't expose @IntDef: @Status must be hidden.
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability: Don't expose @IntDef: @MmTelCapability must be hidden.
+
+PublicTypedef: android.telephony.ims.feature.MmTelFeature.ProcessCallResult: Don't expose @IntDef: @ProcessCallResult must be hidden.
+
 
 RawAidl: android.telephony.mbms.vendor.MbmsDownloadServiceBase:
     
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 1173d57..3a26063 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -22,6 +22,7 @@
         "android.test.runner",
         "junit",
         "android.test.base",
+        "unsupportedappusage",
     ],
     custom_template: "droiddoc-templates-sdk",
     installable: false,
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index 471606da..55f92be 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -56,6 +56,7 @@
 
     public ManualTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) {
         mUtcTime = Objects.requireNonNull(utcTime);
+        Objects.requireNonNull(utcTime.getValue());
     }
 
     private static ManualTimeSuggestion createFromParcel(Parcel in) {
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index dd02af7..4a89a12 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -166,7 +166,12 @@
         }
 
         /** Returns the builder for call chaining. */
-        public Builder setUtcTime(TimestampedValue<Long> utcTime) {
+        public Builder setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
+            if (utcTime != null) {
+                // utcTime can be null, but the value it holds cannot.
+                Objects.requireNonNull(utcTime.getValue());
+            }
+
             mUtcTime = utcTime;
             return this;
         }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 49187dc..323c7d1 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -33,8 +34,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.UUID;
 
 /**
@@ -771,6 +776,13 @@
     @UnsupportedAppUsage
     public static final String EXTRA_SDP_SEARCH_STATUS =
             "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
+
+    /** @hide */
+    @IntDef(prefix = "ACCESS_", value = {ACCESS_UNKNOWN,
+            ACCESS_ALLOWED, ACCESS_REJECTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AccessPermission{}
+
     /**
      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
@@ -1096,15 +1108,14 @@
 
     /**
      * Get the most recent identified battery level of this Bluetooth device
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      *
      * @return Battery level in percents from 0 to 100, or {@link #BATTERY_LEVEL_UNKNOWN} if
      * Bluetooth is disabled, or device is disconnected, or does not have any battery reporting
      * service, or return value is invalid
      * @hide
      */
+    @SystemApi
     @RequiresPermission(Manifest.permission.BLUETOOTH)
-    @UnsupportedAppUsage
     public int getBatteryLevel() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1187,8 +1198,15 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
+    /**
+     * Gets whether bonding was initiated locally
+     *
+     * @return true if bonding is initiated locally, false otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public boolean isBondingInitiatedLocally() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1480,15 +1498,20 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean setPasskey(int passkey) {
-        //TODO(BT)
-        /*
-        try {
-            return sService.setPasskey(this, true, 4, passkey);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
+    /**
+     * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
+     *
+     * @return true pin has been set false for error
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public boolean setPin(@Nullable String pin) {
+        byte[] pinBytes = convertPinToBytes(pin);
+        if (pinBytes == null) {
+            return false;
+        }
+        return setPin(pinBytes);
     }
 
     /**
@@ -1511,22 +1534,18 @@
         return false;
     }
 
-    /** @hide */
-    public boolean setRemoteOutOfBandData() {
-        // TODO(BT)
-        /*
-        try {
-          return sService.setRemoteOutOfBandData(this);
-      } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean cancelPairingUserInput() {
+    /**
+     * Cancels pairing to this device
+     *
+     * @return true if pairing cancelled successfully, false otherwise
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean cancelPairing() {
         final IBluetooth service = sService;
         if (service == null) {
-            Log.e(TAG, "BT not enabled. Cannot create pairing user input");
+            Log.e(TAG, "BT not enabled. Cannot cancel pairing");
             return false;
         }
         try {
@@ -1537,17 +1556,6 @@
         return false;
     }
 
-    /** @hide */
-    @UnsupportedAppUsage
-    public boolean isBluetoothDock() {
-        // TODO(BT)
-        /*
-        try {
-            return sService.isBluetoothDock(this);
-        } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-        return false;
-    }
-
     boolean isBluetoothEnabled() {
         boolean ret = false;
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1558,13 +1566,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether the phonebook access is allowed for this bluetooth device
      *
      * @return Whether the phonebook access is allowed to this device. Can be {@link
      * #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
     public int getPhonebookAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1667,14 +1676,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether message access is allowed to this bluetooth device
      *
-     * @return Whether the message access is allowed to this device. Can be {@link #ACCESS_UNKNOWN},
-     * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @return Whether the message access is allowed to this device.
      * @hide
      */
-    @UnsupportedAppUsage
-    public int getMessageAccessPermission() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public @AccessPermission int getMessageAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
             return ACCESS_UNKNOWN;
@@ -1689,15 +1698,18 @@
 
     /**
      * Sets whether the message access is allowed to this device.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
      *
-     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
-     * #ACCESS_REJECTED}.
+     * @param value is the value we are setting the message access permission to
      * @return Whether the value has been successfully set.
      * @hide
      */
-    @UnsupportedAppUsage
-    public boolean setMessageAccessPermission(int value) {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setMessageAccessPermission(@AccessPermission int value) {
+        // Validates param value is one of the accepted constants
+        if (value != ACCESS_ALLOWED && value != ACCESS_REJECTED && value != ACCESS_UNKNOWN) {
+            throw new IllegalArgumentException(value + "is not a valid AccessPermission value");
+        }
         final IBluetooth service = sService;
         if (service == null) {
             return false;
@@ -1711,13 +1723,14 @@
     }
 
     /**
-     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * Gets whether sim access is allowed for this bluetooth device
      *
-     * @return Whether the Sim access is allowed to this device. Can be {@link #ACCESS_UNKNOWN},
-     * {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @return Whether the Sim access is allowed to this device.
      * @hide
      */
-    public int getSimAccessPermission() {
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public @AccessPermission int getSimAccessPermission() {
         final IBluetooth service = sService;
         if (service == null) {
             return ACCESS_UNKNOWN;
@@ -1732,14 +1745,14 @@
 
     /**
      * Sets whether the Sim access is allowed to this device.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
      *
      * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link
      * #ACCESS_REJECTED}.
      * @return Whether the value has been successfully set.
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setSimAccessPermission(int value) {
         final IBluetooth service = sService;
         if (service == null) {
@@ -1970,7 +1983,7 @@
      * @return the pin code as a UTF-8 byte array, or null if it is an invalid Bluetooth pin.
      * @hide
      */
-    @UnsupportedAppUsage
+    @VisibleForTesting
     public static byte[] convertPinToBytes(String pin) {
         if (pin == null) {
             return null;
diff --git a/core/java/android/content/pm/ParceledListSlice.aidl b/core/java/android/content/pm/ParceledListSlice.aidl
index c02cc6a..5031fba 100644
--- a/core/java/android/content/pm/ParceledListSlice.aidl
+++ b/core/java/android/content/pm/ParceledListSlice.aidl
@@ -16,4 +16,4 @@
 
 package android.content.pm;
 
-parcelable ParceledListSlice;
+parcelable ParceledListSlice<T>;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 4270740..471b23e 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
@@ -461,6 +462,14 @@
         return networkCapabilities.hasTransport(transportType);
     }
 
+    /**
+     * @see Builder#setNetworkSpecifier(NetworkSpecifier)
+     */
+    @Nullable
+    public NetworkSpecifier getNetworkSpecifier() {
+        return networkCapabilities.getNetworkSpecifier();
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 450bfae..1130f1d 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -519,11 +519,12 @@
      * @param appDataDir null-ok the data directory of the app.
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
-     *
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws RuntimeException on fatal start failure
-     * 
+     *
      * {@hide}
      */
     public static ProcessStartResult start(@NonNull final String processClass,
@@ -538,11 +539,12 @@
                                            @Nullable String appDataDir,
                                            @Nullable String invokeWith,
                                            @Nullable String packageName,
+                                           @Nullable long[] disabledCompatChanges,
                                            @Nullable String[] zygoteArgs) {
         return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    /*useUsapPool=*/ true, zygoteArgs);
+                    /*useUsapPool=*/ true, disabledCompatChanges, zygoteArgs);
     }
 
     /** @hide */
@@ -558,11 +560,12 @@
                                                   @Nullable String appDataDir,
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
+                                                  @Nullable long[] disabledCompatChanges,
                                                   @Nullable String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, packageName,
-                    /*useUsapPool=*/ false, zygoteArgs);
+                    /*useUsapPool=*/ false, disabledCompatChanges, zygoteArgs);
     }
 
     /**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 3a55aff..c2d3ecc 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -306,6 +306,8 @@
      * @param appDataDir null-ok the data directory of the app.
      * @param invokeWith null-ok the command to invoke with.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
+     *                             started.
      * @param zygoteArgs Additional arguments to supply to the zygote process.
      *
      * @return An object that describes the result of the attempt to start the process.
@@ -323,6 +325,7 @@
                                                   @Nullable String invokeWith,
                                                   @Nullable String packageName,
                                                   boolean useUsapPool,
+                                                  @Nullable long[] disabledCompatChanges,
                                                   @Nullable String[] zygoteArgs) {
         // TODO (chriswailes): Is there a better place to check this value?
         if (fetchUsapPoolEnabledPropWithMinInterval()) {
@@ -333,7 +336,7 @@
             return startViaZygote(processClass, niceName, uid, gid, gids,
                     runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/ false,
-                    packageName, useUsapPool, zygoteArgs);
+                    packageName, useUsapPool, disabledCompatChanges, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
                     "Starting VM process through Zygote failed");
@@ -534,6 +537,7 @@
      * @param startChildZygote Start a sub-zygote. This creates a new zygote process
      * that has its state cloned from this zygote process.
      * @param packageName null-ok the name of the package this process belongs to.
+     * @param disabledCompatChanges a list of disabled compat changes for the process being started.
      * @param extraArgs Additional arguments to supply to the zygote process.
      * @return An object that describes the result of the attempt to start the process.
      * @throws ZygoteStartFailedEx if process start failed for any reason
@@ -552,6 +556,7 @@
                                                       boolean startChildZygote,
                                                       @Nullable String packageName,
                                                       boolean useUsapPool,
+                                                      @Nullable long[] disabledCompatChanges,
                                                       @Nullable String[] extraArgs)
                                                       throws ZygoteStartFailedEx {
         ArrayList<String> argsForZygote = new ArrayList<>();
@@ -623,6 +628,21 @@
             argsForZygote.add("--package-name=" + packageName);
         }
 
+        if (disabledCompatChanges != null && disabledCompatChanges.length > 0) {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("--disabled-compat-changes=");
+
+            final int sz = disabledCompatChanges.length;
+            for (int i = 0; i < sz; i++) {
+                if (i != 0) {
+                    sb.append(',');
+                }
+                sb.append(disabledCompatChanges[i]);
+            }
+
+            argsForZygote.add(sb.toString());
+        }
+
         argsForZygote.add(processClass);
 
         if (extraArgs != null) {
@@ -1170,7 +1190,8 @@
                     gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
                     abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
                     true /* startChildZygote */, null /* packageName */,
-                    false /* useUsapPool */, extraArgs);
+                    false /* useUsapPool */,
+                    null /* disabledCompatChanges */, extraArgs);
         } catch (ZygoteStartFailedEx ex) {
             throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
         }
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index e94b800..90625183 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -56,8 +56,12 @@
     public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
     public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
     /**
-     * This constant is actually the length of 364 days, not of a year!
+     * @deprecated Not all years have the same number of days, and this constant is actually the
+     * length of 364 days. Please use other date/time constructs such as
+     * {@link java.util.concurrent.TimeUnit}, {@link java.util.Calendar} or
+     * {@link java.time.Duration} instead.
      */
+    @Deprecated
     public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
 
     // The following FORMAT_* symbols are used for specifying the format of
diff --git a/core/java/android/util/CloseGuard.java b/core/java/android/util/CloseGuard.java
new file mode 100644
index 0000000..c39a6c9
--- /dev/null
+++ b/core/java/android/util/CloseGuard.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import android.annotation.NonNull;
+
+/**
+ * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
+ * resources that should have been cleaned up by explicit close
+ * methods (aka "explicit termination methods" in Effective Java).
+ * <p>
+ * A simple example: <pre>   {@code
+ *   class Foo {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Foo() {
+ *           ...;
+ *           guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * In usage where the resource to be explicitly cleaned up is
+ * allocated after object construction, CloseGuard protection can
+ * be deferred. For example: <pre>   {@code
+ *   class Bar {
+ *
+ *       private final CloseGuard guard = CloseGuard.get();
+ *
+ *       ...
+ *
+ *       public Bar() {
+ *           ...;
+ *       }
+ *
+ *       public void connect() {
+ *          ...;
+ *          guard.open("cleanup");
+ *       }
+ *
+ *       public void cleanup() {
+ *          guard.close();
+ *          ...;
+ *          Reference.reachabilityFence(this);
+ *          // For full correctness in the absence of a close() call, other methods may also need
+ *          // reachabilityFence() calls.
+ *       }
+ *
+ *       protected void finalize() throws Throwable {
+ *           try {
+ *               // Note that guard could be null if the constructor threw.
+ *               if (guard != null) {
+ *                   guard.warnIfOpen();
+ *               }
+ *               cleanup();
+ *           } finally {
+ *               super.finalize();
+ *           }
+ *       }
+ *   }
+ * }</pre>
+ *
+ * When used in a constructor, calls to {@code open} should occur at
+ * the end of the constructor since an exception that would cause
+ * abrupt termination of the constructor will mean that the user will
+ * not have a reference to the object to cleanup explicitly. When used
+ * in a method, the call to {@code open} should occur just after
+ * resource acquisition.
+ */
+public final class CloseGuard {
+    private final dalvik.system.CloseGuard mImpl;
+
+    /**
+     * Constructs a new CloseGuard instance.
+     * {@link #open(String)} can be used to set up the instance to warn on failure to close.
+     */
+    public CloseGuard() {
+        mImpl = dalvik.system.CloseGuard.get();
+    }
+
+    /**
+     * Initializes the instance with a warning that the caller should have explicitly called the
+     * {@code closeMethodName} method instead of relying on finalization.
+     *
+     * @param closeMethodName non-null name of explicit termination method. Printed by warnIfOpen.
+     * @throws NullPointerException if closeMethodName is null.
+     */
+    public void open(@NonNull String closeMethodName) {
+        mImpl.open(closeMethodName);
+    }
+
+    /** Marks this CloseGuard instance as closed to avoid warnings on finalization. */
+    public void close() {
+        mImpl.close();
+    }
+
+    /**
+     * Logs a warning if the caller did not properly cleanup by calling an explicit close method
+     * before finalization.
+     */
+    public void warnIfOpen() {
+        mImpl.warnIfOpen();
+    }
+}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index a211871..fa823c4 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -367,8 +367,8 @@
         if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
-    protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
-            ClassLoader classLoader) {
+    protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
+            String[] argv, ClassLoader classLoader) {
         // If the application calls System.exit(), terminate the process
         // immediately without running any shutdown hooks.  It is not possible to
         // shutdown an Android application gracefully.  Among other things, the
@@ -377,6 +377,7 @@
         nativeSetExitWithoutCleanup(true);
 
         VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
+        VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
 
         final Arguments args = new Arguments(argv);
 
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index f0e7796..790d7f7 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -23,16 +23,18 @@
 import android.system.OsConstants;
 import android.system.StructCapUserData;
 import android.system.StructCapUserHeader;
-import android.util.TimingsTraceLog;
 import android.util.Slog;
+import android.util.TimingsTraceLog;
+
 import dalvik.system.VMRuntime;
+
+import libcore.io.IoUtils;
+
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 
-import libcore.io.IoUtils;
-
 /**
  * Startup class for the wrapper process.
  * @hide
@@ -166,10 +168,10 @@
             System.arraycopy(argv, 2, removedArgs, 0, argv.length - 2);
             argv = removedArgs;
         }
-
         // Perform the same initialization that would happen after the Zygote forks.
         Zygote.nativePreApplicationInit();
-        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+        return RuntimeInit.applicationInit(targetSdkVersion, /*disabledCompatChanges*/ null,
+                argv, classLoader);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 00ab45e..33adec1 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -642,6 +642,7 @@
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
             return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
+                                         args.mDisabledCompatChanges,
                                          args.mRemainingArgs,
                                          null /* classLoader */);
         } finally {
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index fc55ccf..3915ba2 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -210,6 +210,12 @@
     int mHiddenApiAccessStatslogSampleRate = -1;
 
     /**
+     * A set of disabled app compatibility changes for the running app. From
+     * --disabled-compat-changes.
+     */
+    long[] mDisabledCompatChanges = null;
+
+    /**
      * Constructs instance and parses args
      *
      * @param args zygote command-line args
@@ -416,6 +422,16 @@
                 mUsapPoolStatusSpecified = true;
                 mUsapPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1));
                 expectRuntimeArgs = false;
+            } else if (arg.startsWith("--disabled-compat-changes=")) {
+                if (mDisabledCompatChanges != null) {
+                    throw new IllegalArgumentException("Duplicate arg specified");
+                }
+                final String[] params = arg.substring(arg.indexOf('=') + 1).split(",");
+                final int length = params.length;
+                mDisabledCompatChanges = new long[length];
+                for (int i = 0; i < length; i++) {
+                    mDisabledCompatChanges[i] = Long.parseLong(params[i]);
+                }
             } else {
                 break;
             }
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index b15e1ef..4c37591 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -502,6 +502,7 @@
         } else {
             if (!isZygote) {
                 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+                        parsedArgs.mDisabledCompatChanges,
                         parsedArgs.mRemainingArgs, null /* classLoader */);
             } else {
                 return ZygoteInit.childZygoteInit(parsedArgs.mTargetSdkVersion,
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 93e6102..7b77a92 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -547,6 +547,7 @@
              * Pass the remaining arguments to SystemServer.
              */
             return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
+                    parsedArgs.mDisabledCompatChanges,
                     parsedArgs.mRemainingArgs, cl);
         }
 
@@ -972,14 +973,16 @@
      *
      * Current recognized args:
      * <ul>
-     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
+     * <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
      * </ul>
      *
      * @param targetSdkVersion target SDK version
-     * @param argv arg strings
+     * @param disabledCompatChanges set of disabled compat changes for the process (all others
+     *                              are enabled)
+     * @param argv             arg strings
      */
-    public static final Runnable zygoteInit(int targetSdkVersion, String[] argv,
-            ClassLoader classLoader) {
+    public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
+            String[] argv, ClassLoader classLoader) {
         if (RuntimeInit.DEBUG) {
             Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
         }
@@ -989,7 +992,8 @@
 
         RuntimeInit.commonInit();
         ZygoteInit.nativeZygoteInit();
-        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
+                classLoader);
     }
 
     /**
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index b906d84..ed613c3 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -176,14 +176,12 @@
                         mDevice.setPin(mPin);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
-                        mDevice.setPasskey(mPasskey);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
                     case BluetoothDevice.PAIRING_VARIANT_CONSENT:
                         mDevice.setPairingConfirmation(true);
                         break;
                     case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
-                        mDevice.setRemoteOutOfBandData();
                         break;
                 }
             } else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) {
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1670d49..4d4f447 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -24,6 +24,7 @@
     ],
     static_libs: [
         "frameworks-base-testutils",
+        "core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
         "core-tests-support",
         "android-common",
         "frameworks-core-util-lib",
diff --git a/core/tests/coretests/src/android/util/CloseGuardTest.java b/core/tests/coretests/src/android/util/CloseGuardTest.java
new file mode 100644
index 0000000..d86c7b7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/CloseGuardTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 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.util;
+
+import libcore.dalvik.system.CloseGuardSupport;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+
+/** Unit tests for {@link android.util.CloseGuard} */
+public class CloseGuardTest {
+
+    @Rule
+    public final TestRule rule = CloseGuardSupport.getRule();
+
+    @Test
+    public void testEnabled_NotOpen() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        assertUnreleasedResources(owner, 0);
+    }
+
+    @Test
+    public void testEnabled_OpenNotClosed() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        owner.open();
+        assertUnreleasedResources(owner, 1);
+    }
+
+    @Test
+    public void testEnabled_OpenThenClosed() throws Throwable {
+        ResourceOwner owner = new ResourceOwner();
+        owner.open();
+        owner.close();
+        assertUnreleasedResources(owner, 0);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testOpen_withNullMethodName_throwsNPE() throws Throwable {
+        CloseGuard closeGuard = new CloseGuard();
+        closeGuard.open(null);
+    }
+
+    private void assertUnreleasedResources(ResourceOwner owner, int expectedCount)
+            throws Throwable {
+        try {
+            CloseGuardSupport.getFinalizerChecker().accept(owner, expectedCount);
+        } finally {
+            // Close the resource so that CloseGuard does not generate a warning for real when it
+            // is actually finalized.
+            owner.close();
+        }
+    }
+
+    /**
+     * A test user of {@link CloseGuard}.
+     */
+    private static class ResourceOwner {
+
+        private final CloseGuard mCloseGuard;
+
+        ResourceOwner() {
+            mCloseGuard = new CloseGuard();
+        }
+
+        public void open() {
+            mCloseGuard.open("close");
+        }
+
+        public void close() {
+            mCloseGuard.close();
+        }
+
+        /**
+         * Make finalize public so that it can be tested directly without relying on garbage
+         * collection to trigger it.
+         */
+        @Override
+        public void finalize() throws Throwable {
+            mCloseGuard.warnIfOpen();
+            super.finalize();
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 56be3bb..abfee1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -164,9 +164,7 @@
 
     public void disconnect() {
         synchronized (mProfileLock) {
-            for (LocalBluetoothProfile profile : mProfiles) {
-                disconnect(profile);
-            }
+            mLocalAdapter.disconnectAllEnabledProfiles(mDevice);
         }
         // Disconnect  PBAP server in case its connected
         // This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -645,12 +643,8 @@
 
         refresh();
 
-        if (bondState == BluetoothDevice.BOND_BONDED) {
-            if (mDevice.isBluetoothDock()) {
-                onBondingDockConnect();
-            } else if (mDevice.isBondingInitiatedLocally()) {
-                connect(false);
-            }
+        if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) {
+            connect(false);
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
index 7162121..387bae1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OWNERS
@@ -1,8 +1,7 @@
 # Default reviewers for this and subdirectories.
-asapperstein@google.com
-asargent@google.com
-eisenbach@google.com
-jackqdyulei@google.com
 siyuanh@google.com
+hughchen@google.com
+timhypeng@google.com
+robertluo@google.com
 
-# Emergency approvers in case the above are not available
\ No newline at end of file
+# Emergency approvers in case the above are not available
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 7e8721d..3c953b3 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -31,6 +31,7 @@
         "android.hardware.tetheroffload.control-V1.0-java",
         "tethering-client",
     ],
+    libs: ["unsupportedappusage"],
     manifest: "AndroidManifestBase.xml",
 }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 753c117..b719435 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5573,7 +5573,7 @@
      * @param linkProperties the initial link properties of this network. They can be updated
      *         later : see {@link #updateLinkProperties}.
      * @param networkCapabilities the initial capabilites of this network. They can be updated
-     *         later : see {@link #updateNetworkCapabilities}.
+     *         later : see {@link #updateCapabilities}.
      * @param currentScore the initial score of the network. See
      *         {@link NetworkAgentInfo#getCurrentScore}.
      * @param networkMisc metadata about the network. This is never updated.
@@ -5596,7 +5596,7 @@
                 ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
                 mDnsResolver, mNMS, factorySerialNumber);
         // Make sure the network capabilities reflect what the agent info says.
-        nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
+        nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
         final String name = TextUtils.isEmpty(extraInfo)
                 ? nai.networkCapabilities.getSSID() : extraInfo;
@@ -5950,11 +5950,7 @@
             }
         }
 
-        final NetworkCapabilities prevNc;
-        synchronized (nai) {
-            prevNc = nai.networkCapabilities;
-            nai.setNetworkCapabilities(newNc);
-        }
+        final NetworkCapabilities prevNc = nai.getAndSetNetworkCapabilities(newNc);
 
         updateUids(nai, prevNc, newNc);
 
@@ -5963,7 +5959,7 @@
             // the change we're processing can't affect any requests, it can only affect the listens
             // on this network. We might have been called by rematchNetworkAndRequests when a
             // network changed foreground state.
-            processListenRequests(nai, true);
+            processListenRequests(nai);
         } else {
             // If the requestable capabilities have changed or the score changed, we can't have been
             // called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -6271,8 +6267,14 @@
         updateAllVpnsCapabilities();
     }
 
-    private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
+    private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
         // For consistency with previous behaviour, send onLost callbacks before onAvailable.
+        processNewlyLostListenRequests(nai);
+        notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
+        processNewlySatisfiedListenRequests(nai);
+    }
+
+    private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             NetworkRequest nr = nri.request;
             if (!nr.isListen()) continue;
@@ -6281,11 +6283,9 @@
                 callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
             }
         }
+    }
 
-        if (capabilitiesChanged) {
-            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
-        }
-
+    private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             NetworkRequest nr = nri.request;
             if (!nr.isListen()) continue;
@@ -6468,19 +6468,20 @@
         // before LegacyTypeTracker sends legacy broadcasts
         for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
 
-        // Second pass: process all listens.
-        if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
-            // TODO : most of the following is useless because the only thing that changed
-            // here is whether the network is a background network. Clean this up.
+        // Finally, process listen requests and update capabilities if the background state has
+        // changed for this network. For consistency with previous behavior, send onLost callbacks
+        // before onAvailable.
+        processNewlyLostListenRequests(newNetwork);
 
-            NetworkCapabilities newNc = mixInCapabilities(newNetwork,
+        // Maybe the network changed background states. Update its capabilities.
+        final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork();
+        if (backgroundChanged) {
+            final NetworkCapabilities newNc = mixInCapabilities(newNetwork,
                     newNetwork.networkCapabilities);
 
-            if (Objects.equals(newNetwork.networkCapabilities, newNc)) return;
-
             final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
             final int newPermission = getNetworkPermission(newNc);
-            if (oldPermission != newPermission && newNetwork.created && !newNetwork.isVPN()) {
+            if (oldPermission != newPermission) {
                 try {
                     mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
                 } catch (RemoteException e) {
@@ -6488,53 +6489,11 @@
                 }
             }
 
-            final NetworkCapabilities prevNc;
-            synchronized (newNetwork) {
-                prevNc = newNetwork.networkCapabilities;
-                newNetwork.setNetworkCapabilities(newNc);
-            }
-
-            updateUids(newNetwork, prevNc, newNc);
-
-            if (newNetwork.getCurrentScore() == score
-                    && newNc.equalRequestableCapabilities(prevNc)) {
-                // If the requestable capabilities haven't changed, and the score hasn't changed,
-                // then the change we're processing can't affect any requests, it can only affect
-                // the listens on this network.
-                processListenRequests(newNetwork, true);
-            } else {
-                rematchAllNetworksAndRequests();
-                notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
-            }
-
-            if (prevNc != null) {
-                final boolean oldMetered = prevNc.isMetered();
-                final boolean newMetered = newNc.isMetered();
-                final boolean meteredChanged = oldMetered != newMetered;
-
-                if (meteredChanged) {
-                    maybeNotifyNetworkBlocked(newNetwork, oldMetered, newMetered,
-                            mRestrictBackground, mRestrictBackground);
-                }
-
-                final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
-                        != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
-
-                // Report changes that are interesting for network statistics tracking.
-                if (meteredChanged || roamingChanged) {
-                    notifyIfacesChangedForNetworkStats();
-                }
-            }
-
-            if (!newNc.hasTransport(TRANSPORT_VPN)) {
-                // Tell VPNs about updated capabilities, since they may need to
-                // bubble those changes through.
-                updateAllVpnsCapabilities();
-            }
-
-        } else {
-            processListenRequests(newNetwork, false);
+            newNetwork.getAndSetNetworkCapabilities(newNc);
+            notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
         }
+
+        processNewlySatisfiedListenRequests(newNetwork);
     }
 
     /**
@@ -6719,9 +6678,8 @@
 
             // NetworkCapabilities need to be set before sending the private DNS config to
             // NetworkMonitor, otherwise NetworkMonitor cannot determine if validation is required.
-            synchronized (networkAgent) {
-                networkAgent.setNetworkCapabilities(networkAgent.networkCapabilities);
-            }
+            networkAgent.getAndSetNetworkCapabilities(networkAgent.networkCapabilities);
+
             handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
             updateLinkProperties(networkAgent, new LinkProperties(networkAgent.linkProperties),
                     null);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 840b7af..0d496b6 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -58,6 +58,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.TetherConfigParcel;
 import android.net.TetherStatsParcel;
 import android.net.UidRange;
 import android.net.UidRangeParcel;
@@ -1023,7 +1024,10 @@
         NetworkStack.checkNetworkStackPermission(mContext);
         // an odd number of addrs will fail
         try {
-            mNetdService.tetherStartWithConfiguration(usingLegacyDnsProxy, dhcpRange);
+            final TetherConfigParcel config = new TetherConfigParcel();
+            config.usingLegacyDnsProxy = usingLegacyDnsProxy;
+            config.dhcpRanges = dhcpRange;
+            mNetdService.tetherStartWithConfiguration(config);
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 48e8e96..be95456 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -449,9 +449,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -546,9 +546,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -1610,8 +1610,6 @@
         }
         broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
                 networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
-                linkProperties, DataFailCause.NONE);
     }
 
     public void notifyDataConnectionFailed(String apnType) {
@@ -1651,9 +1649,6 @@
             handleRemoveListLocked();
         }
         broadcastDataConnectionFailed(apnType, subId);
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, null, null,
-                DataFailCause.NONE);
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1743,7 +1738,7 @@
                     if (mPreciseCallState[phoneId].getForegroundCallState()
                             != PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
                         mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-                        mCallQuality[phoneId] = new CallQuality();
+                        mCallQuality[phoneId] = createCallQuality();
                     }
                     mCallAttributes[phoneId] = new CallAttributes(mPreciseCallState[phoneId],
                             mCallNetworkType[phoneId], mCallQuality[phoneId]);
@@ -1771,8 +1766,6 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
-                backgroundCallState);
     }
 
     public void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
@@ -1854,8 +1847,6 @@
 
             handleRemoveListLocked();
         }
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -2347,33 +2338,6 @@
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-                                                  int backgroundCallState) {
-        Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
-        intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
-        intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
-    private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, LinkProperties linkProperties,
-            @DataFailureCause int failCause) {
-        Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_STATE, state);
-        intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
-        if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
-        }
-        intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
-
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
     private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
         if (checkNotifyPermission()) {
             return;
@@ -2769,4 +2733,9 @@
                 return "UNKNOWN";
         }
     }
+
+    /** Returns a new CallQuality object with default values. */
+    private static CallQuality createCallQuality() {
+        return new CallQuality(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5ffdf02..35774ed 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2411,7 +2411,8 @@
         mConstants = hasHandlerThread
                 ? new ActivityManagerConstants(mContext, this, mHandler) : null;
         final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */);
-        mProcessList.init(this, activeUids);
+        mPlatformCompat = null;
+        mProcessList.init(this, activeUids, mPlatformCompat);
         mLowMemDetector = null;
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
@@ -2432,7 +2433,6 @@
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
-        mPlatformCompat = null;
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2461,7 +2461,9 @@
 
         mConstants = new ActivityManagerConstants(mContext, this, mHandler);
         final ActiveUids activeUids = new ActiveUids(this, true /* postChangesToAtm */);
-        mProcessList.init(this, activeUids);
+        mPlatformCompat = (PlatformCompat) ServiceManager.getService(
+                Context.PLATFORM_COMPAT_SERVICE);
+        mProcessList.init(this, activeUids, mPlatformCompat);
         mLowMemDetector = new LowMemDetector(this);
         mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids);
 
@@ -2569,9 +2571,6 @@
 
         mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
 
-        mPlatformCompat = (PlatformCompat) ServiceManager.getService(
-                Context.PLATFORM_COMPAT_SERVICE);
-
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
 
@@ -5048,9 +5047,7 @@
             bindApplicationTimeMillis = SystemClock.elapsedRealtime();
             mAtmInternal.preBindApplication(app.getWindowProcessController());
             final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
-            long[] disabledCompatChanges = {};
             if (mPlatformCompat != null) {
-                disabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
                 mPlatformCompat.resetReporting(app.info);
             }
             if (app.isolatedEntryPoint != null) {
@@ -5069,7 +5066,7 @@
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        disabledCompatChanges);
+                        app.mDisabledCompatChanges);
             } else {
                 thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
                         null, null, null, testMode,
@@ -5079,7 +5076,7 @@
                         app.compat, getCommonServicesLocked(app.isolated),
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial, autofillOptions, contentCaptureOptions,
-                        disabledCompatChanges);
+                        app.mDisabledCompatChanges);
             }
             if (profilerInfo != null) {
                 profilerInfo.closeFd();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a2670d8..f397a32 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -96,6 +96,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.wm.ActivityServiceConnectionsHolder;
 import com.android.server.wm.WindowManagerService;
@@ -384,6 +385,8 @@
     final ArrayMap<AppZygote, ArrayList<ProcessRecord>> mAppZygoteProcesses =
             new ArrayMap<AppZygote, ArrayList<ProcessRecord>>();
 
+    private PlatformCompat mPlatformCompat = null;
+
     final class IsolatedUidRange {
         @VisibleForTesting
         public final int mFirstUid;
@@ -565,9 +568,11 @@
         updateOomLevels(0, 0, false);
     }
 
-    void init(ActivityManagerService service, ActiveUids activeUids) {
+    void init(ActivityManagerService service, ActiveUids activeUids,
+            PlatformCompat platformCompat) {
         mService = service;
         mActiveUids = activeUids;
+        mPlatformCompat = platformCompat;
 
         if (sKillHandler == null) {
             sKillThread = new ServiceThread(TAG + ":kill",
@@ -1657,6 +1662,10 @@
             Slog.wtf(TAG, "startProcessLocked processName:" + app.processName
                     + " with non-zero pid:" + app.pid);
         }
+        app.mDisabledCompatChanges = null;
+        if (mPlatformCompat != null) {
+            app.mDisabledCompatChanges = mPlatformCompat.getDisabledChanges(app.info);
+        }
         final long startSeq = app.startSeq = ++mProcStartSeqCounter;
         app.setStartParams(uid, hostingRecord, seInfo, startTime);
         app.setUsingWrapper(invokeWith != null
@@ -1811,8 +1820,8 @@
                 startResult = startWebView(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
-                        app.info.dataDir, null, app.info.packageName,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        app.info.dataDir, null, app.info.packageName, app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else if (hostingRecord.usesAppZygote()) {
                 final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);
 
@@ -1820,14 +1829,15 @@
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, app.info.packageName,
-                        /*useUsapPool=*/ false,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        /*useUsapPool=*/ false, app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             } else {
                 startResult = Process.start(entryPoint,
                         app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, app.info.packageName,
-                        new String[] {PROC_START_SEQ_IDENT + app.startSeq});
+                        app.mDisabledCompatChanges,
+                        new String[]{PROC_START_SEQ_IDENT + app.startSeq});
             }
             checkSlow(startTime, "startProcess: returned from zygote!");
             return startResult;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index ea30842..8c1a0d3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -307,6 +307,8 @@
     long startTime;
     // This will be same as {@link #uid} usually except for some apps used during factory testing.
     int startUid;
+    // set of disabled compat changes for the process (all others are enabled)
+    long[] mDisabledCompatChanges;
 
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 37add3d..135f199d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -62,9 +62,6 @@
 
     private @NonNull AudioDeviceBroker mDeviceBroker;
 
-    // cache of the address of the last dock the device was connected to
-    private String mDockAddress;
-
     // Monitoring of audio routes.  Protected by mAudioRoutes.
     final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers =
@@ -168,7 +165,7 @@
         int a2dpVolume = btInfo.getVolume();
         if (AudioService.DEBUG_DEVICES) {
             Log.d(TAG, "onSetA2dpSinkConnectionState btDevice=" + btDevice + " state="
-                    + state + " is dock=" + btDevice.isBluetoothDock() + " vol=" + a2dpVolume);
+                    + state + " vol=" + a2dpVolume);
         }
         String address = btDevice.getAddress();
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
@@ -196,42 +193,17 @@
                         mDeviceBroker.postBluetoothA2dpDeviceConfigChange(btDevice);
                     }
                 } else {
-                    if (btDevice.isBluetoothDock()) {
-                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
-                            // introduction of a delay for transient disconnections of docks when
-                            // power is rapidly turned off/on, this message will be canceled if
-                            // we reconnect the dock under a preset delay
-                            makeA2dpDeviceUnavailableLater(address,
-                                    AudioDeviceBroker.BTA2DP_DOCK_TIMEOUT_MS);
-                            // the next time isConnected is evaluated, it will be false for the dock
-                        }
-                    } else {
-                        makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
-                    }
+                    makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat);
                 }
-            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
-                if (btDevice.isBluetoothDock()) {
-                    // this could be a reconnection after a transient disconnection
-                    mDeviceBroker.cancelA2dpDockTimeout();
-                    mDockAddress = address;
-                } else {
-                    // this could be a connection of another A2DP device before the timeout of
-                    // a dock: cancel the dock timeout, and make the dock unavailable now
-                    if (mDeviceBroker.hasScheduledA2dpDockTimeout() && mDockAddress != null) {
-                        mDeviceBroker.cancelA2dpDockTimeout();
-                        makeA2dpDeviceUnavailableNow(mDockAddress,
-                                AudioSystem.AUDIO_FORMAT_DEFAULT);
-                    }
-                }
-                if (a2dpVolume != -1) {
-                    mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
-                            // convert index to internal representation in VolumeStreamState
-                            a2dpVolume * 10,
-                            AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
-                }
-                makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
-                        "onSetA2dpSinkConnectionState", a2dpCodec);
             }
+            if (a2dpVolume != -1) {
+                mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
+                        // convert index to internal representation in VolumeStreamState
+                        a2dpVolume * 10,
+                        AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "onSetA2dpSinkConnectionState");
+            }
+            makeA2dpDeviceAvailable(address, BtHelper.getName(btDevice),
+                    "onSetA2dpSinkConnectionState", a2dpCodec);
         }
     }
 
@@ -672,9 +644,6 @@
                 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
         // Remove A2DP routes as well
         setCurrentAudioRouteNameIfPossible(null);
-        if (mDockAddress == address) {
-            mDockAddress = null;
-        }
     }
 
     @GuardedBy("mConnectedDevices")
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index bb7f862..5e085ca 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -291,13 +291,18 @@
      *
      * <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
      * the exception is logged but not reported to callers.
+     *
+     * @return the old capabilities of this network.
      */
-    public void setNetworkCapabilities(NetworkCapabilities nc) {
+    public synchronized NetworkCapabilities getAndSetNetworkCapabilities(
+            @NonNull final NetworkCapabilities nc) {
+        final NetworkCapabilities oldNc = networkCapabilities;
         networkCapabilities = nc;
         final NetworkMonitorManager nm = mNetworkMonitor;
         if (nm != null) {
             nm.notifyNetworkCapabilitiesChanged(nc);
         }
+        return oldNc;
     }
 
     public ConnectivityService connService() {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
index 52cede2..23b5c14 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecKeycode.java
@@ -250,7 +250,11 @@
             new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_UP),
             // No Android keycode defined for CEC_KEYCODE_LEFT_DOWN
             new KeycodeEntry(UNSUPPORTED_KEYCODE, CEC_KEYCODE_LEFT_DOWN),
+            // Both KEYCODE_HOME and KEYCODE_MENU keys are sent as CEC_KEYCODE_ROOT_MENU
+            // NOTE that the HOME key is not usually forwarded.
+            // When CEC_KEYCODE_ROOT_MENU is received, it is translated to KEYCODE_HOME
             new KeycodeEntry(KeyEvent.KEYCODE_HOME, CEC_KEYCODE_ROOT_MENU),
+            new KeycodeEntry(KeyEvent.KEYCODE_MENU, CEC_KEYCODE_ROOT_MENU),
             new KeycodeEntry(KeyEvent.KEYCODE_SETTINGS, CEC_KEYCODE_SETUP_MENU),
             new KeycodeEntry(KeyEvent.KEYCODE_TV_CONTENTS_MENU, CEC_KEYCODE_CONTENTS_MENU, false),
             // No Android keycode defined for CEC_KEYCODE_FAVORITE_MENU
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 181a435..3fc8438 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -508,8 +508,10 @@
         CarrierConfigManager configManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        String mccMnc = SubscriptionManager.isValidSubscriptionId(ddSubId)
-                ? phone.getSimOperator(ddSubId) : phone.getSimOperator();
+        if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+            phone = phone.createForSubscriptionId(ddSubId);
+        }
+        String mccMnc = phone.getSimOperator();
         boolean isKeepLppProfile = false;
         if (!TextUtils.isEmpty(mccMnc)) {
             if (DEBUG) Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
@@ -1903,24 +1905,17 @@
         String setId = null;
 
         int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+            phone = phone.createForSubscriptionId(ddSubId);
+        }
         if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
-            if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
-                setId = phone.getSubscriberId(ddSubId);
-            }
-            if (setId == null) {
-                setId = phone.getSubscriberId();
-            }
+            setId = phone.getSubscriberId();
             if (setId != null) {
                 // This means the framework has the SIM card.
                 type = AGPS_SETID_TYPE_IMSI;
             }
         } else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
-            if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
-                setId = phone.getLine1Number(ddSubId);
-            }
-            if (setId == null) {
-                setId = phone.getLine1Number();
-            }
+            setId = phone.getLine1Number();
             if (setId != null) {
                 // This means the framework has the SIM card.
                 type = AGPS_SETID_TYPE_MSISDN;
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 9b9f4de..f295ed3 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.provider.Settings;
 import android.telecom.TelecomManager;
 
 import com.android.internal.util.NotificationMessagingUtil;
@@ -55,14 +54,9 @@
         final boolean isLeftHighImportance = leftImportance >= IMPORTANCE_DEFAULT;
         final boolean isRightHighImportance = rightImportance >= IMPORTANCE_DEFAULT;
 
-        // With new interruption model, prefer importance bucket above all other criteria
-        // (to ensure buckets are contiguous)
-        if (Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.NOTIFICATION_NEW_INTERRUPTION_MODEL, 1) == 1) {
-            if (isLeftHighImportance != isRightHighImportance) {
-                // by importance bucket, high importance higher than low importance
-                return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
-            }
+        if (isLeftHighImportance != isRightHighImportance) {
+            // by importance bucket, high importance higher than low importance
+            return -1 * Boolean.compare(isLeftHighImportance, isRightHighImportance);
         }
 
         // first all colorized notifications
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
deleted file mode 100644
index 4e8ba07..0000000
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timedetector;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
-import android.util.LocalLog;
-import android.util.Slog;
-import android.util.TimestampedValue;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.IndentingPrintWriter;
-
-import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * An implementation of TimeDetectorStrategy that passes only NITZ suggestions to
- * {@link AlarmManager}.
- *
- * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
- */
-public final class SimpleTimeDetectorStrategy implements TimeDetectorStrategy {
-
-    private static final boolean DBG = false;
-    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
-
-    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface Origin {}
-
-    /** Used when a time value originated from a telephony signal. */
-    @Origin
-    private static final int ORIGIN_PHONE = 1;
-
-    /** Used when a time value originated from a user / manual settings. */
-    @Origin
-    private static final int ORIGIN_MANUAL = 2;
-
-    /**
-     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
-     * actual system clock time before a warning is logged. Used to help identify situations where
-     * there is something other than this class setting the system clock automatically.
-     */
-    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
-
-    // A log for changes made to the system clock and why.
-    @NonNull
-    private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
-
-    // @NonNull after initialize()
-    private Callback mCallback;
-
-    // Last phone suggestion.
-    @Nullable private PhoneTimeSuggestion mLastPhoneSuggestion;
-
-    // Information about the last time signal received: Used when toggling auto-time.
-    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTime;
-    private boolean mLastAutoSystemClockTimeSendNetworkBroadcast;
-
-    // System clock state.
-    @Nullable private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
-
-    @Override
-    public void initialize(@NonNull Callback callback) {
-        mCallback = callback;
-    }
-
-    @Override
-    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
-        // NITZ logic
-
-        // Empty suggestions are just ignored as we don't currently keep track of suggestion origin.
-        if (timeSuggestion.getUtcTime() == null) {
-            return;
-        }
-
-        boolean timeSuggestionIsValid =
-                validateNewPhoneSuggestion(timeSuggestion, mLastPhoneSuggestion);
-        if (!timeSuggestionIsValid) {
-            return;
-        }
-        // Always store the last NITZ value received, regardless of whether we go on to use it to
-        // update the system clock. This is so that we can validate future phone suggestions.
-        mLastPhoneSuggestion = timeSuggestion;
-
-        // System clock update logic.
-        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
-        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, timeSuggestion);
-    }
-
-    @Override
-    public synchronized void suggestManualTime(ManualTimeSuggestion timeSuggestion) {
-        final TimestampedValue<Long> newUtcTime = timeSuggestion.getUtcTime();
-        setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, timeSuggestion);
-    }
-
-    private static boolean validateNewPhoneSuggestion(@NonNull PhoneTimeSuggestion newSuggestion,
-            @Nullable PhoneTimeSuggestion lastSuggestion) {
-
-        if (lastSuggestion != null) {
-            long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
-                    newSuggestion.getUtcTime(), lastSuggestion.getUtcTime());
-            if (referenceTimeDifference < 0 || referenceTimeDifference > Integer.MAX_VALUE) {
-                // Out of order or bogus.
-                Slog.w(LOG_TAG, "Bad NITZ signal received."
-                        + " referenceTimeDifference=" + referenceTimeDifference
-                        + " lastSuggestion=" + lastSuggestion
-                        + " newSuggestion=" + newSuggestion);
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @GuardedBy("this")
-    private void setSystemClockIfRequired(
-            @Origin int origin, TimestampedValue<Long> time, Object cause) {
-        // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
-        // when setting the time using NITZ.
-        boolean sendNetworkBroadcast = origin == ORIGIN_PHONE;
-
-        boolean isOriginAutomatic = isOriginAutomatic(origin);
-        if (isOriginAutomatic) {
-            // Store the last auto time candidate we've seen in all cases so we can set the system
-            // clock when/if time detection is off but later enabled.
-            mLastAutoSystemClockTime = time;
-            mLastAutoSystemClockTimeSendNetworkBroadcast = sendNetworkBroadcast;
-
-            if (!mCallback.isAutoTimeDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
-                            + " origin=" + origin
-                            + ", time=" + time
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        } else {
-            if (mCallback.isAutoTimeDetectionEnabled()) {
-                if (DBG) {
-                    Slog.d(LOG_TAG, "Auto time detection is enabled."
-                            + " origin=" + origin
-                            + ", time=" + time
-                            + ", cause=" + cause);
-                }
-                return;
-            }
-        }
-
-        mCallback.acquireWakeLock();
-        try {
-            long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
-            long actualTimeMillis = mCallback.systemClockMillis();
-
-            if (isOriginAutomatic) {
-                // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
-                // may be setting the clock.
-                if (mLastAutoSystemClockTimeSet != null) {
-                    long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
-                            mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
-                    long absSystemClockDifference = Math.abs(expectedTimeMillis - actualTimeMillis);
-                    if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
-                        Slog.w(LOG_TAG,
-                                "System clock has not tracked elapsed real time clock. A clock may"
-                                        + " be inaccurate or something unexpectedly set the system"
-                                        + " clock."
-                                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                                        + " expectedTimeMillis=" + expectedTimeMillis
-                                        + " actualTimeMillis=" + actualTimeMillis);
-                    }
-                }
-            }
-
-            adjustAndSetDeviceSystemClock(
-                    time, sendNetworkBroadcast, elapsedRealtimeMillis, actualTimeMillis, cause);
-        } finally {
-            mCallback.releaseWakeLock();
-        }
-    }
-
-    private static boolean isOriginAutomatic(@Origin int origin) {
-        return origin == ORIGIN_PHONE;
-    }
-
-    @Override
-    public synchronized void handleAutoTimeDetectionChanged() {
-        // If automatic time detection is enabled we update the system clock instantly if we can.
-        // Conversely, if automatic time detection is disabled we leave the clock as it is.
-        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
-        if (enabled) {
-            if (mLastAutoSystemClockTime != null) {
-                // Only send the network broadcast if the last candidate would have caused one.
-                final boolean sendNetworkBroadcast = mLastAutoSystemClockTimeSendNetworkBroadcast;
-
-                mCallback.acquireWakeLock();
-                try {
-                    long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
-                    long actualTimeMillis = mCallback.systemClockMillis();
-
-                    final String reason = "Automatic time detection enabled.";
-                    adjustAndSetDeviceSystemClock(mLastAutoSystemClockTime, sendNetworkBroadcast,
-                            elapsedRealtimeMillis, actualTimeMillis, reason);
-                } finally {
-                    mCallback.releaseWakeLock();
-                }
-            }
-        } else {
-            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
-            // it should be in future.
-            mLastAutoSystemClockTimeSet = null;
-        }
-    }
-
-    @Override
-    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-        ipw.println("TimeDetectorStrategy:");
-        ipw.increaseIndent(); // level 1
-
-        ipw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
-        ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
-        ipw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
-        ipw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
-                + mLastAutoSystemClockTimeSendNetworkBroadcast);
-
-
-        ipw.println("Time change log:");
-        ipw.increaseIndent(); // level 2
-        mTimeChangesLog.dump(ipw);
-        ipw.decreaseIndent(); // level 2
-
-        ipw.decreaseIndent(); // level 1
-        ipw.flush();
-    }
-
-    @GuardedBy("this")
-    private void adjustAndSetDeviceSystemClock(
-            TimestampedValue<Long> newTime, boolean sendNetworkBroadcast,
-            long elapsedRealtimeMillis, long actualSystemClockMillis, Object cause) {
-
-        // Adjust for the time that has elapsed since the signal was received.
-        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
-
-        // Check if the new signal would make sufficient difference to the system clock. If it's
-        // below the threshold then ignore it.
-        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
-        long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
-        if (absTimeDifference < systemClockUpdateThreshold) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "Not setting system clock. New time and"
-                        + " system clock are close enough."
-                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                        + " newTime=" + newTime
-                        + " cause=" + cause
-                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
-                        + " absTimeDifference=" + absTimeDifference);
-            }
-            return;
-        }
-
-        mCallback.setSystemClock(newSystemClockMillis);
-        String logMsg = "Set system clock using time=" + newTime
-                + " cause=" + cause
-                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
-                + " newSystemClockMillis=" + newSystemClockMillis;
-        if (DBG) {
-            Slog.d(LOG_TAG, logMsg);
-        }
-        mTimeChangesLog.log(logMsg);
-
-        // CLOCK_PARANOIA : Record the last time this class set the system clock.
-        mLastAutoSystemClockTimeSet = newTime;
-
-        if (sendNetworkBroadcast) {
-            // Send a broadcast that telephony code used to send after setting the clock.
-            // TODO Remove this broadcast as soon as there are no remaining listeners.
-            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-            intent.putExtra("time", newSystemClockMillis);
-            mCallback.sendStickyBroadcast(intent);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 34400ff..172367a 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -31,7 +31,6 @@
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
-import com.android.server.timedetector.TimeDetectorStrategy.Callback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -58,17 +57,16 @@
 
     @NonNull private final Handler mHandler;
     @NonNull private final Context mContext;
-    @NonNull private final Callback mCallback;
     @NonNull private final TimeDetectorStrategy mTimeDetectorStrategy;
 
     private static TimeDetectorService create(@NonNull Context context) {
-        TimeDetectorStrategy timeDetector = new SimpleTimeDetectorStrategy();
+        TimeDetectorStrategy timeDetectorStrategy = new TimeDetectorStrategyImpl();
         TimeDetectorStrategyCallbackImpl callback = new TimeDetectorStrategyCallbackImpl(context);
-        timeDetector.initialize(callback);
+        timeDetectorStrategy.initialize(callback);
 
         Handler handler = FgThread.getHandler();
         TimeDetectorService timeDetectorService =
-                new TimeDetectorService(context, handler, callback, timeDetector);
+                new TimeDetectorService(context, handler, timeDetectorStrategy);
 
         // Wire up event listening.
         ContentResolver contentResolver = context.getContentResolver();
@@ -85,10 +83,9 @@
 
     @VisibleForTesting
     public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
-            @NonNull Callback callback, @NonNull TimeDetectorStrategy timeDetectorStrategy) {
+            @NonNull TimeDetectorStrategy timeDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
-        mCallback = Objects.requireNonNull(callback);
         mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
     }
 
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
new file mode 100644
index 0000000..1b1ac6d
--- /dev/null
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AlarmManager;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.content.Intent;
+import android.util.ArrayMap;
+import android.util.LocalLog;
+import android.util.Slog;
+import android.util.TimestampedValue;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * An implementation of TimeDetectorStrategy that passes phone and manual suggestions to
+ * {@link AlarmManager}. When there are multiple phone sources, the one with the lowest ID is used
+ * unless the data becomes too stale.
+ *
+ * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
+ */
+public final class TimeDetectorStrategyImpl implements TimeDetectorStrategy {
+
+    private static final boolean DBG = false;
+    private static final String LOG_TAG = "SimpleTimeDetectorStrategy";
+
+    /** A score value used to indicate "no score", either due to validation failure or age. */
+    private static final int PHONE_INVALID_SCORE = -1;
+    /** The number of buckets phone suggestions can be put in by age. */
+    private static final int PHONE_BUCKET_COUNT = 24;
+    /** Each bucket is this size. All buckets are equally sized. */
+    @VisibleForTesting
+    static final int PHONE_BUCKET_SIZE_MILLIS = 60 * 60 * 1000;
+    /** Phone suggestions older than this value are considered too old. */
+    @VisibleForTesting
+    static final long PHONE_MAX_AGE_MILLIS = PHONE_BUCKET_COUNT * PHONE_BUCKET_SIZE_MILLIS;
+
+    @IntDef({ ORIGIN_PHONE, ORIGIN_MANUAL })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Origin {}
+
+    /** Used when a time value originated from a telephony signal. */
+    @Origin
+    private static final int ORIGIN_PHONE = 1;
+
+    /** Used when a time value originated from a user / manual settings. */
+    @Origin
+    private static final int ORIGIN_MANUAL = 2;
+
+    /**
+     * CLOCK_PARANOIA: The maximum difference allowed between the expected system clock time and the
+     * actual system clock time before a warning is logged. Used to help identify situations where
+     * there is something other than this class setting the system clock.
+     */
+    private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
+
+    /** The number of previous phone suggestions to keep for each ID (for use during debugging). */
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+
+    // A log for changes made to the system clock and why.
+    @NonNull
+    private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
+
+    // @NonNull after initialize()
+    private Callback mCallback;
+
+    // Used to store the last time the system clock state was set automatically. It is used to
+    // detect (and log) issues with the realtime clock or whether the clock is being set without
+    // going through this strategy code.
+    @GuardedBy("this")
+    @Nullable
+    private TimestampedValue<Long> mLastAutoSystemClockTimeSet;
+
+    /**
+     * A mapping from phoneId to a linked list of time suggestions (the "first" being the latest).
+     * We typically expect one or two entries in this Map: devices will have a small number
+     * of telephony devices and phoneIds are assumed to be stable. The LinkedList associated with
+     * the ID will not exceed {@link #KEEP_SUGGESTION_HISTORY_SIZE} in size.
+     */
+    @GuardedBy("this")
+    private ArrayMap<Integer, LinkedList<PhoneTimeSuggestion>> mSuggestionByPhoneId =
+            new ArrayMap<>();
+
+    @Override
+    public void initialize(@NonNull Callback callback) {
+        mCallback = callback;
+    }
+
+    @Override
+    public synchronized void suggestManualTime(@NonNull ManualTimeSuggestion suggestion) {
+        final TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
+
+        if (!validateSuggestionTime(newUtcTime, suggestion)) {
+            return;
+        }
+
+        String cause = "Manual time suggestion received: suggestion=" + suggestion;
+        setSystemClockIfRequired(ORIGIN_MANUAL, newUtcTime, cause);
+    }
+
+    @Override
+    public synchronized void suggestPhoneTime(@NonNull PhoneTimeSuggestion timeSuggestion) {
+        // Empty time suggestion means that telephony network connectivity has been lost.
+        // The passage of time is relentless, and we don't expect our users to use a time machine,
+        // so we can continue relying on previous suggestions when we lose connectivity. This is
+        // unlike time zone, where a user may lose connectivity when boarding a flight and where we
+        // do want to "forget" old signals. Suggestions that are too old are discarded later in the
+        // detection algorithm.
+        if (timeSuggestion.getUtcTime() == null) {
+            return;
+        }
+
+        // Perform validation / input filtering and record the validated suggestion against the
+        // phoneId.
+        if (!validateAndStorePhoneSuggestion(timeSuggestion)) {
+            return;
+        }
+
+        // Now perform auto time detection. The new suggestion may be used to modify the system
+        // clock.
+        String reason = "New phone time suggested. timeSuggestion=" + timeSuggestion;
+        doAutoTimeDetection(reason);
+    }
+
+    @Override
+    public synchronized void handleAutoTimeDetectionChanged() {
+        boolean enabled = mCallback.isAutoTimeDetectionEnabled();
+        // When automatic time detection is enabled we update the system clock instantly if we can.
+        // Conversely, when automatic time detection is disabled we leave the clock as it is.
+        if (enabled) {
+            String reason = "Auto time zone detection setting enabled.";
+            doAutoTimeDetection(reason);
+        } else {
+            // CLOCK_PARANOIA: We are losing "control" of the system clock so we cannot predict what
+            // it should be in future.
+            mLastAutoSystemClockTimeSet = null;
+        }
+    }
+
+    @Override
+    public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+        ipw.println("TimeDetectorStrategy:");
+        ipw.increaseIndent(); // level 1
+
+        ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+
+        ipw.println("Time change log:");
+        ipw.increaseIndent(); // level 2
+        mTimeChangesLog.dump(ipw);
+        ipw.decreaseIndent(); // level 2
+
+        ipw.println("Phone suggestion history:");
+        ipw.increaseIndent(); // level 2
+        for (Map.Entry<Integer, LinkedList<PhoneTimeSuggestion>> entry
+                : mSuggestionByPhoneId.entrySet()) {
+            ipw.println("Phone " + entry.getKey());
+
+            ipw.increaseIndent(); // level 3
+            for (PhoneTimeSuggestion suggestion : entry.getValue()) {
+                ipw.println(suggestion);
+            }
+            ipw.decreaseIndent(); // level 3
+        }
+        ipw.decreaseIndent(); // level 2
+
+        ipw.decreaseIndent(); // level 1
+        ipw.flush();
+    }
+
+    @GuardedBy("this")
+    private boolean validateAndStorePhoneSuggestion(@NonNull PhoneTimeSuggestion suggestion) {
+        TimestampedValue<Long> newUtcTime = suggestion.getUtcTime();
+        if (!validateSuggestionTime(newUtcTime, suggestion)) {
+            // There's probably nothing useful we can do: elsewhere we assume that reference
+            // times are in the past so just stop here.
+            return false;
+        }
+
+        int phoneId = suggestion.getPhoneId();
+        LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.get(phoneId);
+        if (phoneSuggestions == null) {
+            // The first time we've seen this phoneId.
+            phoneSuggestions = new LinkedList<>();
+            mSuggestionByPhoneId.put(phoneId, phoneSuggestions);
+        } else if (phoneSuggestions.isEmpty()) {
+            Slog.w(LOG_TAG, "Suggestions unexpectedly empty when adding suggestion=" + suggestion);
+        }
+
+        if (!phoneSuggestions.isEmpty()) {
+            // We can log / discard suggestions with obvious issues with the reference time clock.
+            PhoneTimeSuggestion previousSuggestion = phoneSuggestions.getFirst();
+            if (previousSuggestion == null
+                    || previousSuggestion.getUtcTime() == null
+                    || previousSuggestion.getUtcTime().getValue() == null) {
+                // This should be impossible given we only store validated suggestions.
+                Slog.w(LOG_TAG, "Previous suggestion is null or has a null time."
+                        + " previousSuggestion=" + previousSuggestion
+                        + ", suggestion=" + suggestion);
+                return false;
+            }
+
+            long referenceTimeDifference = TimestampedValue.referenceTimeDifference(
+                    newUtcTime, previousSuggestion.getUtcTime());
+            if (referenceTimeDifference < 0) {
+                // The reference time is before the previously received suggestion. Ignore it.
+                Slog.w(LOG_TAG, "Out of order phone suggestion received."
+                        + " referenceTimeDifference=" + referenceTimeDifference
+                        + " previousSuggestion=" + previousSuggestion
+                        + " suggestion=" + suggestion);
+                return false;
+            }
+        }
+
+        // Store the latest suggestion.
+        phoneSuggestions.addFirst(suggestion);
+        if (phoneSuggestions.size() > KEEP_SUGGESTION_HISTORY_SIZE) {
+            phoneSuggestions.removeLast();
+        }
+        return true;
+    }
+
+    private boolean validateSuggestionTime(
+            @NonNull TimestampedValue<Long> newUtcTime, @NonNull Object suggestion) {
+        if (newUtcTime.getValue() == null) {
+            Slog.w(LOG_TAG, "Suggested time value is null. suggestion=" + suggestion);
+            return false;
+        }
+
+        // We can validate the suggestion against the reference time clock.
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+        if (elapsedRealtimeMillis < newUtcTime.getReferenceTimeMillis()) {
+            // elapsedRealtime clock went backwards?
+            Slog.w(LOG_TAG, "New reference time is in the future? Ignoring."
+                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                    + ", suggestion=" + suggestion);
+            return false;
+        }
+        return true;
+    }
+
+    @GuardedBy("this")
+    private void doAutoTimeDetection(@NonNull String detectionReason) {
+        if (!mCallback.isAutoTimeDetectionEnabled()) {
+            // Avoid doing unnecessary work with this (race-prone) check.
+            return;
+        }
+
+        PhoneTimeSuggestion bestPhoneSuggestion = findBestPhoneSuggestion();
+
+        // Work out what to do with the best suggestion.
+        if (bestPhoneSuggestion == null) {
+            // There is no good phone suggestion.
+            if (DBG) {
+                Slog.d(LOG_TAG, "Could not determine time: No best phone suggestion."
+                        + " detectionReason=" + detectionReason);
+            }
+            return;
+        }
+
+        final TimestampedValue<Long> newUtcTime = bestPhoneSuggestion.getUtcTime();
+        String cause = "Found good suggestion."
+                + ", bestPhoneSuggestion=" + bestPhoneSuggestion
+                + ", detectionReason=" + detectionReason;
+        setSystemClockIfRequired(ORIGIN_PHONE, newUtcTime, cause);
+    }
+
+    @GuardedBy("this")
+    @Nullable
+    private PhoneTimeSuggestion findBestPhoneSuggestion() {
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+
+        // Phone time suggestions are assumed to be derived from NITZ or NITZ-like signals. These
+        // have a number of limitations:
+        // 1) No guarantee of accuracy ("accuracy of the time information is in the order of
+        // minutes") [1]
+        // 2) No guarantee of regular signals ("dependent on the handset crossing radio network
+        // boundaries") [1]
+        //
+        // [1] https://en.wikipedia.org/wiki/NITZ
+        //
+        // Generally, when there are suggestions from multiple phoneIds they should usually
+        // approximately agree. In cases where signals *are* inaccurate we don't want to vacillate
+        // between signals from two phoneIds. However, it is known for NITZ signals to be incorrect
+        // occasionally, which means we also don't want to stick forever with one phoneId. Without
+        // cross-referencing across sources (e.g. the current device time, NTP), or doing some kind
+        // of statistical analysis of consistency within and across phoneIds, we can't know which
+        // suggestions are more correct.
+        //
+        // For simplicity, we try to value recency, then consistency of phoneId.
+        //
+        // The heuristic works as follows:
+        // Recency: The most recent suggestion from each phone is scored. The score is based on a
+        // discrete age bucket, i.e. so signals received around the same time will be in the same
+        // bucket, thus applying a loose reference time ordering. The suggestion with the highest
+        // score is used.
+        // Consistency: If there a multiple suggestions with the same score, the suggestion with the
+        // lowest phoneId is always taken.
+        //
+        // In the trivial case with a single ID this will just mean that the latest received
+        // suggestion is used.
+
+        PhoneTimeSuggestion bestSuggestion = null;
+        int bestScore = PHONE_INVALID_SCORE;
+        for (int i = 0; i < mSuggestionByPhoneId.size(); i++) {
+            Integer phoneId = mSuggestionByPhoneId.keyAt(i);
+            LinkedList<PhoneTimeSuggestion> phoneSuggestions = mSuggestionByPhoneId.valueAt(i);
+            if (phoneSuggestions == null) {
+                // Unexpected - map is missing a value.
+                Slog.w(LOG_TAG, "Suggestions unexpectedly missing for phoneId."
+                        + " phoneId=" + phoneId);
+                continue;
+            }
+
+            PhoneTimeSuggestion candidateSuggestion = phoneSuggestions.getFirst();
+            if (candidateSuggestion == null) {
+                // Unexpected - null suggestions should never be stored.
+                Slog.w(LOG_TAG, "Latest suggestion unexpectedly null for phoneId."
+                        + " phoneId=" + phoneId);
+                continue;
+            } else if (candidateSuggestion.getUtcTime() == null) {
+                // Unexpected - we do not store empty suggestions.
+                Slog.w(LOG_TAG, "Latest suggestion unexpectedly empty. "
+                        + " candidateSuggestion=" + candidateSuggestion);
+                continue;
+            }
+
+            int candidateScore = scorePhoneSuggestion(elapsedRealtimeMillis, candidateSuggestion);
+            if (candidateScore == PHONE_INVALID_SCORE) {
+                // Expected: This means the suggestion is obviously invalid or just too old.
+                continue;
+            }
+
+            // Higher scores are better.
+            if (bestSuggestion == null || bestScore < candidateScore) {
+                bestSuggestion = candidateSuggestion;
+                bestScore = candidateScore;
+            } else if (bestScore == candidateScore) {
+                // Tie! Use the suggestion with the lowest phoneId.
+                int candidatePhoneId = candidateSuggestion.getPhoneId();
+                int bestPhoneId = bestSuggestion.getPhoneId();
+                if (candidatePhoneId < bestPhoneId) {
+                    bestSuggestion = candidateSuggestion;
+                }
+            }
+        }
+        return bestSuggestion;
+    }
+
+    private static int scorePhoneSuggestion(
+            long elapsedRealtimeMillis, @NonNull PhoneTimeSuggestion timeSuggestion) {
+        // The score is based on the age since receipt. Suggestions are bucketed so two
+        // suggestions in the same bucket from different phoneIds are scored the same.
+        TimestampedValue<Long> utcTime = timeSuggestion.getUtcTime();
+        long referenceTimeMillis = utcTime.getReferenceTimeMillis();
+        if (referenceTimeMillis > elapsedRealtimeMillis) {
+            // Future times are ignored. They imply the reference time was wrong, or the elapsed
+            // realtime clock has gone backwards, neither of which are supportable situations.
+            Slog.w(LOG_TAG, "Existing suggestion found to be in the future. "
+                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                    + ", timeSuggestion=" + timeSuggestion);
+            return PHONE_INVALID_SCORE;
+        }
+
+        long ageMillis = elapsedRealtimeMillis - referenceTimeMillis;
+
+        // Any suggestion > MAX_AGE_MILLIS is treated as too old. Although time is relentless and
+        // predictable, the accuracy of the reference time clock may be poor over long periods which
+        // would lead to errors creeping in. Also, in edge cases where a bad suggestion has been
+        // made and never replaced, it could also mean that the time detection code remains
+        // opinionated using a bad invalid suggestion. This caps that edge case at MAX_AGE_MILLIS.
+        if (ageMillis > PHONE_MAX_AGE_MILLIS) {
+            return PHONE_INVALID_SCORE;
+        }
+
+        // Turn the age into a discrete value: 0 <= bucketIndex < MAX_AGE_HOURS.
+        int bucketIndex = (int) (ageMillis / PHONE_BUCKET_SIZE_MILLIS);
+
+        // We want the lowest bucket index to have the highest score. 0 > score >= BUCKET_COUNT.
+        return PHONE_BUCKET_COUNT - bucketIndex;
+    }
+
+    @GuardedBy("this")
+    private void setSystemClockIfRequired(
+            @Origin int origin, @NonNull TimestampedValue<Long> time, @NonNull String cause) {
+
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        if (isOriginAutomatic) {
+            if (!mCallback.isAutoTimeDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is not enabled."
+                            + " origin=" + origin
+                            + ", time=" + time
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        } else {
+            if (mCallback.isAutoTimeDetectionEnabled()) {
+                if (DBG) {
+                    Slog.d(LOG_TAG, "Auto time detection is enabled."
+                            + " origin=" + origin
+                            + ", time=" + time
+                            + ", cause=" + cause);
+                }
+                return;
+            }
+        }
+
+        mCallback.acquireWakeLock();
+        try {
+            setSystemClockUnderWakeLock(origin, time, cause);
+        } finally {
+            mCallback.releaseWakeLock();
+        }
+    }
+
+    private static boolean isOriginAutomatic(@Origin int origin) {
+        return origin == ORIGIN_PHONE;
+    }
+
+    @GuardedBy("this")
+    private void setSystemClockUnderWakeLock(
+            int origin, @NonNull TimestampedValue<Long> newTime, @NonNull Object cause) {
+
+        long elapsedRealtimeMillis = mCallback.elapsedRealtimeMillis();
+        boolean isOriginAutomatic = isOriginAutomatic(origin);
+        long actualSystemClockMillis = mCallback.systemClockMillis();
+        if (isOriginAutomatic) {
+            // CLOCK_PARANOIA : Check to see if this class owns the clock or if something else
+            // may be setting the clock.
+            if (mLastAutoSystemClockTimeSet != null) {
+                long expectedTimeMillis = TimeDetectorStrategy.getTimeAt(
+                        mLastAutoSystemClockTimeSet, elapsedRealtimeMillis);
+                long absSystemClockDifference =
+                        Math.abs(expectedTimeMillis - actualSystemClockMillis);
+                if (absSystemClockDifference > SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS) {
+                    Slog.w(LOG_TAG,
+                            "System clock has not tracked elapsed real time clock. A clock may"
+                                    + " be inaccurate or something unexpectedly set the system"
+                                    + " clock."
+                                    + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                                    + " expectedTimeMillis=" + expectedTimeMillis
+                                    + " actualTimeMillis=" + actualSystemClockMillis
+                                    + " cause=" + cause);
+                }
+            }
+        }
+
+        // Adjust for the time that has elapsed since the signal was received.
+        long newSystemClockMillis = TimeDetectorStrategy.getTimeAt(newTime, elapsedRealtimeMillis);
+
+        // Check if the new signal would make sufficient difference to the system clock. If it's
+        // below the threshold then ignore it.
+        long absTimeDifference = Math.abs(newSystemClockMillis - actualSystemClockMillis);
+        long systemClockUpdateThreshold = mCallback.systemClockUpdateThresholdMillis();
+        if (absTimeDifference < systemClockUpdateThreshold) {
+            if (DBG) {
+                Slog.d(LOG_TAG, "Not setting system clock. New time and"
+                        + " system clock are close enough."
+                        + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                        + " newTime=" + newTime
+                        + " cause=" + cause
+                        + " systemClockUpdateThreshold=" + systemClockUpdateThreshold
+                        + " absTimeDifference=" + absTimeDifference);
+            }
+            return;
+        }
+
+        mCallback.setSystemClock(newSystemClockMillis);
+        String logMsg = "Set system clock using time=" + newTime
+                + " cause=" + cause
+                + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
+                + " newSystemClockMillis=" + newSystemClockMillis;
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
+        }
+        mTimeChangesLog.log(logMsg);
+
+        // CLOCK_PARANOIA : Record the last time this class set the system clock due to an auto-time
+        // signal, or clear the record it is being done manually.
+        if (isOriginAutomatic(origin)) {
+            mLastAutoSystemClockTimeSet = newTime;
+        } else {
+            mLastAutoSystemClockTimeSet = null;
+        }
+
+        // Historically, Android has sent a TelephonyIntents.ACTION_NETWORK_SET_TIME broadcast only
+        // when setting the time using NITZ.
+        if (origin == ORIGIN_PHONE) {
+            // Send a broadcast that telephony code used to send after setting the clock.
+            // TODO Remove this broadcast as soon as there are no remaining listeners.
+            Intent intent = new Intent(TelephonyIntents.ACTION_NETWORK_SET_TIME);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            intent.putExtra("time", newSystemClockMillis);
+            mCallback.sendStickyBroadcast(intent);
+        }
+    }
+
+    /**
+     * Returns the current best phone suggestion. Not intended for general use: it is used during
+     * tests to check strategy behavior.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized PhoneTimeSuggestion findBestPhoneSuggestionForTests() {
+        return findBestPhoneSuggestion();
+    }
+
+    /**
+     * A method used to inspect state during tests. Not intended for general use.
+     */
+    @VisibleForTesting
+    @Nullable
+    public synchronized PhoneTimeSuggestion getLatestPhoneSuggestion(int phoneId) {
+        LinkedList<PhoneTimeSuggestion> suggestions = mSuggestionByPhoneId.get(phoneId);
+        if (suggestions == null) {
+            return null;
+        }
+        return suggestions.getFirst();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
deleted file mode 100644
index 7a0a28d..0000000
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
+++ /dev/null
@@ -1,661 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.timedetector;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.app.timedetector.ManualTimeSuggestion;
-import android.app.timedetector.PhoneTimeSuggestion;
-import android.content.Intent;
-import android.icu.util.Calendar;
-import android.icu.util.GregorianCalendar;
-import android.icu.util.TimeZone;
-import android.util.TimestampedValue;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.time.Duration;
-
-@RunWith(AndroidJUnit4.class)
-public class SimpleTimeDetectorStrategyTest {
-
-    private static final Scenario SCENARIO_1 = new Scenario.Builder()
-            .setInitialDeviceSystemClockUtc(1977, 1, 1, 12, 0, 0)
-            .setInitialDeviceRealtimeMillis(123456789L)
-            .setActualTimeUtc(2018, 1, 1, 12, 0, 0)
-            .build();
-
-    private static final int ARBITRARY_PHONE_ID = 123456;
-
-    private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
-
-    private Script mScript;
-
-    @Before
-    public void setUp() {
-        mScript = new Script();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_autoTimeEnabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        final int clockIncrement = 1000;
-        long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_emptySuggestionIgnored() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion = createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, null);
-
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_systemClockThreshold() {
-        Scenario scenario = SCENARIO_1;
-        final int systemClockUpdateThresholdMillis = 1000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThresholdMillis)
-                .pokeTimeDetectionEnabled(true);
-
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        final int clockIncrement = 100;
-        // Increment the the device clocks to simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-
-        long expectSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-
-        // Send the first time signal. It should be used.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // Now send another time signal, but one that is too similar to the last one and should be
-        // ignored.
-        int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + underThresholdMillis);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-        mScript.simulateTimePassing(clockIncrement)
-                .simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Now send another time signal, but one that is on the threshold and so should be used.
-        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
-
-        PhoneTimeSuggestion timeSuggestion3 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
-        mScript.simulateTimePassing(clockIncrement);
-
-        long expectSystemClockMillis3 =
-                TimeDetectorStrategy.getTimeAt(utcTime3, mScript.peekElapsedRealtimeMillis());
-
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis3, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_autoTimeDisabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(false);
-
-        PhoneTimeSuggestion timeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    @Test
-    public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() {
-        Scenario scenario = SCENARIO_1;
-        final int systemClockUpdateThreshold = 2000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThreshold)
-                .pokeTimeDetectionEnabled(true);
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        // Initialize the strategy / device with a time set from NITZ.
-        mScript.simulateTimePassing(100);
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // The UTC time increment should be larger than the system clock update threshold so we
-        // know it shouldn't be ignored for other reasons.
-        long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
-
-        // Now supply a new signal that has an obviously bogus reference time : older than the last
-        // one.
-        long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Now supply a new signal that has an obviously bogus reference time : substantially in the
-        // future.
-        long referenceTimeInFutureMillis =
-                utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
-        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
-                referenceTimeInFutureMillis, validUtcTimeMillis);
-        PhoneTimeSuggestion timeSuggestion3 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime3);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Just to prove validUtcTimeMillis is valid.
-        long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
-        TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
-                validReferenceTimeMillis, validUtcTimeMillis);
-        long expectedSystemClockMillis4 =
-                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
-        PhoneTimeSuggestion timeSuggestion4 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime4);
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis4, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestPhoneTime_timeDetectionToggled() {
-        Scenario scenario = SCENARIO_1;
-        final int clockIncrementMillis = 100;
-        final int systemClockUpdateThreshold = 2000;
-        mScript.pokeFakeClocks(scenario)
-                .pokeThresholds(systemClockUpdateThreshold)
-                .pokeTimeDetectionEnabled(false);
-
-        PhoneTimeSuggestion timeSuggestion1 =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
-
-        // Simulate time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        // Simulate the time signal being received. It should not be used because auto time
-        // detection is off but it should be recorded.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Simulate more time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        long expectedSystemClockMillis1 =
-                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
-
-        // Turn on auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */);
-
-        // Turn off auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Receive another valid time signal.
-        // It should be on the threshold and accounting for the clock increments.
-        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
-                mScript.peekElapsedRealtimeMillis(),
-                mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
-        PhoneTimeSuggestion timeSuggestion2 =
-                createPhoneTimeSuggestion(ARBITRARY_PHONE_ID, utcTime2);
-
-        // Simulate more time passing.
-        mScript.simulateTimePassing(clockIncrementMillis);
-
-        long expectedSystemClockMillis2 =
-                TimeDetectorStrategy.getTimeAt(utcTime2, mScript.peekElapsedRealtimeMillis());
-
-        // The new time, though valid, should not be set in the system clock because auto time is
-        // disabled.
-        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Turn on auto time detection.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedSystemClockMillis2, true /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestManualTime_autoTimeDisabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(false);
-
-        ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual();
-        final int clockIncrement = 1000;
-        long expectSystemClockMillis = scenario.getActualTimeMillis() + clockIncrement;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulateManualTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectSystemClockMillis, false /* expectNetworkBroadcast */);
-    }
-
-    @Test
-    public void testSuggestManualTime_retainsAutoSignal() {
-        Scenario scenario = SCENARIO_1;
-
-        // Configure the start state.
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        // Simulate a phone suggestion.
-        PhoneTimeSuggestion phoneTimeSuggestion =
-                scenario.createPhoneTimeSuggestionForActual(ARBITRARY_PHONE_ID);
-        long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
-        final int clockIncrement = 1000;
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */);
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        // Switch to manual.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-
-        // Simulate a manual suggestion 1 day different from the auto suggestion.
-        long manualTimeMillis = SCENARIO_1.getActualTimeMillis() + ONE_DAY_MILLIS;
-        long expectedManualClockMillis = manualTimeMillis;
-        ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion(manualTimeMillis);
-        mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
-                .verifySystemClockWasSetAndResetCallTracking(
-                        expectedManualClockMillis, false /* expectNetworkBroadcast */);
-
-        // Simulate the passage of time.
-        mScript.simulateTimePassing(clockIncrement);
-        expectedAutoClockMillis += clockIncrement;
-
-        // Switch back to auto.
-        mScript.simulateAutoTimeDetectionToggle();
-
-        mScript.verifySystemClockWasSetAndResetCallTracking(
-                        expectedAutoClockMillis, true /* expectNetworkBroadcast */);
-
-        // Switch back to manual - nothing should happen to the clock.
-        mScript.simulateAutoTimeDetectionToggle()
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    /**
-     * Manual suggestions should be ignored if auto time is enabled.
-     */
-    @Test
-    public void testSuggestManualTime_autoTimeEnabled() {
-        Scenario scenario = SCENARIO_1;
-        mScript.pokeFakeClocks(scenario)
-                .pokeTimeDetectionEnabled(true);
-
-        ManualTimeSuggestion timeSuggestion = scenario.createManualTimeSuggestionForActual();
-        final int clockIncrement = 1000;
-
-        mScript.simulateTimePassing(clockIncrement)
-                .simulateManualTimeSuggestion(timeSuggestion)
-                .verifySystemClockWasNotSetAndResetCallTracking();
-    }
-
-    /**
-     * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
-     * like the real thing should, it also asserts preconditions.
-     */
-    private static class FakeCallback implements TimeDetectorStrategy.Callback {
-        private boolean mTimeDetectionEnabled;
-        private boolean mWakeLockAcquired;
-        private long mElapsedRealtimeMillis;
-        private long mSystemClockMillis;
-        private int mSystemClockUpdateThresholdMillis = 2000;
-
-        // Tracking operations.
-        private boolean mSystemClockWasSet;
-        private Intent mBroadcastSent;
-
-        @Override
-        public int systemClockUpdateThresholdMillis() {
-            return mSystemClockUpdateThresholdMillis;
-        }
-
-        @Override
-        public boolean isAutoTimeDetectionEnabled() {
-            return mTimeDetectionEnabled;
-        }
-
-        @Override
-        public void acquireWakeLock() {
-            if (mWakeLockAcquired) {
-                fail("Wake lock already acquired");
-            }
-            mWakeLockAcquired = true;
-        }
-
-        @Override
-        public long elapsedRealtimeMillis() {
-            assertWakeLockAcquired();
-            return mElapsedRealtimeMillis;
-        }
-
-        @Override
-        public long systemClockMillis() {
-            assertWakeLockAcquired();
-            return mSystemClockMillis;
-        }
-
-        @Override
-        public void setSystemClock(long newTimeMillis) {
-            assertWakeLockAcquired();
-            mSystemClockWasSet = true;
-            mSystemClockMillis = newTimeMillis;
-        }
-
-        @Override
-        public void releaseWakeLock() {
-            assertWakeLockAcquired();
-            mWakeLockAcquired = false;
-        }
-
-        @Override
-        public void sendStickyBroadcast(Intent intent) {
-            assertNotNull(intent);
-            mBroadcastSent = intent;
-        }
-
-        // Methods below are for managing the fake's behavior.
-
-        public void pokeSystemClockUpdateThreshold(int thresholdMillis) {
-            mSystemClockUpdateThresholdMillis = thresholdMillis;
-        }
-
-        public void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
-            mElapsedRealtimeMillis = elapsedRealtimeMillis;
-        }
-
-        public void pokeSystemClockMillis(long systemClockMillis) {
-            mSystemClockMillis = systemClockMillis;
-        }
-
-        public void pokeAutoTimeDetectionEnabled(boolean enabled) {
-            mTimeDetectionEnabled = enabled;
-        }
-
-        public long peekElapsedRealtimeMillis() {
-            return mElapsedRealtimeMillis;
-        }
-
-        public long peekSystemClockMillis() {
-            return mSystemClockMillis;
-        }
-
-        public void simulateTimePassing(int incrementMillis) {
-            mElapsedRealtimeMillis += incrementMillis;
-            mSystemClockMillis += incrementMillis;
-        }
-
-        public void simulateAutoTimeZoneDetectionToggle() {
-            mTimeDetectionEnabled = !mTimeDetectionEnabled;
-        }
-
-        public void verifySystemClockNotSet() {
-            assertFalse(mSystemClockWasSet);
-        }
-
-        public void verifySystemClockWasSet(long expectSystemClockMillis) {
-            assertTrue(mSystemClockWasSet);
-            assertEquals(expectSystemClockMillis, mSystemClockMillis);
-        }
-
-        public void verifyIntentWasBroadcast() {
-            assertTrue(mBroadcastSent != null);
-        }
-
-        public void verifyIntentWasNotBroadcast() {
-            assertNull(mBroadcastSent);
-        }
-
-        public void resetCallTracking() {
-            mSystemClockWasSet = false;
-            mBroadcastSent = null;
-        }
-
-        private void assertWakeLockAcquired() {
-            assertTrue("The operation must be performed only after acquiring the wakelock",
-                    mWakeLockAcquired);
-        }
-    }
-
-    /**
-     * A fluent helper class for tests.
-     */
-    private class Script {
-
-        private final FakeCallback mFakeCallback;
-        private final SimpleTimeDetectorStrategy mSimpleTimeDetectorStrategy;
-
-        Script() {
-            mFakeCallback = new FakeCallback();
-            mSimpleTimeDetectorStrategy = new SimpleTimeDetectorStrategy();
-            mSimpleTimeDetectorStrategy.initialize(mFakeCallback);
-
-        }
-
-        Script pokeTimeDetectionEnabled(boolean enabled) {
-            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
-            return this;
-        }
-
-        Script pokeFakeClocks(Scenario scenario) {
-            mFakeCallback.pokeElapsedRealtimeMillis(scenario.getInitialRealTimeMillis());
-            mFakeCallback.pokeSystemClockMillis(scenario.getInitialSystemClockMillis());
-            return this;
-        }
-
-        Script pokeThresholds(int systemClockUpdateThreshold) {
-            mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
-            return this;
-        }
-
-        long peekElapsedRealtimeMillis() {
-            return mFakeCallback.peekElapsedRealtimeMillis();
-        }
-
-        long peekSystemClockMillis() {
-            return mFakeCallback.peekSystemClockMillis();
-        }
-
-        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
-            mSimpleTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
-            return this;
-        }
-
-        Script simulateManualTimeSuggestion(ManualTimeSuggestion timeSuggestion) {
-            mSimpleTimeDetectorStrategy.suggestManualTime(timeSuggestion);
-            return this;
-        }
-
-        Script simulateAutoTimeDetectionToggle() {
-            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
-            mSimpleTimeDetectorStrategy.handleAutoTimeDetectionChanged();
-            return this;
-        }
-
-        Script simulateTimePassing(int clockIncrement) {
-            mFakeCallback.simulateTimePassing(clockIncrement);
-            return this;
-        }
-
-        Script verifySystemClockWasNotSetAndResetCallTracking() {
-            mFakeCallback.verifySystemClockNotSet();
-            mFakeCallback.verifyIntentWasNotBroadcast();
-            mFakeCallback.resetCallTracking();
-            return this;
-        }
-
-        Script verifySystemClockWasSetAndResetCallTracking(
-                long expectSystemClockMillis, boolean expectNetworkBroadcast) {
-            mFakeCallback.verifySystemClockWasSet(expectSystemClockMillis);
-            if (expectNetworkBroadcast) {
-                mFakeCallback.verifyIntentWasBroadcast();
-            }
-            mFakeCallback.resetCallTracking();
-            return this;
-        }
-    }
-
-    /**
-     * A starting scenario used during tests. Describes a fictional "physical" reality.
-     */
-    private static class Scenario {
-
-        private final long mInitialDeviceSystemClockMillis;
-        private final long mInitialDeviceRealtimeMillis;
-        private final long mActualTimeMillis;
-
-        Scenario(long initialDeviceSystemClock, long elapsedRealtime, long timeMillis) {
-            mInitialDeviceSystemClockMillis = initialDeviceSystemClock;
-            mActualTimeMillis = timeMillis;
-            mInitialDeviceRealtimeMillis = elapsedRealtime;
-        }
-
-        long getInitialRealTimeMillis() {
-            return mInitialDeviceRealtimeMillis;
-        }
-
-        long getInitialSystemClockMillis() {
-            return mInitialDeviceSystemClockMillis;
-        }
-
-        long getActualTimeMillis() {
-            return mActualTimeMillis;
-        }
-
-        PhoneTimeSuggestion createPhoneTimeSuggestionForActual(int phoneId) {
-            TimestampedValue<Long> time = new TimestampedValue<>(
-                    mInitialDeviceRealtimeMillis, mActualTimeMillis);
-            return createPhoneTimeSuggestion(phoneId, time);
-        }
-
-        ManualTimeSuggestion createManualTimeSuggestionForActual() {
-            TimestampedValue<Long> time = new TimestampedValue<>(
-                    mInitialDeviceRealtimeMillis, mActualTimeMillis);
-            return new ManualTimeSuggestion(time);
-        }
-
-        static class Builder {
-
-            private long mInitialDeviceSystemClockMillis;
-            private long mInitialDeviceRealtimeMillis;
-            private long mActualTimeMillis;
-
-            Builder setInitialDeviceSystemClockUtc(int year, int monthInYear, int day,
-                    int hourOfDay, int minute, int second) {
-                mInitialDeviceSystemClockMillis = createUtcTime(year, monthInYear, day, hourOfDay,
-                        minute, second);
-                return this;
-            }
-
-            Builder setInitialDeviceRealtimeMillis(long realtimeMillis) {
-                mInitialDeviceRealtimeMillis = realtimeMillis;
-                return this;
-            }
-
-            Builder setActualTimeUtc(int year, int monthInYear, int day, int hourOfDay,
-                    int minute, int second) {
-                mActualTimeMillis =
-                        createUtcTime(year, monthInYear, day, hourOfDay, minute, second);
-                return this;
-            }
-
-            Scenario build() {
-                return new Scenario(mInitialDeviceSystemClockMillis, mInitialDeviceRealtimeMillis,
-                        mActualTimeMillis);
-            }
-        }
-    }
-
-    private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
-            TimestampedValue<Long> utcTime) {
-        return new PhoneTimeSuggestion.Builder(phoneId)
-                .setUtcTime(utcTime)
-                .build();
-    }
-
-    private ManualTimeSuggestion createManualTimeSuggestion(long timeMillis) {
-        TimestampedValue<Long> utcTime =
-                new TimestampedValue<>(mScript.peekElapsedRealtimeMillis(), timeMillis);
-        return new ManualTimeSuggestion(utcTime);
-    }
-
-    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
-            int second) {
-        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
-        cal.clear();
-        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
-        return cal.getTimeInMillis();
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 84b495f..72a7f50 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -38,8 +38,6 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.timedetector.TimeDetectorStrategy.Callback;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -52,7 +50,6 @@
 
     private Context mMockContext;
     private StubbedTimeDetectorStrategy mStubbedTimeDetectorStrategy;
-    private Callback mMockCallback;
 
     private TimeDetectorService mTimeDetectorService;
     private HandlerThread mHandlerThread;
@@ -68,12 +65,10 @@
         mHandlerThread.start();
         mTestHandler = new TestHandler(mHandlerThread.getLooper());
 
-        mMockCallback = mock(Callback.class);
         mStubbedTimeDetectorStrategy = new StubbedTimeDetectorStrategy();
 
         mTimeDetectorService = new TimeDetectorService(
-                mMockContext, mTestHandler, mMockCallback,
-                mStubbedTimeDetectorStrategy);
+                mMockContext, mTestHandler, mStubbedTimeDetectorStrategy);
     }
 
     @After
@@ -100,13 +95,13 @@
 
     @Test
     public void testSuggestManualTime() throws Exception {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
+        doNothing().when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
 
         ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
         mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
         mTestHandler.assertTotalMessagesEnqueued(1);
 
-        verify(mMockContext).enforceCallingPermission(
+        verify(mMockContext).enforceCallingOrSelfPermission(
                 eq(android.Manifest.permission.SET_TIME),
                 anyString());
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
new file mode 100644
index 0000000..1aa3d8f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.timedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.PhoneTimeSuggestion;
+import android.content.Intent;
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.util.TimestampedValue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+
+@RunWith(AndroidJUnit4.class)
+public class TimeDetectorStrategyImplTest {
+
+    private static final TimestampedValue<Long> ARBITRARY_CLOCK_INITIALIZATION_INFO =
+            new TimestampedValue<>(
+                    123456789L /* realtimeClockMillis */,
+                    createUtcTime(1977, 1, 1, 12, 0, 0));
+
+    private static final long ARBITRARY_TEST_TIME_MILLIS = createUtcTime(2018, 1, 1, 12, 0, 0);
+
+    private static final int ARBITRARY_PHONE_ID = 123456;
+
+    private static final long ONE_DAY_MILLIS = Duration.ofDays(1).toMillis();
+
+    private Script mScript;
+
+    @Before
+    public void setUp() {
+        mScript = new Script();
+    }
+
+    @Test
+    public void testSuggestPhoneTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        int clockIncrement = 1000;
+        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_emptySuggestionIgnored() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, null);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, null);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_systemClockThreshold() {
+        int systemClockUpdateThresholdMillis = 1000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThresholdMillis)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        final int clockIncrement = 100;
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        // Send the first time signal. It should be used.
+        {
+            long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+            PhoneTimeSuggestion timeSuggestion1 =
+                    mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+            TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+            // Increment the the device clocks to simulate the passage of time.
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis1 =
+                    TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+            mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+        }
+
+        // Now send another time signal, but one that is too similar to the last one and should be
+        // stored, but not used to set the system clock.
+        {
+            int underThresholdMillis = systemClockUpdateThresholdMillis - 1;
+            PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
+                    phoneId, mScript.peekSystemClockMillis() + underThresholdMillis);
+            mScript.simulateTimePassing(clockIncrement)
+                    .simulatePhoneTimeSuggestion(timeSuggestion2)
+                    .verifySystemClockWasNotSetAndResetCallTracking()
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+        }
+
+        // Now send another time signal, but one that is on the threshold and so should be used.
+        {
+            PhoneTimeSuggestion timeSuggestion3 = mScript.generatePhoneTimeSuggestion(
+                    phoneId,
+                    mScript.peekSystemClockMillis() + systemClockUpdateThresholdMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis3 =
+                    TimeDetectorStrategy.getTimeAt(timeSuggestion3.getUtcTime(),
+                            mScript.peekElapsedRealtimeMillis());
+
+            mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis3, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phoneId, timeSuggestion3);
+        }
+    }
+
+    @Test
+    public void testSuggestPhoneTime_multiplePhoneIdsAndBucketing() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        // There are 2 phones in this test. Phone 2 has a different idea of the current time.
+        // phone1Id < phone2Id (which is important because the strategy uses the lowest ID when
+        // multiple phone suggestions are available.
+        int phone1Id = ARBITRARY_PHONE_ID;
+        int phone2Id = ARBITRARY_PHONE_ID + 1;
+        long phone1TimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        long phone2TimeMillis = phone1TimeMillis + 60000;
+
+        final int clockIncrement = 999;
+
+        // Make a suggestion with phone2Id.
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone1Id, null)
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+
+        mScript.simulateTimePassing(clockIncrement);
+
+        // Now make a different suggestion with phone1Id.
+        {
+            PhoneTimeSuggestion phone1TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone1Id, phone1TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone1TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone1TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone1Id, phone1TimeSuggestion);
+
+        }
+
+        mScript.simulateTimePassing(clockIncrement);
+
+        // Make another suggestion with phone2Id. It should be stored but not used because the
+        // phone1Id suggestion will still "win".
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasNotSetAndResetCallTracking()
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+
+        // Let enough time pass that phone1Id's suggestion should now be too old.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_BUCKET_SIZE_MILLIS);
+
+        // Make another suggestion with phone2Id. It should be used because the phoneId1
+        // is in an older "bucket".
+        {
+            PhoneTimeSuggestion phone2TimeSuggestion =
+                    mScript.generatePhoneTimeSuggestion(phone2Id, phone2TimeMillis);
+            mScript.simulateTimePassing(clockIncrement);
+
+            long expectedSystemClockMillis = phone2TimeMillis + clockIncrement;
+
+            mScript.simulatePhoneTimeSuggestion(phone2TimeSuggestion)
+                    .verifySystemClockWasSetAndResetCallTracking(
+                            expectedSystemClockMillis, true /* expectNetworkBroadcast */)
+                    .assertLatestPhoneSuggestion(phone2Id, phone2TimeSuggestion);
+        }
+    }
+
+    @Test
+    public void testSuggestPhoneTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        PhoneTimeSuggestion timeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, ARBITRARY_TEST_TIME_MILLIS);
+        mScript.simulateTimePassing(1000)
+                .simulatePhoneTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_invalidNitzReferenceTimesIgnored() {
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        PhoneTimeSuggestion timeSuggestion1 =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+        // Initialize the strategy / device with a time set from a phone suggestion.
+        mScript.simulateTimePassing(100);
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // The UTC time increment should be larger than the system clock update threshold so we
+        // know it shouldn't be ignored for other reasons.
+        long validUtcTimeMillis = utcTime1.getValue() + (2 * systemClockUpdateThreshold);
+
+        // Now supply a new signal that has an obviously bogus reference time : older than the last
+        // one.
+        long referenceTimeBeforeLastSignalMillis = utcTime1.getReferenceTimeMillis() - 1;
+        TimestampedValue<Long> utcTime2 = new TimestampedValue<>(
+                referenceTimeBeforeLastSignalMillis, validUtcTimeMillis);
+        PhoneTimeSuggestion timeSuggestion2 =
+                createPhoneTimeSuggestion(phoneId, utcTime2);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Now supply a new signal that has an obviously bogus reference time : substantially in the
+        // future.
+        long referenceTimeInFutureMillis =
+                utcTime1.getReferenceTimeMillis() + Integer.MAX_VALUE + 1;
+        TimestampedValue<Long> utcTime3 = new TimestampedValue<>(
+                referenceTimeInFutureMillis, validUtcTimeMillis);
+        PhoneTimeSuggestion timeSuggestion3 =
+                createPhoneTimeSuggestion(phoneId, utcTime3);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion3)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Just to prove validUtcTimeMillis is valid.
+        long validReferenceTimeMillis = utcTime1.getReferenceTimeMillis() + 100;
+        TimestampedValue<Long> utcTime4 = new TimestampedValue<>(
+                validReferenceTimeMillis, validUtcTimeMillis);
+        long expectedSystemClockMillis4 =
+                TimeDetectorStrategy.getTimeAt(utcTime4, mScript.peekElapsedRealtimeMillis());
+        PhoneTimeSuggestion timeSuggestion4 =
+                createPhoneTimeSuggestion(phoneId, utcTime4);
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion4)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis4, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion4);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_timeDetectionToggled() {
+        final int clockIncrementMillis = 100;
+        final int systemClockUpdateThreshold = 2000;
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeThresholds(systemClockUpdateThreshold)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion timeSuggestion1 =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        TimestampedValue<Long> utcTime1 = timeSuggestion1.getUtcTime();
+
+        // Simulate time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        // Simulate the time signal being received. It should not be used because auto time
+        // detection is off but it should be recorded.
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion1)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis1 =
+                TimeDetectorStrategy.getTimeAt(utcTime1, mScript.peekElapsedRealtimeMillis());
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis1, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Turn off auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion1);
+
+        // Receive another valid time signal.
+        // It should be on the threshold and accounting for the clock increments.
+        PhoneTimeSuggestion timeSuggestion2 = mScript.generatePhoneTimeSuggestion(
+                phoneId, mScript.peekSystemClockMillis() + systemClockUpdateThreshold);
+
+        // Simulate more time passing.
+        mScript.simulateTimePassing(clockIncrementMillis);
+
+        long expectedSystemClockMillis2 = TimeDetectorStrategy.getTimeAt(
+                timeSuggestion2.getUtcTime(), mScript.peekElapsedRealtimeMillis());
+
+        // The new time, though valid, should not be set in the system clock because auto time is
+        // disabled.
+        mScript.simulatePhoneTimeSuggestion(timeSuggestion2)
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+
+        // Turn on auto time detection.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis2, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, timeSuggestion2);
+    }
+
+    @Test
+    public void testSuggestPhoneTime_maxSuggestionAge() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion phoneSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        int clockIncrementMillis = 1000;
+
+        mScript.simulateTimePassing(clockIncrementMillis)
+                .simulatePhoneTimeSuggestion(phoneSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        testTimeMillis + clockIncrementMillis, true /* expectedNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+
+        // Look inside and check what the strategy considers the current best phone suggestion.
+        assertEquals(phoneSuggestion, mScript.peekBestPhoneSuggestion());
+
+        // Simulate time passing, long enough that phoneSuggestion is now too old.
+        mScript.simulateTimePassing(TimeDetectorStrategyImpl.PHONE_MAX_AGE_MILLIS);
+
+        // Look inside and check what the strategy considers the current best phone suggestion. It
+        // should still be the, it's just no longer used.
+        assertNull(mScript.peekBestPhoneSuggestion());
+        mScript.assertLatestPhoneSuggestion(phoneId, phoneSuggestion);
+    }
+
+    @Test
+    public void testSuggestManualTime_autoTimeDisabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(false);
+
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        ManualTimeSuggestion timeSuggestion = mScript.generateManualTimeSuggestion(testTimeMillis);
+        final int clockIncrement = 1000;
+        long expectedSystemClockMillis = testTimeMillis + clockIncrement;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateManualTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedSystemClockMillis, false /* expectNetworkBroadcast */);
+    }
+
+    @Test
+    public void testSuggestManualTime_retainsAutoSignal() {
+        // Configure the start state.
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        int phoneId = ARBITRARY_PHONE_ID;
+
+        // Simulate a phone suggestion.
+        long testTimeMillis = ARBITRARY_TEST_TIME_MILLIS;
+        PhoneTimeSuggestion phoneTimeSuggestion =
+                mScript.generatePhoneTimeSuggestion(phoneId, testTimeMillis);
+        long expectedAutoClockMillis = phoneTimeSuggestion.getUtcTime().getValue();
+        final int clockIncrement = 1000;
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        mScript.simulatePhoneTimeSuggestion(phoneTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Switch to manual.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Simulate a manual suggestion 1 day different from the auto suggestion.
+        long manualTimeMillis = testTimeMillis + ONE_DAY_MILLIS;
+        long expectedManualClockMillis = manualTimeMillis;
+        ManualTimeSuggestion manualTimeSuggestion =
+                mScript.generateManualTimeSuggestion(manualTimeMillis);
+        mScript.simulateManualTimeSuggestion(manualTimeSuggestion)
+                .verifySystemClockWasSetAndResetCallTracking(
+                        expectedManualClockMillis, false /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Simulate the passage of time.
+        mScript.simulateTimePassing(clockIncrement);
+        expectedAutoClockMillis += clockIncrement;
+
+        // Switch back to auto.
+        mScript.simulateAutoTimeDetectionToggle();
+
+        mScript.verifySystemClockWasSetAndResetCallTracking(
+                        expectedAutoClockMillis, true /* expectNetworkBroadcast */)
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+
+        // Switch back to manual - nothing should happen to the clock.
+        mScript.simulateAutoTimeDetectionToggle()
+                .verifySystemClockWasNotSetAndResetCallTracking()
+                .assertLatestPhoneSuggestion(phoneId, phoneTimeSuggestion);
+    }
+
+    /**
+     * Manual suggestions should be ignored if auto time is enabled.
+     */
+    @Test
+    public void testSuggestManualTime_autoTimeEnabled() {
+        mScript.pokeFakeClocks(ARBITRARY_CLOCK_INITIALIZATION_INFO)
+                .pokeAutoTimeDetectionEnabled(true);
+
+        ManualTimeSuggestion timeSuggestion =
+                mScript.generateManualTimeSuggestion(ARBITRARY_TEST_TIME_MILLIS);
+        final int clockIncrement = 1000;
+
+        mScript.simulateTimePassing(clockIncrement)
+                .simulateManualTimeSuggestion(timeSuggestion)
+                .verifySystemClockWasNotSetAndResetCallTracking();
+    }
+
+    /**
+     * A fake implementation of TimeDetectorStrategy.Callback. Besides tracking changes and behaving
+     * like the real thing should, it also asserts preconditions.
+     */
+    private static class FakeCallback implements TimeDetectorStrategy.Callback {
+        private boolean mAutoTimeDetectionEnabled;
+        private boolean mWakeLockAcquired;
+        private long mElapsedRealtimeMillis;
+        private long mSystemClockMillis;
+        private int mSystemClockUpdateThresholdMillis = 2000;
+
+        // Tracking operations.
+        private boolean mSystemClockWasSet;
+        private Intent mBroadcastSent;
+
+        @Override
+        public int systemClockUpdateThresholdMillis() {
+            return mSystemClockUpdateThresholdMillis;
+        }
+
+        @Override
+        public boolean isAutoTimeDetectionEnabled() {
+            return mAutoTimeDetectionEnabled;
+        }
+
+        @Override
+        public void acquireWakeLock() {
+            if (mWakeLockAcquired) {
+                fail("Wake lock already acquired");
+            }
+            mWakeLockAcquired = true;
+        }
+
+        @Override
+        public long elapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        @Override
+        public long systemClockMillis() {
+            assertWakeLockAcquired();
+            return mSystemClockMillis;
+        }
+
+        @Override
+        public void setSystemClock(long newTimeMillis) {
+            assertWakeLockAcquired();
+            mSystemClockWasSet = true;
+            mSystemClockMillis = newTimeMillis;
+        }
+
+        @Override
+        public void releaseWakeLock() {
+            assertWakeLockAcquired();
+            mWakeLockAcquired = false;
+        }
+
+        @Override
+        public void sendStickyBroadcast(Intent intent) {
+            assertNotNull(intent);
+            mBroadcastSent = intent;
+        }
+
+        // Methods below are for managing the fake's behavior.
+
+        void pokeSystemClockUpdateThreshold(int thresholdMillis) {
+            mSystemClockUpdateThresholdMillis = thresholdMillis;
+        }
+
+        void pokeElapsedRealtimeMillis(long elapsedRealtimeMillis) {
+            mElapsedRealtimeMillis = elapsedRealtimeMillis;
+        }
+
+        void pokeSystemClockMillis(long systemClockMillis) {
+            mSystemClockMillis = systemClockMillis;
+        }
+
+        void pokeAutoTimeDetectionEnabled(boolean enabled) {
+            mAutoTimeDetectionEnabled = enabled;
+        }
+
+        long peekElapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        long peekSystemClockMillis() {
+            return mSystemClockMillis;
+        }
+
+        void simulateTimePassing(long incrementMillis) {
+            mElapsedRealtimeMillis += incrementMillis;
+            mSystemClockMillis += incrementMillis;
+        }
+
+        void simulateAutoTimeZoneDetectionToggle() {
+            mAutoTimeDetectionEnabled = !mAutoTimeDetectionEnabled;
+        }
+
+        void verifySystemClockNotSet() {
+            assertFalse(mSystemClockWasSet);
+        }
+
+        void verifySystemClockWasSet(long expectedSystemClockMillis) {
+            assertTrue(mSystemClockWasSet);
+            assertEquals(expectedSystemClockMillis, mSystemClockMillis);
+        }
+
+        void verifyIntentWasBroadcast() {
+            assertTrue(mBroadcastSent != null);
+        }
+
+        void verifyIntentWasNotBroadcast() {
+            assertNull(mBroadcastSent);
+        }
+
+        void resetCallTracking() {
+            mSystemClockWasSet = false;
+            mBroadcastSent = null;
+        }
+
+        private void assertWakeLockAcquired() {
+            assertTrue("The operation must be performed only after acquiring the wakelock",
+                    mWakeLockAcquired);
+        }
+    }
+
+    /**
+     * A fluent helper class for tests.
+     */
+    private class Script {
+
+        private final FakeCallback mFakeCallback;
+        private final TimeDetectorStrategyImpl mTimeDetectorStrategy;
+
+        Script() {
+            mFakeCallback = new FakeCallback();
+            mTimeDetectorStrategy = new TimeDetectorStrategyImpl();
+            mTimeDetectorStrategy.initialize(mFakeCallback);
+
+        }
+
+        Script pokeAutoTimeDetectionEnabled(boolean enabled) {
+            mFakeCallback.pokeAutoTimeDetectionEnabled(enabled);
+            return this;
+        }
+
+        Script pokeFakeClocks(TimestampedValue<Long> timeInfo) {
+            mFakeCallback.pokeElapsedRealtimeMillis(timeInfo.getReferenceTimeMillis());
+            mFakeCallback.pokeSystemClockMillis(timeInfo.getValue());
+            return this;
+        }
+
+        Script pokeThresholds(int systemClockUpdateThreshold) {
+            mFakeCallback.pokeSystemClockUpdateThreshold(systemClockUpdateThreshold);
+            return this;
+        }
+
+        long peekElapsedRealtimeMillis() {
+            return mFakeCallback.peekElapsedRealtimeMillis();
+        }
+
+        long peekSystemClockMillis() {
+            return mFakeCallback.peekSystemClockMillis();
+        }
+
+        Script simulatePhoneTimeSuggestion(PhoneTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestPhoneTime(timeSuggestion);
+            return this;
+        }
+
+        Script simulateManualTimeSuggestion(ManualTimeSuggestion timeSuggestion) {
+            mTimeDetectorStrategy.suggestManualTime(timeSuggestion);
+            return this;
+        }
+
+        Script simulateAutoTimeDetectionToggle() {
+            mFakeCallback.simulateAutoTimeZoneDetectionToggle();
+            mTimeDetectorStrategy.handleAutoTimeDetectionChanged();
+            return this;
+        }
+
+        Script simulateTimePassing(long clockIncrementMillis) {
+            mFakeCallback.simulateTimePassing(clockIncrementMillis);
+            return this;
+        }
+
+        Script verifySystemClockWasNotSetAndResetCallTracking() {
+            mFakeCallback.verifySystemClockNotSet();
+            mFakeCallback.verifyIntentWasNotBroadcast();
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+
+        Script verifySystemClockWasSetAndResetCallTracking(
+                long expectedSystemClockMillis, boolean expectNetworkBroadcast) {
+            mFakeCallback.verifySystemClockWasSet(expectedSystemClockMillis);
+            if (expectNetworkBroadcast) {
+                mFakeCallback.verifyIntentWasBroadcast();
+            }
+            mFakeCallback.resetCallTracking();
+            return this;
+        }
+
+        /**
+         * White box test info: Asserts the latest suggestion for the phone ID is as expected.
+         */
+        Script assertLatestPhoneSuggestion(int phoneId, PhoneTimeSuggestion expected) {
+            assertEquals(expected, mTimeDetectorStrategy.getLatestPhoneSuggestion(phoneId));
+            return this;
+        }
+
+        /**
+         * White box test info: Returns the phone suggestion that would be used, if any, given the
+         * current elapsed real time clock.
+         */
+        PhoneTimeSuggestion peekBestPhoneSuggestion() {
+            return mTimeDetectorStrategy.findBestPhoneSuggestionForTests();
+        }
+
+        /**
+         * Generates a ManualTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        ManualTimeSuggestion generateManualTimeSuggestion(long timeMillis) {
+            TimestampedValue<Long> utcTime =
+                    new TimestampedValue<>(mFakeCallback.peekElapsedRealtimeMillis(), timeMillis);
+            return new ManualTimeSuggestion(utcTime);
+        }
+
+        /**
+         * Generates a PhoneTimeSuggestion using the current elapsed realtime clock for the
+         * reference time.
+         */
+        PhoneTimeSuggestion generatePhoneTimeSuggestion(int phoneId, Long timeMillis) {
+            TimestampedValue<Long> time = null;
+            if (timeMillis != null) {
+                time = new TimestampedValue<>(peekElapsedRealtimeMillis(), timeMillis);
+            }
+            return createPhoneTimeSuggestion(phoneId, time);
+        }
+    }
+
+    private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
+            TimestampedValue<Long> utcTime) {
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(utcTime)
+                .build();
+    }
+
+    private static long createUtcTime(int year, int monthInYear, int day, int hourOfDay, int minute,
+            int second) {
+        Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("Etc/UTC"));
+        cal.clear();
+        cal.set(year, monthInYear - 1, day, hourOfDay, minute, second);
+        return cal.getTimeInMillis();
+    }
+}
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 73faf9f..3940a3b 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -116,7 +116,8 @@
             ApnSetting.TYPE_CBS,
             ApnSetting.TYPE_IA,
             ApnSetting.TYPE_EMERGENCY,
-            ApnSetting.TYPE_MCX
+            ApnSetting.TYPE_MCX,
+            ApnSetting.TYPE_XCAP,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApnType {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 4071e95..261e8d00 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2989,7 +2989,6 @@
         /**
          * Location information during (and after) an emergency call is only provided over control
          * plane signaling from the network.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
 
@@ -2997,7 +2996,6 @@
          * Location information during (and after) an emergency call is provided over the data
          * plane and serviced by the framework GNSS service, but if it fails, the carrier also
          * supports control plane backup signaling.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
 
@@ -3005,7 +3003,6 @@
          * Location information during (and after) an emergency call is provided over the data plane
          * and serviced by the framework GNSS service only. There is no backup signalling over the
          * control plane if it fails.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
 
@@ -3113,11 +3110,21 @@
          * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
          * <p>
          * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
-         * @hide
          */
         public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
                 + "es_supl_control_plane_support_int";
 
+        /**
+         * A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
+         * get a user's location in the event that data plane SUPL fails or is otherwise
+         * unavailable.
+         * <p>
+         * A string array of PLMNs that do not support a control-plane mechanism for getting a
+         * user's location for SUPL ES.
+         */
+        public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
+                KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -3134,6 +3141,7 @@
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
             defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                     SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
+            defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
             return defaults;
         }
     }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index bbf746fc..fc717e7 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -24,8 +24,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.TransportType;
-
 import android.telephony.Annotation.NetworkType;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -90,7 +90,7 @@
      * Dual Connectivity(EN-DC).
      * @hide
      */
-    public static final int NR_STATE_NONE = -1;
+    public static final int NR_STATE_NONE = 0;
 
     /**
      * The device is camped on an LTE cell that supports E-UTRA-NR Dual Connectivity(EN-DC) but
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 1e601cf..9ace86c 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -102,7 +102,7 @@
      * Indicates frequency range is unknown.
      * @hide
      */
-    public static final int FREQUENCY_RANGE_UNKNOWN = -1;
+    public static final int FREQUENCY_RANGE_UNKNOWN = 0;
 
     /**
      * Indicates the frequency range is below 1GHz.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f1abe04..4fb26fd 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -703,31 +703,6 @@
     public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
 
     /**
-     * Broadcast intent action indicating that a precise call state
-     * (cellular) on the device has changed.
-     *
-     * <p>
-     * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
-     * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
-     * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_RINGING_CALL_STATE
-     * @see #EXTRA_FOREGROUND_CALL_STATE
-     * @see #EXTRA_BACKGROUND_CALL_STATE
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PRECISE_CALL_STATE_CHANGED =
-            "android.intent.action.PRECISE_CALL_STATE";
-
-    /**
      * Broadcast intent action indicating that call disconnect cause has changed.
      *
      * <p>
@@ -749,78 +724,6 @@
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
      * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current ringing call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current foreground call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current background call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
      * containing the disconnect cause.
      *
      * @see DisconnectCause
@@ -849,88 +752,6 @@
     public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
 
     /**
-     * Broadcast intent action indicating a data connection has changed,
-     * providing precise information about the connection.
-     *
-     * <p>
-     * The {@link #EXTRA_DATA_STATE} extra indicates the connection state.
-     * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
-     * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
-     * The {@link #EXTRA_DATA_APN} extra indicates the APN.
-     * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
-     * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_DATA_STATE
-     * @see #EXTRA_DATA_NETWORK_TYPE
-     * @see #EXTRA_DATA_APN_TYPE
-     * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_IFACE
-     * @see #EXTRA_DATA_FAILURE_CAUSE
-     * @hide
-     *
-     * @deprecated If the app is running in the background, it won't be able to receive this
-     * broadcast. Apps should use ConnectivityManager {@link #registerNetworkCallback(
-     * android.net.NetworkRequest, ConnectivityManager.NetworkCallback)} to listen for network
-     * changes.
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @Deprecated
-    @UnsupportedAppUsage
-    public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
-            "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current data connection.
-     *
-     * @see TelephonyManager#DATA_UNKNOWN
-     * @see TelephonyManager#DATA_DISCONNECTED
-     * @see TelephonyManager#DATA_CONNECTING
-     * @see TelephonyManager#DATA_CONNECTED
-     * @see TelephonyManager#DATA_SUSPENDED
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_STATE = PhoneConstants.STATE_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the network type.
-     *
-     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
-     * @see TelephonyManager#NETWORK_TYPE_GPRS
-     * @see TelephonyManager#NETWORK_TYPE_EDGE
-     * @see TelephonyManager#NETWORK_TYPE_UMTS
-     * @see TelephonyManager#NETWORK_TYPE_CDMA
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
-     * @see TelephonyManager#NETWORK_TYPE_1xRTT
-     * @see TelephonyManager#NETWORK_TYPE_HSDPA
-     * @see TelephonyManager#NETWORK_TYPE_HSUPA
-     * @see TelephonyManager#NETWORK_TYPE_HSPA
-     * @see TelephonyManager#NETWORK_TYPE_IDEN
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
-     * @see TelephonyManager#NETWORK_TYPE_LTE
-     * @see TelephonyManager#NETWORK_TYPE_EHRPD
-     * @see TelephonyManager#NETWORK_TYPE_HSPAP
-     * @see TelephonyManager#NETWORK_TYPE_NR
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_NETWORK_TYPE = PhoneConstants.DATA_NETWORK_TYPE_KEY;
-
-    /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String containing the data APN type.
      *
@@ -967,18 +788,6 @@
     public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for the data connection fail cause.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
-
-    /**
      * Broadcast intent action for letting the default dialer to know to show voicemail
      * notification.
      *
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 60774e7..034fc22 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -20,7 +20,7 @@
 import android.annotation.Nullable;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.hardware.radio.V1_4.ApnTypes;
+import android.hardware.radio.V1_5.ApnTypes;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -110,6 +110,8 @@
     public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
     /** APN type for MCX (Mission Critical Service) where X can be PTT/Video/Data */
     public static final int TYPE_MCX = ApnTypes.MCX;
+    /** APN type for XCAP. */
+    public static final int TYPE_XCAP = ApnTypes.XCAP;
 
     // Possible values for authentication types.
     /** No authentication type. */
@@ -198,6 +200,7 @@
         APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
         APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
         APN_TYPE_STRING_MAP.put("mcx", TYPE_MCX);
+        APN_TYPE_STRING_MAP.put("xcap", TYPE_XCAP);
         APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
         APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
         APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
@@ -210,6 +213,7 @@
         APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
         APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
         APN_TYPE_INT_MAP.put(TYPE_MCX, "mcx");
+        APN_TYPE_INT_MAP.put(TYPE_XCAP, "xcap");
 
         PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
         PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
@@ -1944,8 +1948,9 @@
          * {@link ApnSetting} built from this builder otherwise.
          */
         public ApnSetting build() {
-            if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI |
-                    TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX)) == 0
+            if ((mApnTypeBitmask & (TYPE_DEFAULT | TYPE_MMS | TYPE_SUPL | TYPE_DUN | TYPE_HIPRI
+                    | TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
+                    | TYPE_XCAP)) == 0
                 || TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
                 return null;
             }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 76b214b..f4b2cef 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -889,6 +889,12 @@
      */
     public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
 
+    /**
+     * The dialed RTT call should be retried without RTT
+     * @hide
+     */
+    public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
     /*
      * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
      * would be replaced by ERROR_UNSPECIFIED.
@@ -1069,6 +1075,7 @@
             CODE_REJECT_VT_AVPF_NOT_ALLOWED,
             CODE_REJECT_ONGOING_ENCRYPTED_CALL,
             CODE_REJECT_ONGOING_CS_CALL,
+            CODE_RETRY_ON_IMS_WITHOUT_RTT,
             CODE_OEM_CAUSE_1,
             CODE_OEM_CAUSE_2,
             CODE_OEM_CAUSE_3,
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 05e2ed9..6e63514 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -161,6 +161,8 @@
     public static final String APN_TYPE_EMERGENCY = "emergency";
     /** APN type for Mission Critical Services */
     public static final String APN_TYPE_MCX = "mcx";
+    /** APN type for XCAP */
+    public static final String APN_TYPE_XCAP = "xcap";
     /** Array of all APN types */
     public static final String[] APN_TYPES = {APN_TYPE_DEFAULT,
             APN_TYPE_MMS,
@@ -172,7 +174,8 @@
             APN_TYPE_CBS,
             APN_TYPE_IA,
             APN_TYPE_EMERGENCY,
-            APN_TYPE_MCX
+            APN_TYPE_MCX,
+            APN_TYPE_XCAP,
     };
 
     public static final int RIL_CARD_MAX_APPS    = 8;
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index aa4174a..616b6b0 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,6 +30,7 @@
     libs: [
         "framework-all",
         "app-compat-annotations",
+        "unsupportedappusage",
     ],
 
     api_packages: [