Merge "Snap for 6686656 from 83cbf59a7037698c250959f856be720d95b8a479 to sdk-release" into sdk-release
diff --git a/StubLibraries.bp b/StubLibraries.bp
index bb13eaa..26478d3 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -150,6 +150,9 @@
 module_libs = " " +
     " --show-annotation android.annotation.SystemApi\\(" +
         "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+    "\\)" +
+    " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
     "\\) "
 
 droidstubs {
@@ -222,16 +225,10 @@
 }
 
 /////////////////////////////////////////////////////////////////////
-// Following droidstubs modules are for extra APIs for modules.
-// The framework currently have two more API surfaces for modules:
-// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
+// Following droidstubs modules are for extra APIs for modules,
+// namely @SystemApi(client=MODULE_LIBRARIES) APIs.
 /////////////////////////////////////////////////////////////////////
 
-// TODO(b/146727827) remove the *-api module when we can teach metalava
-// about the relationship among the API surfaces. Currently, these modules are only to generate
-// the API signature files and ensure that the APIs evolve in a backwards compatible manner.
-// They however are NOT used for building the API stub.
-
 droidstubs {
     name: "module-lib-api",
     defaults: ["metalava-full-api-stubs-default"],
@@ -268,7 +265,7 @@
     name: "module-lib-api-stubs-docs-non-updatable",
     defaults: ["metalava-non-updatable-api-stubs-default"],
     arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args + module_libs,
+    args: metalava_framework_docs_args + priv_apps + module_libs,
     check_api: {
         current: {
             api_file: "non-updatable-api/module-lib-current.txt",
@@ -277,17 +274,6 @@
     },
 }
 
-// The following droidstub module generates source files for the API stub library for
-// modules. Note that it not only includes its own APIs but also other APIs that have
-// narrower scope (all @SystemApis, not just the ones with 'client=MODULE_LIBRARIES').
-
-droidstubs {
-    name: "module-lib-api-stubs-docs",
-    defaults: ["metalava-non-updatable-api-stubs-default"],
-    arg_files: ["core/res/AndroidManifest.xml"],
-    args: metalava_framework_docs_args + priv_apps + module_libs,
-}
-
 /////////////////////////////////////////////////////////////////////
 // android_*_stubs_current modules are the stubs libraries compiled
 // from *-api-stubs-docs
@@ -333,7 +319,7 @@
 
 java_library_static {
     name: "android_module_lib_stubs_current",
-    srcs: [ ":module-lib-api-stubs-docs" ],
+    srcs: [ ":module-lib-api-stubs-docs-non-updatable" ],
     defaults: ["android_defaults_stubs_current"],
     libs: ["sdk_system_29_android"],
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index 380b4c6..f34ecbd 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -53,6 +53,9 @@
 module_libs = " " +
     " --show-annotation android.annotation.SystemApi\\(" +
         "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+    "\\)" +
+    " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+        "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
     "\\) "
 
 mainline_service_stubs_args =
diff --git a/api/current.txt b/api/current.txt
index fea1f2f..f1338f0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45143,7 +45143,7 @@
   public abstract class CellLocation {
     ctor public CellLocation();
     method public static android.telephony.CellLocation getEmpty();
-    method public static void requestLocationUpdate();
+    method @Deprecated public static void requestLocationUpdate();
   }
 
   public abstract class CellSignalStrength {
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 355fa18..283af06 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -11,24 +11,6 @@
 
 package android.net {
 
-  public final class TetheredClient implements android.os.Parcelable {
-    ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
-    method public int describeContents();
-    method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
-    method @NonNull public android.net.MacAddress getMacAddress();
-    method public int getTetheringType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
-  }
-
-  public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.net.LinkAddress getAddress();
-    method @Nullable public String getHostname();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
-  }
-
   public final class TetheringConstants {
     field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -48,69 +30,15 @@
     method @NonNull public String[] getTetheringErroredIfaces();
     method public boolean isTetheringSupported();
     method public boolean isTetheringSupported(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
     method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
     method @Deprecated public int setUsbTethering(boolean);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
     method @Deprecated public int tether(@NonNull String);
-    method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
     method @Deprecated public int untether(@NonNull String);
-    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
-    field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
-    field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
-    field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
-    field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
-    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
-    field public static final int TETHERING_ETHERNET = 5; // 0x5
-    field public static final int TETHERING_INVALID = -1; // 0xffffffff
-    field public static final int TETHERING_NCM = 4; // 0x4
-    field public static final int TETHERING_USB = 1; // 0x1
-    field public static final int TETHERING_WIFI = 0; // 0x0
-    field public static final int TETHERING_WIFI_P2P = 3; // 0x3
-    field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
-    field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
-    field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
-    field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
-    field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
-    field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
-    field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
-    field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
-    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
-    field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
-    field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
-    field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
-    field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
-    field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
-    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
-    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
-    field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
-    field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
-    field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
-  }
-
-  public static interface TetheringManager.OnTetheringEntitlementResultListener {
-    method public void onTetheringEntitlementResult(int);
-  }
-
-  public static interface TetheringManager.StartTetheringCallback {
-    method public default void onTetheringFailed(int);
-    method public default void onTetheringStarted();
   }
 
   public static interface TetheringManager.TetheringEventCallback {
-    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
-    method public default void onError(@NonNull String, int);
-    method public default void onOffloadStatusChanged(int);
     method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
-    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheringSupported(boolean);
-    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
   public static class TetheringManager.TetheringInterfaceRegexps {
@@ -119,21 +47,5 @@
     method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
   }
 
-  public static class TetheringManager.TetheringRequest {
-    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
-    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
-    method public boolean getShouldShowEntitlementUi();
-    method public int getTetheringType();
-    method public boolean isExemptFromEntitlementCheck();
-  }
-
-  public static class TetheringManager.TetheringRequest.Builder {
-    ctor public TetheringManager.TetheringRequest.Builder(int);
-    method @NonNull public android.net.TetheringManager.TetheringRequest build();
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
-    method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
-  }
-
 }
 
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
index 4ac0098..a468439 100644
--- a/core/java/android/annotation/SystemApi.java
+++ b/core/java/android/annotation/SystemApi.java
@@ -23,7 +23,6 @@
 import static java.lang.annotation.ElementType.PACKAGE;
 import static java.lang.annotation.ElementType.TYPE;
 
-import java.lang.annotation.Repeatable;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
@@ -41,7 +40,6 @@
  */
 @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
 @Retention(RetentionPolicy.RUNTIME)
-@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable
 public @interface SystemApi {
     enum Client {
         /**
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index f962ea0..47bd207 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1784,6 +1784,7 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
+                if (DBG) Log.d(TAG, "removeActiveDevice, profiles: " + profiles);
                 return mService.removeActiveDevice(profiles);
             }
         } catch (RemoteException e) {
@@ -1828,6 +1829,9 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
+                if (DBG) {
+                    Log.d(TAG, "setActiveDevice, device: " + device + ", profiles: " + profiles);
+                }
                 return mService.setActiveDevice(device, profiles);
             }
         } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 4f5c4fe..df11d3a 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -52,6 +52,18 @@
     public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
             "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
 
+    /**
+     * Action to notify read status changed
+     */
+    public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
+            "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
+
+    /**
+     * Action to notify deleted status changed
+     */
+    public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
+            "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
+
     /* Extras used in ACTION_MESSAGE_RECEIVED intent.
      * NOTE: HANDLE is only valid for a single session with the device. */
     public static final String EXTRA_MESSAGE_HANDLE =
@@ -65,6 +77,25 @@
     public static final String EXTRA_SENDER_CONTACT_NAME =
             "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
 
+    /**
+     * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED
+     * Contains the MAP message deleted status
+     * Possible values are:
+     * true: deleted
+     * false: undeleted
+     */
+    public static final String EXTRA_MESSAGE_DELETED_STATUS =
+            "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
+
+    /**
+     * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED
+     * Possible values are:
+     * 0: failure
+     * 1: success
+     */
+    public static final String EXTRA_RESULT_CODE =
+            "android.bluetooth.device.extra.RESULT_CODE";
+
     /** There was an error trying to obtain the state */
     public static final int STATE_ERROR = -1;
 
@@ -75,6 +106,12 @@
 
     private static final int UPLOADING_FEATURE_BITMASK = 0x08;
 
+    /** Parameters in setMessageStatus */
+    public static final int UNREAD = 0;
+    public static final int READ = 1;
+    public static final int UNDELETED = 2;
+    public static final int DELETED = 3;
+
     private BluetoothAdapter mAdapter;
     private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
             new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
@@ -405,6 +442,38 @@
         return false;
     }
 
+    /**
+     * Set message status of message on MSE
+     * <p>
+     * When read status changed, the result will be published via
+     * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED}
+     * When deleted status changed, the result will be published via
+     * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED}
+     *
+     * @param device Bluetooth device
+     * @param handle message handle
+     * @param status <code>UNREAD</code> for "unread", <code>READ</code> for
+     *            "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
+     *            "deleted", otherwise return error
+     * @return <code>true</code> if request has been sent, <code>false</code> on error
+     *
+     */
+    @RequiresPermission(Manifest.permission.READ_SMS)
+    public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
+        if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
+        final IBluetoothMapClient service = getService();
+        if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
+            (status == READ || status == UNREAD || status == UNDELETED  || status == DELETED)) {
+            try {
+                return service.setMessageStatus(device, handle, status);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        return false;
+    }
+
     private boolean isEnabled() {
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 0e10c42..0eb3c1e 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -38,7 +38,9 @@
  * Representation of a MAC address.
  *
  * This class only supports 48 bits long addresses and does not support 64 bits long addresses.
- * Instances of this class are immutable.
+ * Instances of this class are immutable. This class provides implementations of hashCode()
+ * and equals() that make it suitable for use as keys in standard implementations of
+ * {@link java.util.Map}.
  */
 public final class MacAddress implements Parcelable {
 
@@ -122,12 +124,22 @@
     }
 
     /**
+     * Convert this MacAddress to a byte array.
+     *
+     * The returned array is in network order. For example, if this MacAddress is 1:2:3:4:5:6,
+     * the returned array is [1, 2, 3, 4, 5, 6].
+     *
      * @return a byte array representation of this MacAddress.
      */
     public @NonNull byte[] toByteArray() {
         return byteAddrFromLongAddr(mAddr);
     }
 
+    /**
+     * Returns a human-readable representation of this MacAddress.
+     * The exact format is implementation-dependent and should not be assumed to have any
+     * particular format.
+     */
     @Override
     public @NonNull String toString() {
         return stringAddrFromLongAddr(mAddr);
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index cb9463a..4b806e7 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -84,6 +84,15 @@
      * @hide
      */
     public static final int NETWORK_TYPE_ALL = -1;
+    /**
+     * Virtual RAT type to represent 5G NSA (Non Stand Alone) mode, where the primary cell is
+     * still LTE and network allocates a secondary 5G cell so telephony reports RAT = LTE along
+     * with NR state as connected. This should not be overlapped with any of the
+     * {@code TelephonyManager.NETWORK_TYPE_*} constants.
+     *
+     * @hide
+     */
+    public static final int NETWORK_TYPE_5G_NSA = -2;
 
     private static boolean isKnownMatchRule(final int rule) {
         switch (rule) {
@@ -472,6 +481,9 @@
                 return TelephonyManager.NETWORK_TYPE_LTE;
             case TelephonyManager.NETWORK_TYPE_NR:
                 return TelephonyManager.NETWORK_TYPE_NR;
+            // Virtual RAT type for 5G NSA mode, see {@link NetworkTemplate#NETWORK_TYPE_5G_NSA}.
+            case NetworkTemplate.NETWORK_TYPE_5G_NSA:
+                return NetworkTemplate.NETWORK_TYPE_5G_NSA;
             default:
                 return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 7c42c36..64ab1d7 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -96,6 +96,15 @@
         throws RemoteException, NoSuchElementException;
 
     /**
+     * This allows getService to bypass the VINTF manifest for testing only.
+     *
+     * Disabled on user builds.
+     * @hide
+     */
+    public static native final void setTrebleTestingOverride(
+            boolean testingOverride);
+
+    /**
      * Configures how many threads the process-wide hwbinder threadpool
      * has to process incoming requests.
      *
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index a95fe3c..7946dda 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1331,8 +1331,7 @@
                 Object[] messages;
                 try {
                     messages = (Object[]) intent.getSerializableExtra("pdus");
-                }
-                catch (ClassCastException e) {
+                } catch (ClassCastException e) {
                     Rlog.e(TAG, "getMessagesFromIntent: " + e);
                     return null;
                 }
@@ -1344,9 +1343,12 @@
 
                 String format = intent.getStringExtra("format");
                 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
-                        SubscriptionManager.getDefaultSmsSubscriptionId());
-
-                Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
+                        SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+                if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                    Rlog.v(TAG, "getMessagesFromIntent with valid subId : " + subId);
+                } else {
+                    Rlog.v(TAG, "getMessagesFromIntent");
+                }
 
                 int pduCount = messages.length;
                 SmsMessage[] msgs = new SmsMessage[pduCount];
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 0863a81..683c747 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -19,12 +19,12 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
+import android.icu.text.DateTimePatternGenerator;
 import android.provider.Settings;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
 
-import libcore.icu.ICU;
 import libcore.icu.LocaleData;
 
 import java.text.SimpleDateFormat;
@@ -251,7 +251,8 @@
      * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}.
      */
     public static String getBestDateTimePattern(Locale locale, String skeleton) {
-        return ICU.getBestDateTimePattern(skeleton, locale);
+        DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale);
+        return dtpg.getBestPattern(skeleton);
     }
 
     /**
@@ -333,7 +334,52 @@
      * order returned here.
      */
     public static char[] getDateFormatOrder(Context context) {
-        return ICU.getDateFormatOrder(getDateFormatString(context));
+        return getDateFormatOrder(getDateFormatString(context));
+    }
+
+    /**
+     * @hide Used by internal framework class {@link android.widget.DatePickerSpinnerDelegate}.
+     */
+    public static char[] getDateFormatOrder(String pattern) {
+        char[] result = new char[3];
+        int resultIndex = 0;
+        boolean sawDay = false;
+        boolean sawMonth = false;
+        boolean sawYear = false;
+
+        for (int i = 0; i < pattern.length(); ++i) {
+            char ch = pattern.charAt(i);
+            if (ch == 'd' || ch == 'L' || ch == 'M' || ch == 'y') {
+                if (ch == 'd' && !sawDay) {
+                    result[resultIndex++] = 'd';
+                    sawDay = true;
+                } else if ((ch == 'L' || ch == 'M') && !sawMonth) {
+                    result[resultIndex++] = 'M';
+                    sawMonth = true;
+                } else if ((ch == 'y') && !sawYear) {
+                    result[resultIndex++] = 'y';
+                    sawYear = true;
+                }
+            } else if (ch == 'G') {
+                // Ignore the era specifier, if present.
+            } else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
+                throw new IllegalArgumentException("Bad pattern character '" + ch + "' in "
+                    + pattern);
+            } else if (ch == '\'') {
+                if (i < pattern.length() - 1 && pattern.charAt(i + 1) == '\'') {
+                    ++i;
+                } else {
+                    i = pattern.indexOf('\'', i + 1);
+                    if (i == -1) {
+                        throw new IllegalArgumentException("Bad quoting in " + pattern);
+                    }
+                    ++i;
+                }
+            } else {
+                // Ignore spaces and punctuation.
+            }
+        }
+        return result;
     }
 
     private static String getDateFormatString(Context context) {
diff --git a/core/java/android/text/format/DateTimeFormat.java b/core/java/android/text/format/DateTimeFormat.java
new file mode 100644
index 0000000..064d717
--- /dev/null
+++ b/core/java/android/text/format/DateTimeFormat.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2015 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.text.format;
+
+import android.icu.text.DateFormat;
+import android.icu.text.DateTimePatternGenerator;
+import android.icu.text.DisplayContext;
+import android.icu.text.SimpleDateFormat;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+/**
+ * A formatter that outputs a single date/time.
+ *
+ * @hide
+ */
+class DateTimeFormat {
+    private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+    static class FormatterCache extends LruCache<String, DateFormat> {
+        FormatterCache() {
+            super(8);
+        }
+    }
+
+    private DateTimeFormat() {
+    }
+
+    public static String format(ULocale icuLocale, Calendar time, int flags,
+            DisplayContext displayContext) {
+        String skeleton = DateUtilsBridge.toSkeleton(time, flags);
+        String key = skeleton + "\t" + icuLocale + "\t" + time.getTimeZone();
+        synchronized (CACHED_FORMATTERS) {
+            DateFormat formatter = CACHED_FORMATTERS.get(key);
+            if (formatter == null) {
+                DateTimePatternGenerator generator = DateTimePatternGenerator.getInstance(
+                        icuLocale);
+                formatter = new SimpleDateFormat(generator.getBestPattern(skeleton), icuLocale);
+                CACHED_FORMATTERS.put(key, formatter);
+            }
+            formatter.setContext(displayContext);
+            return formatter.format(time);
+        }
+    }
+}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index f236f19..b0253a0 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -29,7 +29,6 @@
 
 import libcore.icu.DateIntervalFormat;
 import libcore.icu.LocaleData;
-import libcore.icu.RelativeDateTimeFormatter;
 
 import java.io.IOException;
 import java.util.Calendar;
diff --git a/core/java/android/text/format/DateUtilsBridge.java b/core/java/android/text/format/DateUtilsBridge.java
new file mode 100644
index 0000000..370d999
--- /dev/null
+++ b/core/java/android/text/format/DateUtilsBridge.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 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.text.format;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.util.Calendar;
+import android.icu.util.GregorianCalendar;
+import android.icu.util.TimeZone;
+import android.icu.util.ULocale;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Common methods and constants for the various ICU formatters used to support {@link
+ * android.text.format.DateUtils}.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class DateUtilsBridge {
+    // These are all public API in DateUtils. There are others, but they're either for use with
+    // other methods (like FORMAT_ABBREV_RELATIVE), don't internationalize (like FORMAT_CAP_AMPM),
+    // or have never been implemented anyway.
+    public static final int FORMAT_SHOW_TIME = 0x00001;
+    public static final int FORMAT_SHOW_WEEKDAY = 0x00002;
+    public static final int FORMAT_SHOW_YEAR = 0x00004;
+    public static final int FORMAT_NO_YEAR = 0x00008;
+    public static final int FORMAT_SHOW_DATE = 0x00010;
+    public static final int FORMAT_NO_MONTH_DAY = 0x00020;
+    public static final int FORMAT_12HOUR = 0x00040;
+    public static final int FORMAT_24HOUR = 0x00080;
+    public static final int FORMAT_UTC = 0x02000;
+    public static final int FORMAT_ABBREV_TIME = 0x04000;
+    public static final int FORMAT_ABBREV_WEEKDAY = 0x08000;
+    public static final int FORMAT_ABBREV_MONTH = 0x10000;
+    public static final int FORMAT_NUMERIC_DATE = 0x20000;
+    public static final int FORMAT_ABBREV_RELATIVE = 0x40000;
+    public static final int FORMAT_ABBREV_ALL = 0x80000;
+
+    /**
+     * Creates an immutable ICU timezone backed by the specified libcore timezone data. At the time
+     * of writing the libcore implementation is faster but restricted to 1902 - 2038. Callers must
+     * not modify the {@code tz} after calling this method.
+     */
+    public static TimeZone icuTimeZone(java.util.TimeZone tz) {
+        TimeZone icuTimeZone = TimeZone.getTimeZone(tz.getID());
+        icuTimeZone.freeze(); // Optimization - allows the timezone to be copied cheaply.
+        return icuTimeZone;
+    }
+
+    /**
+     * Create a GregorianCalendar based on the arguments
+     */
+    public static Calendar createIcuCalendar(TimeZone icuTimeZone, ULocale icuLocale,
+            long timeInMillis) {
+        Calendar calendar = new GregorianCalendar(icuTimeZone, icuLocale);
+        calendar.setTimeInMillis(timeInMillis);
+        return calendar;
+    }
+
+    public static String toSkeleton(Calendar calendar, int flags) {
+        return toSkeleton(calendar, calendar, flags);
+    }
+
+    public static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) {
+        if ((flags & FORMAT_ABBREV_ALL) != 0) {
+            flags |= FORMAT_ABBREV_MONTH | FORMAT_ABBREV_TIME | FORMAT_ABBREV_WEEKDAY;
+        }
+
+        String monthPart = "MMMM";
+        if ((flags & FORMAT_NUMERIC_DATE) != 0) {
+            monthPart = "M";
+        } else if ((flags & FORMAT_ABBREV_MONTH) != 0) {
+            monthPart = "MMM";
+        }
+
+        String weekPart = "EEEE";
+        if ((flags & FORMAT_ABBREV_WEEKDAY) != 0) {
+            weekPart = "EEE";
+        }
+
+        String timePart = "j"; // "j" means choose 12 or 24 hour based on current locale.
+        if ((flags & FORMAT_24HOUR) != 0) {
+            timePart = "H";
+        } else if ((flags & FORMAT_12HOUR) != 0) {
+            timePart = "h";
+        }
+
+        // If we've not been asked to abbreviate times, or we're using the 24-hour clock (where it
+        // never makes sense to leave out the minutes), include minutes. This gets us times like
+        // "4 PM" while avoiding times like "16" (for "16:00").
+        if ((flags & FORMAT_ABBREV_TIME) == 0 || (flags & FORMAT_24HOUR) != 0) {
+            timePart += "m";
+        } else {
+            // Otherwise, we're abbreviating a 12-hour time, and should only show the minutes
+            // if they're not both "00".
+            if (!(onTheHour(startCalendar) && onTheHour(endCalendar))) {
+                timePart = timePart + "m";
+            }
+        }
+
+        if (fallOnDifferentDates(startCalendar, endCalendar)) {
+            flags |= FORMAT_SHOW_DATE;
+        }
+
+        if (fallInSameMonth(startCalendar, endCalendar) && (flags & FORMAT_NO_MONTH_DAY) != 0) {
+            flags &= (~FORMAT_SHOW_WEEKDAY);
+            flags &= (~FORMAT_SHOW_TIME);
+        }
+
+        if ((flags & (FORMAT_SHOW_DATE | FORMAT_SHOW_TIME | FORMAT_SHOW_WEEKDAY)) == 0) {
+            flags |= FORMAT_SHOW_DATE;
+        }
+
+        // If we've been asked to show the date, work out whether we think we should show the year.
+        if ((flags & FORMAT_SHOW_DATE) != 0) {
+            if ((flags & FORMAT_SHOW_YEAR) != 0) {
+                // The caller explicitly wants us to show the year.
+            } else if ((flags & FORMAT_NO_YEAR) != 0) {
+                // The caller explicitly doesn't want us to show the year, even if we otherwise
+                // would.
+            } else if (!fallInSameYear(startCalendar, endCalendar) || !isThisYear(startCalendar)) {
+                flags |= FORMAT_SHOW_YEAR;
+            }
+        }
+
+        StringBuilder builder = new StringBuilder();
+        if ((flags & (FORMAT_SHOW_DATE | FORMAT_NO_MONTH_DAY)) != 0) {
+            if ((flags & FORMAT_SHOW_YEAR) != 0) {
+                builder.append("y");
+            }
+            builder.append(monthPart);
+            if ((flags & FORMAT_NO_MONTH_DAY) == 0) {
+                builder.append("d");
+            }
+        }
+        if ((flags & FORMAT_SHOW_WEEKDAY) != 0) {
+            builder.append(weekPart);
+        }
+        if ((flags & FORMAT_SHOW_TIME) != 0) {
+            builder.append(timePart);
+        }
+        return builder.toString();
+    }
+
+    public static int dayDistance(Calendar c1, Calendar c2) {
+        return c2.get(Calendar.JULIAN_DAY) - c1.get(Calendar.JULIAN_DAY);
+    }
+
+    /**
+     * Returns whether the argument will be displayed as if it were midnight, using any of the
+     * skeletons provided by {@link #toSkeleton}.
+     */
+    public static boolean isDisplayMidnightUsingSkeleton(Calendar c) {
+        // All the skeletons returned by toSkeleton have minute precision (they may abbreviate
+        // 4:00 PM to 4 PM but will still show the following minute as 4:01 PM).
+        return c.get(Calendar.HOUR_OF_DAY) == 0 && c.get(Calendar.MINUTE) == 0;
+    }
+
+    private static boolean onTheHour(Calendar c) {
+        return c.get(Calendar.MINUTE) == 0 && c.get(Calendar.SECOND) == 0;
+    }
+
+    private static boolean fallOnDifferentDates(Calendar c1, Calendar c2) {
+        return c1.get(Calendar.YEAR) != c2.get(Calendar.YEAR)
+                || c1.get(Calendar.MONTH) != c2.get(Calendar.MONTH)
+                || c1.get(Calendar.DAY_OF_MONTH) != c2.get(Calendar.DAY_OF_MONTH);
+    }
+
+    private static boolean fallInSameMonth(Calendar c1, Calendar c2) {
+        return c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH);
+    }
+
+    private static boolean fallInSameYear(Calendar c1, Calendar c2) {
+        return c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR);
+    }
+
+    private static boolean isThisYear(Calendar c) {
+        Calendar now = (Calendar) c.clone();
+        now.setTimeInMillis(System.currentTimeMillis());
+        return c.get(Calendar.YEAR) == now.get(Calendar.YEAR);
+    }
+}
diff --git a/core/java/android/text/format/RelativeDateTimeFormatter.java b/core/java/android/text/format/RelativeDateTimeFormatter.java
new file mode 100644
index 0000000..c5bca17
--- /dev/null
+++ b/core/java/android/text/format/RelativeDateTimeFormatter.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2015 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.text.format;
+
+import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_MONTH;
+import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR;
+import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtilsBridge.FORMAT_SHOW_DATE;
+import static android.text.format.DateUtilsBridge.FORMAT_SHOW_TIME;
+import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.icu.text.DisplayContext;
+import android.icu.util.Calendar;
+import android.icu.util.ULocale;
+import android.util.LruCache;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+
+/**
+ * Exposes icu4j's RelativeDateTimeFormatter.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public final class RelativeDateTimeFormatter {
+
+    public static final long SECOND_IN_MILLIS = 1000;
+    public static final long MINUTE_IN_MILLIS = SECOND_IN_MILLIS * 60;
+    public static final long HOUR_IN_MILLIS = MINUTE_IN_MILLIS * 60;
+    public static final long DAY_IN_MILLIS = HOUR_IN_MILLIS * 24;
+    public static final long WEEK_IN_MILLIS = DAY_IN_MILLIS * 7;
+    // YEAR_IN_MILLIS considers 364 days as a year. However, since this
+    // constant comes from public API in DateUtils, it cannot be fixed here.
+    public static final long YEAR_IN_MILLIS = WEEK_IN_MILLIS * 52;
+
+    private static final int DAY_IN_MS = 24 * 60 * 60 * 1000;
+    private static final int EPOCH_JULIAN_DAY = 2440588;
+
+    private static final FormatterCache CACHED_FORMATTERS = new FormatterCache();
+
+    static class FormatterCache
+            extends LruCache<String, android.icu.text.RelativeDateTimeFormatter> {
+        FormatterCache() {
+            super(8);
+        }
+    }
+
+    private RelativeDateTimeFormatter() {
+    }
+
+    /**
+     * This is the internal API that implements the functionality of DateUtils
+     * .getRelativeTimeSpanString(long,
+     * long, long, int), which is to return a string describing 'time' as a time relative to 'now'
+     * such as '5 minutes ago', or 'In 2 days'. More examples can be found in DateUtils' doc.
+     * <p>
+     * In the implementation below, it selects the appropriate time unit based on the elapsed time
+     * between time' and 'now', e.g. minutes, days and etc. Callers may also specify the desired
+     * minimum resolution to show in the result. For example, '45 minutes ago' will become '0 hours
+     * ago' when minResolution is HOUR_IN_MILLIS. Once getting the quantity and unit to display, it
+     * calls icu4j's RelativeDateTimeFormatter to format the actual string according to the given
+     * locale.
+     * <p>
+     * Note that when minResolution is set to DAY_IN_MILLIS, it returns the result depending on the
+     * actual date difference. For example, it will return 'Yesterday' even if 'time' was less than
+     * 24 hours ago but falling onto a different calendar day.
+     * <p>
+     * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
+     * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
+     * in 'flags' to get the abbreviated forms when available. When 'time' equals to 'now', it
+     * always // returns a string like '0 seconds/minutes/... ago' according to minResolution.
+     */
+    public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+            long now, long minResolution, int flags) {
+        // Android has been inconsistent about capitalization in the past. e.g. bug
+        // http://b/20247811.
+        // Now we capitalize everything consistently.
+        final DisplayContext displayContext =
+                DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE;
+        return getRelativeTimeSpanString(locale, tz, time, now, minResolution, flags,
+                displayContext);
+    }
+
+    public static String getRelativeTimeSpanString(Locale locale, java.util.TimeZone tz, long time,
+            long now, long minResolution, int flags, DisplayContext displayContext) {
+        if (locale == null) {
+            throw new NullPointerException("locale == null");
+        }
+        if (tz == null) {
+            throw new NullPointerException("tz == null");
+        }
+        ULocale icuLocale = ULocale.forLocale(locale);
+        android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+        return getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution, flags,
+                displayContext);
+    }
+
+    private static String getRelativeTimeSpanString(ULocale icuLocale,
+            android.icu.util.TimeZone icuTimeZone, long time, long now, long minResolution,
+            int flags,
+            DisplayContext displayContext) {
+
+        long duration = Math.abs(now - time);
+        boolean past = (now >= time);
+
+        android.icu.text.RelativeDateTimeFormatter.Style style;
+        if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+            style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+        } else {
+            style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
+        }
+
+        android.icu.text.RelativeDateTimeFormatter.Direction direction;
+        if (past) {
+            direction = android.icu.text.RelativeDateTimeFormatter.Direction.LAST;
+        } else {
+            direction = android.icu.text.RelativeDateTimeFormatter.Direction.NEXT;
+        }
+
+        // 'relative' defaults to true as we are generating relative time span
+        // string. It will be set to false when we try to display strings without
+        // a quantity, such as 'Yesterday', etc.
+        boolean relative = true;
+        int count;
+        android.icu.text.RelativeDateTimeFormatter.RelativeUnit unit;
+        android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit aunit = null;
+
+        if (duration < MINUTE_IN_MILLIS && minResolution < MINUTE_IN_MILLIS) {
+            count = (int) (duration / SECOND_IN_MILLIS);
+            unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.SECONDS;
+        } else if (duration < HOUR_IN_MILLIS && minResolution < HOUR_IN_MILLIS) {
+            count = (int) (duration / MINUTE_IN_MILLIS);
+            unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.MINUTES;
+        } else if (duration < DAY_IN_MILLIS && minResolution < DAY_IN_MILLIS) {
+            // Even if 'time' actually happened yesterday, we don't format it as
+            // "Yesterday" in this case. Unless the duration is longer than a day,
+            // or minResolution is specified as DAY_IN_MILLIS by user.
+            count = (int) (duration / HOUR_IN_MILLIS);
+            unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.HOURS;
+        } else if (duration < WEEK_IN_MILLIS && minResolution < WEEK_IN_MILLIS) {
+            count = Math.abs(dayDistance(icuTimeZone, time, now));
+            unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.DAYS;
+
+            if (count == 2) {
+                // Some locales have special terms for "2 days ago". Return them if
+                // available. Note that we cannot set up direction and unit here and
+                // make it fall through to use the call near the end of the function,
+                // because for locales that don't have special terms for "2 days ago",
+                // icu4j returns an empty string instead of falling back to strings
+                // like "2 days ago".
+                String str;
+                if (past) {
+                    synchronized (CACHED_FORMATTERS) {
+                        str = getFormatter(icuLocale, style, displayContext).format(
+                                android.icu.text.RelativeDateTimeFormatter.Direction.LAST_2,
+                                android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+                    }
+                } else {
+                    synchronized (CACHED_FORMATTERS) {
+                        str = getFormatter(icuLocale, style, displayContext).format(
+                                android.icu.text.RelativeDateTimeFormatter.Direction.NEXT_2,
+                                android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY);
+                    }
+                }
+                if (str != null && !str.isEmpty()) {
+                    return str;
+                }
+                // Fall back to show something like "2 days ago".
+            } else if (count == 1) {
+                // Show "Yesterday / Tomorrow" instead of "1 day ago / In 1 day".
+                aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+                relative = false;
+            } else if (count == 0) {
+                // Show "Today" if time and now are on the same day.
+                aunit = android.icu.text.RelativeDateTimeFormatter.AbsoluteUnit.DAY;
+                direction = android.icu.text.RelativeDateTimeFormatter.Direction.THIS;
+                relative = false;
+            }
+        } else if (minResolution == WEEK_IN_MILLIS) {
+            count = (int) (duration / WEEK_IN_MILLIS);
+            unit = android.icu.text.RelativeDateTimeFormatter.RelativeUnit.WEEKS;
+        } else {
+            Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+            // The duration is longer than a week and minResolution is not
+            // WEEK_IN_MILLIS. Return the absolute date instead of relative time.
+
+            // Bug 19822016:
+            // If user doesn't supply the year display flag, we need to explicitly
+            // set that to show / hide the year based on time and now. Otherwise
+            // formatDateRange() would determine that based on the current system
+            // time and may give wrong results.
+            if ((flags & (FORMAT_NO_YEAR | FORMAT_SHOW_YEAR)) == 0) {
+                Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale,
+                        now);
+
+                if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+                    flags |= FORMAT_SHOW_YEAR;
+                } else {
+                    flags |= FORMAT_NO_YEAR;
+                }
+            }
+            return DateTimeFormat.format(icuLocale, timeCalendar, flags, displayContext);
+        }
+
+        synchronized (CACHED_FORMATTERS) {
+            android.icu.text.RelativeDateTimeFormatter formatter =
+                    getFormatter(icuLocale, style, displayContext);
+            if (relative) {
+                return formatter.format(count, direction, unit);
+            } else {
+                return formatter.format(direction, aunit);
+            }
+        }
+    }
+
+    /**
+     * This is the internal API that implements DateUtils.getRelativeDateTimeString(long, long,
+     * long, long, int), which is to return a string describing 'time' as a time relative to 'now',
+     * formatted like '[relative time/date], [time]'. More examples can be found in DateUtils' doc.
+     * <p>
+     * The function is similar to getRelativeTimeSpanString, but it always appends the absolute time
+     * to the relative time string to return '[relative time/date clause], [absolute time clause]'.
+     * It also takes an extra parameter transitionResolution to determine the format of the date
+     * clause. When the elapsed time is less than the transition resolution, it displays the
+     * relative time string. Otherwise, it gives the absolute numeric date string as the date
+     * clause. With the date and time clauses, it relies on icu4j's
+     * RelativeDateTimeFormatter::combineDateAndTime()
+     * to concatenate the two.
+     * <p>
+     * It takes two additional parameters of Locale and TimeZone than the DateUtils' API. Caller
+     * must specify the locale and timezone. FORMAT_ABBREV_RELATIVE or FORMAT_ABBREV_ALL can be set
+     * in 'flags' to get the abbreviated forms when they are available.
+     * <p>
+     * Bug 5252772: Since the absolute time will always be part of the result, minResolution will be
+     * set to at least DAY_IN_MILLIS to correctly indicate the date difference. For example, when
+     * it's 1:30 AM, it will return 'Yesterday, 11:30 PM' for getRelativeDateTimeString(null, null,
+     * now - 2 hours, now, HOUR_IN_MILLIS, DAY_IN_MILLIS, 0), instead of '2 hours ago, 11:30 PM'
+     * even with minResolution being HOUR_IN_MILLIS.
+     */
+    public static String getRelativeDateTimeString(Locale locale, java.util.TimeZone tz, long time,
+            long now, long minResolution, long transitionResolution, int flags) {
+
+        if (locale == null) {
+            throw new NullPointerException("locale == null");
+        }
+        if (tz == null) {
+            throw new NullPointerException("tz == null");
+        }
+        ULocale icuLocale = ULocale.forLocale(locale);
+        android.icu.util.TimeZone icuTimeZone = DateUtilsBridge.icuTimeZone(tz);
+
+        long duration = Math.abs(now - time);
+        // It doesn't make much sense to have results like: "1 week ago, 10:50 AM".
+        if (transitionResolution > WEEK_IN_MILLIS) {
+            transitionResolution = WEEK_IN_MILLIS;
+        }
+        android.icu.text.RelativeDateTimeFormatter.Style style;
+        if ((flags & (FORMAT_ABBREV_RELATIVE | FORMAT_ABBREV_ALL)) != 0) {
+            style = android.icu.text.RelativeDateTimeFormatter.Style.SHORT;
+        } else {
+            style = android.icu.text.RelativeDateTimeFormatter.Style.LONG;
+        }
+
+        Calendar timeCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, time);
+        Calendar nowCalendar = DateUtilsBridge.createIcuCalendar(icuTimeZone, icuLocale, now);
+
+        int days = Math.abs(DateUtilsBridge.dayDistance(timeCalendar, nowCalendar));
+
+        // Now get the date clause, either in relative format or the actual date.
+        String dateClause;
+        if (duration < transitionResolution) {
+            // This is to fix bug 5252772. If there is any date difference, we should
+            // promote the minResolution to DAY_IN_MILLIS so that it can display the
+            // date instead of "x hours/minutes ago, [time]".
+            if (days > 0 && minResolution < DAY_IN_MILLIS) {
+                minResolution = DAY_IN_MILLIS;
+            }
+            dateClause = getRelativeTimeSpanString(icuLocale, icuTimeZone, time, now, minResolution,
+                    flags, DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+        } else {
+            // We always use fixed flags to format the date clause. User-supplied
+            // flags are ignored.
+            if (timeCalendar.get(Calendar.YEAR) != nowCalendar.get(Calendar.YEAR)) {
+                // Different years
+                flags = FORMAT_SHOW_DATE | FORMAT_SHOW_YEAR | FORMAT_NUMERIC_DATE;
+            } else {
+                // Default
+                flags = FORMAT_SHOW_DATE | FORMAT_NO_YEAR | FORMAT_ABBREV_MONTH;
+            }
+
+            dateClause = DateTimeFormat.format(icuLocale, timeCalendar, flags,
+                    DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE);
+        }
+
+        String timeClause = DateTimeFormat.format(icuLocale, timeCalendar, FORMAT_SHOW_TIME,
+                DisplayContext.CAPITALIZATION_NONE);
+
+        // icu4j also has other options available to control the capitalization. We are currently
+        // using
+        // the _NONE option only.
+        DisplayContext capitalizationContext = DisplayContext.CAPITALIZATION_NONE;
+
+        // Combine the two clauses, such as '5 days ago, 10:50 AM'.
+        synchronized (CACHED_FORMATTERS) {
+            return getFormatter(icuLocale, style, capitalizationContext)
+                    .combineDateAndTime(dateClause, timeClause);
+        }
+    }
+
+    /**
+     * getFormatter() caches the RelativeDateTimeFormatter instances based on the combination of
+     * localeName, sytle and capitalizationContext. It should always be used along with the action
+     * of the formatter in a synchronized block, because otherwise the formatter returned by
+     * getFormatter() may have been evicted by the time of the call to formatter->action().
+     */
+    private static android.icu.text.RelativeDateTimeFormatter getFormatter(
+            ULocale locale, android.icu.text.RelativeDateTimeFormatter.Style style,
+            DisplayContext displayContext) {
+        String key = locale + "\t" + style + "\t" + displayContext;
+        android.icu.text.RelativeDateTimeFormatter formatter = CACHED_FORMATTERS.get(key);
+        if (formatter == null) {
+            formatter = android.icu.text.RelativeDateTimeFormatter.getInstance(
+                    locale, null, style, displayContext);
+            CACHED_FORMATTERS.put(key, formatter);
+        }
+        return formatter;
+    }
+
+    // Return the date difference for the two times in a given timezone.
+    private static int dayDistance(android.icu.util.TimeZone icuTimeZone, long startTime,
+            long endTime) {
+        return julianDay(icuTimeZone, endTime) - julianDay(icuTimeZone, startTime);
+    }
+
+    private static int julianDay(android.icu.util.TimeZone icuTimeZone, long time) {
+        long utcMs = time + icuTimeZone.getOffset(time);
+        return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY;
+    }
+}
diff --git a/core/java/android/widget/DatePickerSpinnerDelegate.java b/core/java/android/widget/DatePickerSpinnerDelegate.java
index 096e6ea..fd89b2e 100644
--- a/core/java/android/widget/DatePickerSpinnerDelegate.java
+++ b/core/java/android/widget/DatePickerSpinnerDelegate.java
@@ -34,8 +34,6 @@
 import android.widget.DatePicker.AbstractDatePickerDelegate;
 import android.widget.NumberPicker.OnValueChangeListener;
 
-import libcore.icu.ICU;
-
 import java.text.DateFormatSymbols;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -459,7 +457,7 @@
         // We use numeric spinners for year and day, but textual months. Ask icu4c what
         // order the user's locale uses for that combination. http://b/7207103.
         String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
-        char[] order = ICU.getDateFormatOrder(pattern);
+        char[] order = DateFormat.getDateFormatOrder(pattern);
         final int spinnerCount = order.length;
         for (int i = 0; i < spinnerCount; i++) {
             switch (order[i]) {
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 9ab30d3..da20395 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -109,7 +109,7 @@
      *
      * @return the swap result, index 0 is the start index and index 1 is the end index.
      */
-    private static int[] sortSelctionIndices(int selectionStart, int selectionEnd) {
+    private static int[] sortSelectionIndices(int selectionStart, int selectionEnd) {
         if (selectionStart < selectionEnd) {
             return new int[]{selectionStart, selectionEnd};
         }
@@ -123,11 +123,11 @@
      * @param textView the selected TextView.
      * @return the swap result, index 0 is the start index and index 1 is the end index.
      */
-    private static int[] sortSelctionIndicesFromTextView(TextView textView) {
+    private static int[] sortSelectionIndicesFromTextView(TextView textView) {
         int selectionStart = textView.getSelectionStart();
         int selectionEnd = textView.getSelectionEnd();
 
-        return sortSelctionIndices(selectionStart, selectionEnd);
+        return sortSelectionIndices(selectionStart, selectionEnd);
     }
 
     /**
@@ -136,7 +136,7 @@
     public void startSelectionActionModeAsync(boolean adjustSelection) {
         // Check if the smart selection should run for editable text.
         adjustSelection &= getTextClassificationSettings().isSmartSelectionEnabled();
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
 
         mSelectionTracker.onOriginalSelection(
                 getText(mTextView),
@@ -166,7 +166,7 @@
      * Starts Link ActionMode.
      */
     public void startLinkActionModeAsync(int start, int end) {
-        int[] indexResult = sortSelctionIndices(start, end);
+        int[] indexResult = sortSelectionIndices(start, end);
         mSelectionTracker.onOriginalSelection(getText(mTextView), indexResult[0], indexResult[1],
                 true /*isLink*/);
         cancelAsyncTask();
@@ -202,21 +202,21 @@
 
     /** Reports a selection action event. */
     public void onSelectionAction(int menuItemId, @Nullable String actionLabel) {
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionAction(
                 sortedSelectionIndices[0], sortedSelectionIndices[1],
                 getActionType(menuItemId), actionLabel, mTextClassification);
     }
 
     public void onSelectionDrag() {
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionAction(
                 sortedSelectionIndices[0], sortedSelectionIndices[1],
                 SelectionEvent.ACTION_DRAG, /* actionLabel= */ null, mTextClassification);
     }
 
     public void onTextChanged(int start, int end) {
-        int[] sortedSelectionIndices = sortSelctionIndices(start, end);
+        int[] sortedSelectionIndices = sortSelectionIndices(start, end);
         mSelectionTracker.onTextChanged(sortedSelectionIndices[0], sortedSelectionIndices[1],
                 mTextClassification);
     }
@@ -335,7 +335,7 @@
             startSelectionActionMode(startSelectionResult);
         };
         // TODO do not trigger the animation if the change included only non-printable characters
-        int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         final boolean didSelectionChange =
                 result != null && (sortedSelectionIndices[0] != result.mStart
                         || sortedSelectionIndices[1] != result.mEnd);
@@ -488,7 +488,7 @@
         if (actionMode != null) {
             actionMode.invalidate();
         }
-        final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+        final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
         mSelectionTracker.onSelectionUpdated(
                 sortedSelectionIndices[0], sortedSelectionIndices[1], mTextClassification);
         mTextClassificationAsyncTask = null;
@@ -497,7 +497,7 @@
     private void resetTextClassificationHelper(int selectionStart, int selectionEnd) {
         if (selectionStart < 0 || selectionEnd < 0) {
             // Use selection indices
-            int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(mTextView);
+            int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(mTextView);
             selectionStart = sortedSelectionIndices[0];
             selectionEnd = sortedSelectionIndices[1];
         }
@@ -639,7 +639,7 @@
                 mAllowReset = false;
                 boolean selected = editor.selectCurrentWord();
                 if (selected) {
-                    final int[] sortedSelectionIndices = sortSelctionIndicesFromTextView(textView);
+                    final int[] sortedSelectionIndices = sortSelectionIndicesFromTextView(textView);
                     mSelectionStart = sortedSelectionIndices[0];
                     mSelectionEnd = sortedSelectionIndices[1];
                     mLogger.logSelectionAction(
@@ -1216,7 +1216,7 @@
 
         SelectionResult(int start, int end,
                 @Nullable TextClassification classification, @Nullable TextSelection selection) {
-            int[] sortedIndices = sortSelctionIndices(start, end);
+            int[] sortedIndices = sortSelectionIndices(start, end);
             mStart = sortedIndices[0];
             mEnd = sortedIndices[1];
             mClassification = classification;
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
index cffb0ad..3d35d2f 100644
--- a/core/java/com/android/internal/os/KernelWakelockReader.java
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -153,19 +153,32 @@
     }
 
     /**
+     * Attempt to wait for suspend_control service if not immediately available.
+     */
+    private ISuspendControlService waitForSuspendControlService() throws ServiceNotFoundException {
+        final String name = "suspend_control";
+        final int numRetries = 5;
+        for (int i = 0; i < numRetries; i++) {
+            mSuspendControlService = ISuspendControlService.Stub.asInterface(
+                                        ServiceManager.getService(name));
+            if (mSuspendControlService != null) {
+                return mSuspendControlService;
+            }
+        }
+        throw new ServiceNotFoundException(name);
+    }
+
+    /**
      * On success, returns the updated stats from SystemSupend, else returns null.
      */
     private KernelWakelockStats getWakelockStatsFromSystemSuspend(
             final KernelWakelockStats staleStats) {
         WakeLockInfo[] wlStats = null;
-        if (mSuspendControlService == null) {
-            try {
-                mSuspendControlService = ISuspendControlService.Stub.asInterface(
-                    ServiceManager.getServiceOrThrow("suspend_control"));
-            } catch (ServiceNotFoundException e) {
-                Slog.wtf(TAG, "Required service suspend_control not available", e);
-                return null;
-            }
+        try {
+            mSuspendControlService = waitForSuspendControlService();
+        } catch (ServiceNotFoundException e) {
+            Slog.wtf(TAG, "Required service suspend_control not available", e);
+            return null;
         }
 
         try {
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 0c24065..7a79cc9 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -2088,10 +2088,11 @@
         pw.println(getName() + ":");
         pw.println(" total records=" + getLogRecCount());
         for (int i = 0; i < getLogRecSize(); i++) {
-            pw.println(" rec[" + i + "]: " + getLogRec(i).toString());
+            pw.println(" rec[" + i + "]: " + getLogRec(i));
             pw.flush();
         }
-        pw.println("curState=" + getCurrentState().getName());
+        final IState curState = getCurrentState();
+        pw.println("curState=" + (curState == null ? "<QUIT>" : curState.getName()));
     }
 
     @Override
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index b6427c9a..48f33a6 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -339,6 +339,10 @@
     return JHwRemoteBinder::NewObject(env, service);
 }
 
+void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) {
+    hardware::details::setTrebleTestingOverride(testingOverride);
+}
+
 void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass,
         jlong maxThreads, jboolean callerWillJoin) {
     CHECK(maxThreads > 0);
@@ -368,6 +372,9 @@
     { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;",
         (void *)JHwBinder_native_getService },
 
+    { "setTrebleTestingOverride", "(Z)V",
+        (void *)JHwBinder_native_setTrebleTestingOverride },
+
     { "configureRpcThreadpool", "(JZ)V",
         (void *)JHwBinder_native_configureRpcThreadpool },
 
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index a88f891..ff336ee 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -122,10 +122,18 @@
             std::stringstream ss;
             ss << "HwBinder Error: (" << err << ")";
 
-            jniThrowException(
-                    env,
-                    canThrowRemoteException ? "android/os/RemoteException" : "java/lang/RuntimeException",
-                    ss.str().c_str());
+            const char* exception = nullptr;
+            if (canThrowRemoteException) {
+                if (err == DEAD_OBJECT) {
+                    exception = "android/os/DeadObjectException";
+                } else {
+                    exception = "android/os/RemoteException";
+                }
+            } else {
+                exception = "java/lang/RuntimeException";
+            }
+
+            jniThrowException(env, exception, ss.str().c_str());
 
             break;
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cc988ff..85a22420 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -228,6 +228,8 @@
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" />
     <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" />
+    <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED" />
     <protected-broadcast
         android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
     <protected-broadcast
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 7f9d874..6849a90 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -15,14 +15,18 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.bluetooth.tests" >
+          package="com.android.bluetooth.tests"
+          android:sharedUserId="android.uid.bluetooth" >
 
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
     <uses-permission android:name="android.permission.BROADCAST_STICKY" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.READ_SMS"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 4b32cea..89dbe3f 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -360,6 +360,30 @@
         mTestUtils.unpair(mAdapter, device);
     }
 
+    /* Make sure there is at least 1 unread message in the last week on remote device */
+    public void testMceSetMessageStatus() {
+        int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations;
+        if (iterations == 0) {
+            return;
+        }
+
+        BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+        mTestUtils.enable(mAdapter);
+        mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null);
+        mTestUtils.mceGetUnreadMessage(mAdapter, device);
+
+        for (int i = 0; i < iterations; i++) {
+            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ);
+            mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD);
+        }
+
+        /**
+         * It is hard to find device to support set undeleted status, so just
+         * set deleted in 1 iteration
+         **/
+        mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED);
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
index 56e691d..d19c2c3 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
@@ -40,6 +40,7 @@
  *     [-e connect_input_iterations <iterations>] \
  *     [-e connect_pan_iterations <iterations>] \
  *     [-e start_stop_sco_iterations <iterations>] \
+ *     [-e mce_set_message_status_iterations <iterations>] \
  *     [-e pair_address <address>] \
  *     [-e headset_address <address>] \
  *     [-e a2dp_address <address>] \
@@ -64,6 +65,7 @@
     public static int sConnectInputIterations = 100;
     public static int sConnectPanIterations = 100;
     public static int sStartStopScoIterations = 100;
+    public static int sMceSetMessageStatusIterations = 100;
 
     public static String sDeviceAddress = "";
     public static byte[] sDevicePairPin = {'1', '2', '3', '4'};
@@ -173,6 +175,15 @@
             }
         }
 
+        val = arguments.getString("mce_set_message_status_iterations");
+        if (val != null) {
+            try {
+                sMceSetMessageStatusIterations = Integer.parseInt(val);
+            } catch (NumberFormatException e) {
+                // Invalid argument, fall back to default value
+            }
+        }
+
         val = arguments.getString("device_address");
         if (val != null) {
             sDeviceAddress = val;
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ed613c3..409025b 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -56,6 +56,10 @@
     private static final int CONNECT_PROXY_TIMEOUT = 5000;
     /** Time between polls in ms. */
     private static final int POLL_TIME = 100;
+    /** Timeout to get map message in ms. */
+    private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000;
+    /** Timeout to set map message status in ms. */
+    private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000;
 
     private abstract class FlagReceiver extends BroadcastReceiver {
         private int mExpectedFlags = 0;
@@ -98,6 +102,8 @@
         private static final int STATE_TURNING_ON_FLAG = 1 << 6;
         private static final int STATE_ON_FLAG = 1 << 7;
         private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+        private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9;
+        private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10;
 
         public BluetoothReceiver(int expectedFlags) {
             super(expectedFlags);
@@ -231,6 +237,9 @@
                 case BluetoothProfile.PAN:
                     mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
                     break;
+                case BluetoothProfile.MAP_CLIENT:
+                    mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
+                    break;
                 default:
                     mConnectionAction = null;
             }
@@ -308,6 +317,34 @@
         }
     }
 
+
+    private class MceSetMessageStatusReceiver extends FlagReceiver {
+        private static final int MESSAGE_RECEIVED_FLAG = 1;
+        private static final int STATUS_CHANGED_FLAG = 1 << 1;
+
+        public MceSetMessageStatusReceiver(int expectedFlags) {
+            super(expectedFlags);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
+                String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE);
+                assertNotNull(handle);
+                setFiredFlag(MESSAGE_RECEIVED_FLAG);
+                mMsgHandle = handle;
+            } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) {
+                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+                setFiredFlag(STATUS_CHANGED_FLAG);
+            } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) {
+                int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+                assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+                setFiredFlag(STATUS_CHANGED_FLAG);
+            }
+        }
+    }
+
     private BluetoothProfile.ServiceListener mServiceListener =
             new BluetoothProfile.ServiceListener() {
         @Override
@@ -326,6 +363,9 @@
                     case BluetoothProfile.PAN:
                         mPan = (BluetoothPan) proxy;
                         break;
+                    case BluetoothProfile.MAP_CLIENT:
+                        mMce = (BluetoothMapClient) proxy;
+                        break;
                 }
             }
         }
@@ -346,6 +386,9 @@
                     case BluetoothProfile.PAN:
                         mPan = null;
                         break;
+                    case BluetoothProfile.MAP_CLIENT:
+                        mMce = null;
+                        break;
                 }
             }
         }
@@ -362,6 +405,8 @@
     private BluetoothHeadset mHeadset = null;
     private BluetoothHidHost mInput = null;
     private BluetoothPan mPan = null;
+    private BluetoothMapClient mMce = null;
+    private String mMsgHandle = null;
 
     /**
      * Creates a utility instance for testing Bluetooth.
@@ -898,7 +943,7 @@
      * @param adapter The BT adapter.
      * @param device The remote device.
      * @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
-     * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
+     * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}..
      * @param methodName The method name to printed in the logs.  If null, will be
      * "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
      */
@@ -941,6 +986,8 @@
                     assertTrue(((BluetoothHeadset)proxy).connect(device));
                 } else if (profile == BluetoothProfile.HID_HOST) {
                     assertTrue(((BluetoothHidHost)proxy).connect(device));
+                } else if (profile == BluetoothProfile.MAP_CLIENT) {
+                    assertTrue(((BluetoothMapClient)proxy).connect(device));
                 }
                 break;
             default:
@@ -1016,6 +1063,8 @@
                     assertTrue(((BluetoothHeadset)proxy).disconnect(device));
                 } else if (profile == BluetoothProfile.HID_HOST) {
                     assertTrue(((BluetoothHidHost)proxy).disconnect(device));
+                } else if (profile == BluetoothProfile.MAP_CLIENT) {
+                    assertTrue(((BluetoothMapClient)proxy).disconnect(device));
                 }
                 break;
             case BluetoothProfile.STATE_DISCONNECTED:
@@ -1373,6 +1422,89 @@
         }
     }
 
+    public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) {
+        int mask;
+        String methodName = "getUnreadMessage";
+
+        if (!adapter.isEnabled()) {
+            fail(String.format("%s bluetooth not enabled", methodName));
+        }
+
+        if (!adapter.getBondedDevices().contains(device)) {
+            fail(String.format("%s device not paired", methodName));
+        }
+
+        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+        assertNotNull(mMce);
+
+        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+            fail(String.format("%s device is not connected", methodName));
+        }
+
+        mMsgHandle = null;
+        mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG;
+        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+        assertTrue(mMce.getUnreadMessages(device));
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) {
+            if ((receiver.getFiredFlags() & mask) == mask) {
+                writeOutput(String.format("%s completed", methodName));
+                removeReceiver(receiver);
+                return;
+            }
+            sleep(POLL_TIME);
+        }
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+                methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask));
+    }
+
+    /**
+     * Set a message to read/unread/deleted/undeleted
+     */
+    public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) {
+        int mask;
+        String methodName = "setMessageStatus";
+
+        if (!adapter.isEnabled()) {
+            fail(String.format("%s bluetooth not enabled", methodName));
+        }
+
+        if (!adapter.getBondedDevices().contains(device)) {
+            fail(String.format("%s device not paired", methodName));
+        }
+
+        mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+        assertNotNull(mMce);
+
+        if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+            fail(String.format("%s device is not connected", methodName));
+        }
+
+        assertNotNull(mMsgHandle);
+        mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG;
+        MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+
+        assertTrue(mMce.setMessageStatus(device, mMsgHandle, status));
+
+        long s = System.currentTimeMillis();
+        while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) {
+            if ((receiver.getFiredFlags() & mask) == mask) {
+                writeOutput(String.format("%s completed", methodName));
+                removeReceiver(receiver);
+                return;
+            }
+            sleep(POLL_TIME);
+        }
+
+        int firedFlags = receiver.getFiredFlags();
+        removeReceiver(receiver);
+        fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+                methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask));
+    }
+
     private void addReceiver(BroadcastReceiver receiver, String[] actions) {
         IntentFilter filter = new IntentFilter();
         for (String action: actions) {
@@ -1408,7 +1540,8 @@
         String[] actions = {
                 BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
                 BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
-                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED};
+                BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED,
+                BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED};
         ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
                 expectedFlags);
         addReceiver(receiver, actions);
@@ -1430,6 +1563,16 @@
         return receiver;
     }
 
+    private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device,
+            int expectedFlags) {
+        String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED,
+            BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED,
+            BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED};
+        MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags);
+        addReceiver(receiver, actions);
+        return receiver;
+    }
+
     private void removeReceiver(BroadcastReceiver receiver) {
         mContext.unregisterReceiver(receiver);
         mReceivers.remove(receiver);
@@ -1456,6 +1599,10 @@
                 if (mPan != null) {
                     return mPan;
                 }
+            case BluetoothProfile.MAP_CLIENT:
+                if (mMce != null) {
+                    return mMce;
+                }
                 break;
             default:
                 return null;
@@ -1483,6 +1630,11 @@
                     sleep(POLL_TIME);
                 }
                 return mPan;
+            case BluetoothProfile.MAP_CLIENT:
+                while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+                    sleep(POLL_TIME);
+                }
+                return mMce;
             default:
                 return null;
         }
diff --git a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
index d00d052..0af8c72 100644
--- a/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
+++ b/core/tests/coretests/src/android/service/euicc/EuiccProfileInfoTest.java
@@ -154,6 +154,29 @@
     }
 
     @Test
+    public void testBuilder_BasedOnAnotherProfileWithEmptyAccessRules() {
+        EuiccProfileInfo p =
+                new EuiccProfileInfo.Builder("21430000000000006587")
+                        .setNickname("profile nickname")
+                        .setProfileName("profile name")
+                        .setServiceProviderName("service provider")
+                        .setCarrierIdentifier(
+                                new CarrierIdentifier(
+                                        new byte[] {0x23, 0x45, 0x67},
+                                        "123",
+                                        "45"))
+                        .setState(EuiccProfileInfo.PROFILE_STATE_ENABLED)
+                        .setProfileClass(EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL)
+                        .setPolicyRules(EuiccProfileInfo.POLICY_RULE_DO_NOT_DELETE)
+                        .setUiccAccessRule(null)
+                        .build();
+
+        EuiccProfileInfo copied = new EuiccProfileInfo.Builder(p).build();
+
+        assertEquals(null, copied.getUiccAccessRules());
+    }
+
+    @Test
     public void testEqualsHashCode() {
         EuiccProfileInfo p =
                 new EuiccProfileInfo.Builder("21430000000000006587")
diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java
index 5a0a84d..fa1d56f 100644
--- a/core/tests/coretests/src/android/text/format/DateFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java
@@ -16,8 +16,10 @@
 
 package android.text.format;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
 
@@ -27,6 +29,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
 import java.util.Locale;
 
 @Presubmit
@@ -55,4 +58,71 @@
         assertFalse(DateFormat.is24HourLocale(Locale.US));
         assertTrue(DateFormat.is24HourLocale(Locale.GERMANY));
     }
+
+    @Test
+    public void testGetDateFormatOrder() {
+        // lv and fa use differing orders depending on whether you're using numeric or
+        // textual months.
+        Locale lv = new Locale("lv");
+        assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(lv, "yyyy-M-dd"))));
+        assertEquals("[y, d, M]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(lv, "yyyy-MMM-dd"))));
+        assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(lv, "MMM-dd"))));
+        Locale fa = new Locale("fa");
+        assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(fa, "yyyy-M-dd"))));
+        assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(fa, "yyyy-MMM-dd"))));
+        assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(fa, "MMM-dd"))));
+
+        // English differs on each side of the Atlantic.
+        Locale enUS = Locale.US;
+        assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enUS, "yyyy-M-dd"))));
+        assertEquals("[M, d, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enUS, "yyyy-MMM-dd"))));
+        assertEquals("[M, d, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enUS, "MMM-dd"))));
+        Locale enGB = Locale.UK;
+        assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enGB, "yyyy-M-dd"))));
+        assertEquals("[d, M, y]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enGB, "yyyy-MMM-dd"))));
+        assertEquals("[d, M, \u0000]", Arrays.toString(DateFormat.getDateFormatOrder(
+                best(enGB, "MMM-dd"))));
+
+        assertEquals("[y, M, d]", Arrays.toString(DateFormat.getDateFormatOrder(
+                "yyyy - 'why' '' 'ddd' MMM-dd")));
+
+        try {
+            DateFormat.getDateFormatOrder("the quick brown fox jumped over the lazy dog");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            DateFormat.getDateFormatOrder("'");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            DateFormat.getDateFormatOrder("yyyy'");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            DateFormat.getDateFormatOrder("yyyy'MMM");
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    private static String best(Locale l, String skeleton) {
+        return DateFormat.getBestDateTimePattern(l, skeleton);
+    }
 }
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
new file mode 100644
index 0000000..d9ba8fb
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2015 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.text.format;
+
+import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_ALL;
+import static android.text.format.DateUtilsBridge.FORMAT_ABBREV_RELATIVE;
+import static android.text.format.DateUtilsBridge.FORMAT_NO_YEAR;
+import static android.text.format.DateUtilsBridge.FORMAT_NUMERIC_DATE;
+import static android.text.format.DateUtilsBridge.FORMAT_SHOW_YEAR;
+import static android.text.format.RelativeDateTimeFormatter.DAY_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.HOUR_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.MINUTE_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.SECOND_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.WEEK_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.YEAR_IN_MILLIS;
+import static android.text.format.RelativeDateTimeFormatter.getRelativeDateTimeString;
+import static android.text.format.RelativeDateTimeFormatter.getRelativeTimeSpanString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RelativeDateTimeFormatterTest {
+
+    // Tests adopted from CTS tests for DateUtils.getRelativeTimeSpanString.
+    @Test
+    public void test_getRelativeTimeSpanStringCTS() throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        Calendar cal = Calendar.getInstance(tz, en_US);
+        // Feb 5, 2015 at 10:50 GMT
+        cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+        final long baseTime = cal.getTimeInMillis();
+
+        assertEquals("0 minutes ago",
+                getRelativeTimeSpanString(en_US, tz, baseTime - SECOND_IN_MILLIS, baseTime,
+                        MINUTE_IN_MILLIS, 0));
+        assertEquals("In 0 minutes",
+                getRelativeTimeSpanString(en_US, tz, baseTime + SECOND_IN_MILLIS, baseTime,
+                        MINUTE_IN_MILLIS, 0));
+
+        assertEquals("1 minute ago",
+                getRelativeTimeSpanString(en_US, tz, 0, MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, 0));
+        assertEquals("In 1 minute",
+                getRelativeTimeSpanString(en_US, tz, MINUTE_IN_MILLIS, 0, MINUTE_IN_MILLIS, 0));
+
+        assertEquals("42 minutes ago",
+                getRelativeTimeSpanString(en_US, tz, baseTime - 42 * MINUTE_IN_MILLIS, baseTime,
+                        MINUTE_IN_MILLIS, 0));
+        assertEquals("In 42 minutes",
+                getRelativeTimeSpanString(en_US, tz, baseTime + 42 * MINUTE_IN_MILLIS, baseTime,
+                        MINUTE_IN_MILLIS, 0));
+
+        final long TWO_HOURS_IN_MS = 2 * HOUR_IN_MILLIS;
+        assertEquals("2 hours ago",
+                getRelativeTimeSpanString(en_US, tz, baseTime - TWO_HOURS_IN_MS, baseTime,
+                        MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+        assertEquals("In 2 hours",
+                getRelativeTimeSpanString(en_US, tz, baseTime + TWO_HOURS_IN_MS, baseTime,
+                        MINUTE_IN_MILLIS, FORMAT_NUMERIC_DATE));
+
+        assertEquals("In 42 min.",
+                getRelativeTimeSpanString(en_US, tz, baseTime + (42 * MINUTE_IN_MILLIS), baseTime,
+                        MINUTE_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+
+        assertEquals("Tomorrow",
+                getRelativeTimeSpanString(en_US, tz, DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+        assertEquals("In 2 days",
+                getRelativeTimeSpanString(en_US, tz, 2 * DAY_IN_MILLIS, 0, DAY_IN_MILLIS, 0));
+        assertEquals("Yesterday",
+                getRelativeTimeSpanString(en_US, tz, 0, DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+        assertEquals("2 days ago",
+                getRelativeTimeSpanString(en_US, tz, 0, 2 * DAY_IN_MILLIS, DAY_IN_MILLIS, 0));
+
+        final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+        assertEquals("5 days ago",
+                getRelativeTimeSpanString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+                        DAY_IN_MILLIS, 0));
+    }
+
+    private void test_getRelativeTimeSpanString_helper(long delta, long minResolution, int flags,
+            String expectedInPast,
+            String expectedInFuture) throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+        Calendar cal = Calendar.getInstance(tz, en_US);
+        // Feb 5, 2015 at 10:50 PST
+        cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+        final long base = cal.getTimeInMillis();
+
+        assertEquals(expectedInPast,
+                getRelativeTimeSpanString(en_US, tz, base - delta, base, minResolution, flags));
+        assertEquals(expectedInFuture,
+                getRelativeTimeSpanString(en_US, tz, base + delta, base, minResolution, flags));
+    }
+
+    private void test_getRelativeTimeSpanString_helper(long delta, long minResolution,
+            String expectedInPast,
+            String expectedInFuture) throws Exception {
+        test_getRelativeTimeSpanString_helper(delta, minResolution, 0, expectedInPast,
+                expectedInFuture);
+    }
+
+    @Test
+    public void test_getRelativeTimeSpanString() throws Exception {
+
+        test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, "0 seconds ago",
+                "0 seconds ago");
+        test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago",
+                "In 1 minute");
+        test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, "1 minute ago",
+                "In 1 minute");
+        test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, "5 days ago", "In 5 days");
+
+        test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "0 seconds ago",
+                "0 seconds ago");
+        test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "1 second ago",
+                "In 1 second");
+        test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "2 seconds ago",
+                "In 2 seconds");
+        test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "25 seconds ago",
+                "In 25 seconds");
+        test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "1 minute ago",
+                "In 1 minute");
+        test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS,
+                "1 hour ago",
+                "In 1 hour");
+
+        test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "0 minutes ago",
+                "0 minutes ago");
+        test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "1 minute ago",
+                "In 1 minute");
+        test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "2 minutes ago",
+                "In 2 minutes");
+        test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "25 minutes ago",
+                "In 25 minutes");
+        test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, "1 hour ago",
+                "In 1 hour");
+        test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "12 hours ago",
+                "In 12 hours");
+
+        test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+                "0 hours ago");
+        test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+                "In 1 hour");
+        test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "2 hours ago",
+                "In 2 hours");
+        test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "5 hours ago",
+                "In 5 hours");
+        test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, "20 hours ago",
+                "In 20 hours");
+
+        test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+        test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                "Tomorrow");
+        test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                "Tomorrow");
+        test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                "In 2 days");
+        test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, "January 11",
+                "March 2");
+
+        test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+                "0 weeks ago");
+        test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+                "In 1 week");
+        test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "2 weeks ago",
+                "In 2 weeks");
+        test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, "25 weeks ago",
+                "In 25 weeks");
+
+        // duration >= minResolution
+        test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, "30 seconds ago",
+                "In 30 seconds");
+        test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS,
+                "30 minutes ago", "In 30 minutes");
+        test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, "Yesterday",
+                "Tomorrow");
+        test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, "5 days ago",
+                "In 5 days");
+        test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS,
+                "July 10, 2014",
+                "September 3");
+        test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS,
+                "February 6, 2010", "February 4, 2020");
+
+        test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS,
+                "1 minute ago",
+                "In 1 minute");
+        test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS,
+                "1 minute ago", "In 1 minute");
+        test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "1 hour ago",
+                "In 1 hour");
+        test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS,
+                "1 hour ago",
+                "In 1 hour");
+        test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Today", "Today");
+        test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                "Today");
+        test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "Yesterday",
+                "Tomorrow");
+        test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                "In 2 days");
+        test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, "2 days ago",
+                "In 2 days");
+        test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, "1 week ago",
+                "In 1 week");
+        test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, "1 week ago",
+                "In 1 week");
+
+        // duration < minResolution
+        test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS,
+                "0 minutes ago",
+                "In 0 minutes");
+        test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, "0 hours ago",
+                "In 0 hours");
+        test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, "0 hours ago",
+                "In 0 hours");
+        test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, "Yesterday",
+                "Tomorrow");
+        test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, "0 weeks ago",
+                "In 0 weeks");
+        test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, "0 weeks ago",
+                "In 0 weeks");
+    }
+
+    @Test
+    public void test_getRelativeTimeSpanStringAbbrev() throws Exception {
+        int flags = FORMAT_ABBREV_RELATIVE;
+
+        test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, 0, flags, "0 sec. ago",
+                "0 sec. ago");
+        test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, 0, flags, "1 min. ago",
+                "In 1 min.");
+        test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, 0, flags, "5 days ago",
+                "In 5 days");
+
+        test_getRelativeTimeSpanString_helper(0 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "0 sec. ago", "0 sec. ago");
+        test_getRelativeTimeSpanString_helper(1 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "1 sec. ago", "In 1 sec.");
+        test_getRelativeTimeSpanString_helper(2 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "2 sec. ago", "In 2 sec.");
+        test_getRelativeTimeSpanString_helper(25 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "25 sec. ago", "In 25 sec.");
+        test_getRelativeTimeSpanString_helper(75 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "1 min. ago", "In 1 min.");
+        test_getRelativeTimeSpanString_helper(5000 * SECOND_IN_MILLIS, SECOND_IN_MILLIS, flags,
+                "1 hr. ago", "In 1 hr.");
+
+        test_getRelativeTimeSpanString_helper(0 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "0 min. ago", "0 min. ago");
+        test_getRelativeTimeSpanString_helper(1 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "1 min. ago", "In 1 min.");
+        test_getRelativeTimeSpanString_helper(2 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "2 min. ago", "In 2 min.");
+        test_getRelativeTimeSpanString_helper(25 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "25 min. ago", "In 25 min.");
+        test_getRelativeTimeSpanString_helper(75 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "1 hr. ago", "In 1 hr.");
+        test_getRelativeTimeSpanString_helper(720 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "12 hr. ago", "In 12 hr.");
+
+        test_getRelativeTimeSpanString_helper(0 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "0 hr. ago", "0 hr. ago");
+        test_getRelativeTimeSpanString_helper(1 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "1 hr. ago", "In 1 hr.");
+        test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "2 hr. ago", "In 2 hr.");
+        test_getRelativeTimeSpanString_helper(5 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "5 hr. ago", "In 5 hr.");
+        test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "20 hr. ago", "In 20 hr.");
+
+        test_getRelativeTimeSpanString_helper(0 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+                "Today");
+        test_getRelativeTimeSpanString_helper(20 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "Yesterday", "Tomorrow");
+        test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "Yesterday", "Tomorrow");
+        test_getRelativeTimeSpanString_helper(2 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "2 days ago", "In 2 days");
+        test_getRelativeTimeSpanString_helper(25 * DAY_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "January 11", "March 2");
+
+        test_getRelativeTimeSpanString_helper(0 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "0 wk. ago", "0 wk. ago");
+        test_getRelativeTimeSpanString_helper(1 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "1 wk. ago", "In 1 wk.");
+        test_getRelativeTimeSpanString_helper(2 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "2 wk. ago", "In 2 wk.");
+        test_getRelativeTimeSpanString_helper(25 * WEEK_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "25 wk. ago", "In 25 wk.");
+
+        // duration >= minResolution
+        test_getRelativeTimeSpanString_helper(30 * SECOND_IN_MILLIS, 0, flags, "30 sec. ago",
+                "In 30 sec.");
+        test_getRelativeTimeSpanString_helper(30 * MINUTE_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "30 min. ago", "In 30 min.");
+        test_getRelativeTimeSpanString_helper(30 * HOUR_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "Yesterday", "Tomorrow");
+        test_getRelativeTimeSpanString_helper(5 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "5 days ago", "In 5 days");
+        test_getRelativeTimeSpanString_helper(30 * WEEK_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "July 10, 2014", "September 3");
+        test_getRelativeTimeSpanString_helper(5 * 365 * DAY_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "February 6, 2010", "February 4, 2020");
+
+        test_getRelativeTimeSpanString_helper(60 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "1 min. ago", "In 1 min.");
+        test_getRelativeTimeSpanString_helper(120 * SECOND_IN_MILLIS - 1, MINUTE_IN_MILLIS, flags,
+                "1 min. ago", "In 1 min.");
+        test_getRelativeTimeSpanString_helper(60 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "1 hr. ago", "In 1 hr.");
+        test_getRelativeTimeSpanString_helper(120 * MINUTE_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+                "1 hr. ago", "In 1 hr.");
+        test_getRelativeTimeSpanString_helper(2 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags, "Today",
+                "Today");
+        test_getRelativeTimeSpanString_helper(12 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "Yesterday", "Today");
+        test_getRelativeTimeSpanString_helper(24 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "Yesterday", "Tomorrow");
+        test_getRelativeTimeSpanString_helper(48 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "2 days ago", "In 2 days");
+        test_getRelativeTimeSpanString_helper(45 * HOUR_IN_MILLIS, DAY_IN_MILLIS, flags,
+                "2 days ago", "In 2 days");
+        test_getRelativeTimeSpanString_helper(7 * DAY_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "1 wk. ago", "In 1 wk.");
+        test_getRelativeTimeSpanString_helper(14 * DAY_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+                "1 wk. ago", "In 1 wk.");
+
+        // duration < minResolution
+        test_getRelativeTimeSpanString_helper(59 * SECOND_IN_MILLIS, MINUTE_IN_MILLIS, flags,
+                "0 min. ago", "In 0 min.");
+        test_getRelativeTimeSpanString_helper(59 * MINUTE_IN_MILLIS, HOUR_IN_MILLIS, flags,
+                "0 hr. ago", "In 0 hr.");
+        test_getRelativeTimeSpanString_helper(HOUR_IN_MILLIS - 1, HOUR_IN_MILLIS, flags,
+                "0 hr. ago", "In 0 hr.");
+        test_getRelativeTimeSpanString_helper(DAY_IN_MILLIS - 1, DAY_IN_MILLIS, flags,
+                "Yesterday", "Tomorrow");
+        test_getRelativeTimeSpanString_helper(20 * SECOND_IN_MILLIS, WEEK_IN_MILLIS, flags,
+                "0 wk. ago", "In 0 wk.");
+        test_getRelativeTimeSpanString_helper(WEEK_IN_MILLIS - 1, WEEK_IN_MILLIS, flags,
+                "0 wk. ago", "In 0 wk.");
+
+    }
+
+    @Test
+    public void test_getRelativeTimeSpanStringGerman() throws Exception {
+        // Bug: 19744876
+        // We need to specify the timezone and the time explicitly. Otherwise it
+        // may not always give a correct answer of "tomorrow" by using
+        // (now + DAY_IN_MILLIS).
+        Locale de_DE = new Locale("de", "DE");
+        TimeZone tz = TimeZone.getTimeZone("Europe/Berlin");
+        Calendar cal = Calendar.getInstance(tz, de_DE);
+        // Feb 5, 2015 at 10:50 CET
+        cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+        final long now = cal.getTimeInMillis();
+
+        // 42 minutes ago
+        assertEquals("Vor 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+                now - 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+        // In 42 minutes
+        assertEquals("In 42 Minuten", getRelativeTimeSpanString(de_DE, tz,
+                now + 42 * MINUTE_IN_MILLIS, now, MINUTE_IN_MILLIS, 0));
+        // Yesterday
+        assertEquals("Gestern", getRelativeTimeSpanString(de_DE, tz,
+                now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // The day before yesterday
+        assertEquals("Vorgestern", getRelativeTimeSpanString(de_DE, tz,
+                now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // Tomorrow
+        assertEquals("Morgen", getRelativeTimeSpanString(de_DE, tz,
+                now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // The day after tomorrow
+        assertEquals("Übermorgen", getRelativeTimeSpanString(de_DE, tz,
+                now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    }
+
+    @Test
+    public void test_getRelativeTimeSpanStringFrench() throws Exception {
+        Locale fr_FR = new Locale("fr", "FR");
+        TimeZone tz = TimeZone.getTimeZone("Europe/Paris");
+        Calendar cal = Calendar.getInstance(tz, fr_FR);
+        // Feb 5, 2015 at 10:50 CET
+        cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+        final long now = cal.getTimeInMillis();
+
+        // 42 minutes ago
+        assertEquals("Il y a 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+                now - (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+        // In 42 minutes
+        assertEquals("Dans 42 minutes", getRelativeTimeSpanString(fr_FR, tz,
+                now + (42 * MINUTE_IN_MILLIS), now, MINUTE_IN_MILLIS, 0));
+        // Yesterday
+        assertEquals("Hier", getRelativeTimeSpanString(fr_FR, tz,
+                now - DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // The day before yesterday
+        assertEquals("Avant-hier", getRelativeTimeSpanString(fr_FR, tz,
+                now - 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // Tomorrow
+        assertEquals("Demain", getRelativeTimeSpanString(fr_FR, tz,
+                now + DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+        // The day after tomorrow
+        assertEquals("Après-demain", getRelativeTimeSpanString(fr_FR, tz,
+                now + 2 * DAY_IN_MILLIS, now, DAY_IN_MILLIS, 0));
+    }
+
+    // Tests adopted from CTS tests for DateUtils.getRelativeDateTimeString.
+    @Test
+    public void test_getRelativeDateTimeStringCTS() throws Exception {
+        Locale en_US = Locale.getDefault();
+        TimeZone tz = TimeZone.getDefault();
+        final long baseTime = System.currentTimeMillis();
+
+        final long DAY_DURATION = 5 * 24 * 60 * 60 * 1000;
+        assertNotNull(getRelativeDateTimeString(en_US, tz, baseTime - DAY_DURATION, baseTime,
+                MINUTE_IN_MILLIS, DAY_IN_MILLIS,
+                FORMAT_NUMERIC_DATE));
+    }
+
+    @Test
+    public void test_getRelativeDateTimeString() throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+        Calendar cal = Calendar.getInstance(tz, en_US);
+        // Feb 5, 2015 at 10:50 PST
+        cal.set(2015, Calendar.FEBRUARY, 5, 10, 50, 0);
+        final long base = cal.getTimeInMillis();
+
+        assertEquals("5 seconds ago, 10:49 AM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+                        MINUTE_IN_MILLIS, 0));
+        assertEquals("5 min. ago, 10:45 AM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+                        HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("0 hr. ago, 10:45 AM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * MINUTE_IN_MILLIS, base,
+                        HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("5 hours ago, 5:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base,
+                        HOUR_IN_MILLIS, DAY_IN_MILLIS, 0));
+        assertEquals("Yesterday, 7:50 PM",
+                getRelativeDateTimeString(en_US, tz, base - 15 * HOUR_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("5 days ago, 10:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+        assertEquals("Jan 29, 10:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 7 * DAY_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+        assertEquals("11/27/2014, 10:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+        assertEquals("11/27/2014, 10:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                        YEAR_IN_MILLIS, 0));
+
+        // User-supplied flags should be ignored when formatting the date clause.
+        final int FORMAT_SHOW_WEEKDAY = 0x00002;
+        assertEquals("11/27/2014, 10:50 AM",
+                getRelativeDateTimeString(en_US, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS,
+                        FORMAT_ABBREV_ALL | FORMAT_SHOW_WEEKDAY));
+    }
+
+    @Test
+    public void test_getRelativeDateTimeStringDST() throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+        Calendar cal = Calendar.getInstance(tz, en_US);
+
+        // DST starts on Mar 9, 2014 at 2:00 AM.
+        // So 5 hours before 3:15 AM should be formatted as 'Yesterday, 9:15 PM'.
+        cal.set(2014, Calendar.MARCH, 9, 3, 15, 0);
+        long base = cal.getTimeInMillis();
+        assertEquals("Yesterday, 9:15 PM",
+                getRelativeDateTimeString(en_US, tz, base - 5 * HOUR_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+
+        // 1 hour after 2:00 AM should be formatted as 'In 1 hour, 4:00 AM'.
+        cal.set(2014, Calendar.MARCH, 9, 2, 0, 0);
+        base = cal.getTimeInMillis();
+        assertEquals("In 1 hour, 4:00 AM",
+                getRelativeDateTimeString(en_US, tz, base + 1 * HOUR_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+
+        // DST ends on Nov 2, 2014 at 2:00 AM. Clocks are turned backward 1 hour to
+        // 1:00 AM. 8 hours before 5:20 AM should be 'Yesterday, 10:20 PM'.
+        cal.set(2014, Calendar.NOVEMBER, 2, 5, 20, 0);
+        base = cal.getTimeInMillis();
+        assertEquals("Yesterday, 10:20 PM",
+                getRelativeDateTimeString(en_US, tz, base - 8 * HOUR_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+
+        cal.set(2014, Calendar.NOVEMBER, 2, 0, 45, 0);
+        base = cal.getTimeInMillis();
+        // 45 minutes after 0:45 AM should be 'In 45 minutes, 1:30 AM'.
+        assertEquals("In 45 minutes, 1:30 AM",
+                getRelativeDateTimeString(en_US, tz, base + 45 * MINUTE_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+        // 45 minutes later, it should be 'In 45 minutes, 1:15 AM'.
+        assertEquals("In 45 minutes, 1:15 AM",
+                getRelativeDateTimeString(en_US, tz, base + 90 * MINUTE_IN_MILLIS,
+                        base + 45 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+        // Another 45 minutes later, it should be 'In 45 minutes, 2:00 AM'.
+        assertEquals("In 45 minutes, 2:00 AM",
+                getRelativeDateTimeString(en_US, tz, base + 135 * MINUTE_IN_MILLIS,
+                        base + 90 * MINUTE_IN_MILLIS, 0, WEEK_IN_MILLIS, 0));
+    }
+
+    @Test
+    public void test_getRelativeDateTimeStringItalian() throws Exception {
+        Locale it_IT = new Locale("it", "IT");
+        TimeZone tz = TimeZone.getTimeZone("Europe/Rome");
+        Calendar cal = Calendar.getInstance(tz, it_IT);
+        // 05 febbraio 2015 20:15
+        cal.set(2015, Calendar.FEBRUARY, 5, 20, 15, 0);
+        final long base = cal.getTimeInMillis();
+
+        assertEquals("5 secondi fa, 20:14",
+                getRelativeDateTimeString(it_IT, tz, base - 5 * SECOND_IN_MILLIS, base, 0,
+                        MINUTE_IN_MILLIS, 0));
+        assertEquals("5 min fa, 20:10",
+                getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base, 0,
+                        HOUR_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("0 h fa, 20:10",
+                getRelativeDateTimeString(it_IT, tz, base - 5 * MINUTE_IN_MILLIS, base,
+                        HOUR_IN_MILLIS, DAY_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("Ieri, 22:15",
+                getRelativeDateTimeString(it_IT, tz, base - 22 * HOUR_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+        assertEquals("5 giorni fa, 20:15",
+                getRelativeDateTimeString(it_IT, tz, base - 5 * DAY_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+        assertEquals("27/11/2014, 20:15",
+                getRelativeDateTimeString(it_IT, tz, base - 10 * WEEK_IN_MILLIS, base, 0,
+                        WEEK_IN_MILLIS, 0));
+    }
+
+    // http://b/5252772: detect the actual date difference
+    @Test
+    public void test5252772() throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+
+        // Now is Sep 2, 2011, 10:23 AM PDT.
+        Calendar nowCalendar = Calendar.getInstance(tz, en_US);
+        nowCalendar.set(2011, Calendar.SEPTEMBER, 2, 10, 23, 0);
+        final long now = nowCalendar.getTimeInMillis();
+
+        // Sep 1, 2011, 10:24 AM
+        Calendar yesterdayCalendar1 = Calendar.getInstance(tz, en_US);
+        yesterdayCalendar1.set(2011, Calendar.SEPTEMBER, 1, 10, 24, 0);
+        long yesterday1 = yesterdayCalendar1.getTimeInMillis();
+        assertEquals("Yesterday, 10:24 AM",
+                getRelativeDateTimeString(en_US, tz, yesterday1, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Sep 1, 2011, 10:22 AM
+        Calendar yesterdayCalendar2 = Calendar.getInstance(tz, en_US);
+        yesterdayCalendar2.set(2011, Calendar.SEPTEMBER, 1, 10, 22, 0);
+        long yesterday2 = yesterdayCalendar2.getTimeInMillis();
+        assertEquals("Yesterday, 10:22 AM",
+                getRelativeDateTimeString(en_US, tz, yesterday2, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Aug 31, 2011, 10:24 AM
+        Calendar twoDaysAgoCalendar1 = Calendar.getInstance(tz, en_US);
+        twoDaysAgoCalendar1.set(2011, Calendar.AUGUST, 31, 10, 24, 0);
+        long twoDaysAgo1 = twoDaysAgoCalendar1.getTimeInMillis();
+        assertEquals("2 days ago, 10:24 AM",
+                getRelativeDateTimeString(en_US, tz, twoDaysAgo1, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Aug 31, 2011, 10:22 AM
+        Calendar twoDaysAgoCalendar2 = Calendar.getInstance(tz, en_US);
+        twoDaysAgoCalendar2.set(2011, Calendar.AUGUST, 31, 10, 22, 0);
+        long twoDaysAgo2 = twoDaysAgoCalendar2.getTimeInMillis();
+        assertEquals("2 days ago, 10:22 AM",
+                getRelativeDateTimeString(en_US, tz, twoDaysAgo2, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Sep 3, 2011, 10:22 AM
+        Calendar tomorrowCalendar1 = Calendar.getInstance(tz, en_US);
+        tomorrowCalendar1.set(2011, Calendar.SEPTEMBER, 3, 10, 22, 0);
+        long tomorrow1 = tomorrowCalendar1.getTimeInMillis();
+        assertEquals("Tomorrow, 10:22 AM",
+                getRelativeDateTimeString(en_US, tz, tomorrow1, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Sep 3, 2011, 10:24 AM
+        Calendar tomorrowCalendar2 = Calendar.getInstance(tz, en_US);
+        tomorrowCalendar2.set(2011, Calendar.SEPTEMBER, 3, 10, 24, 0);
+        long tomorrow2 = tomorrowCalendar2.getTimeInMillis();
+        assertEquals("Tomorrow, 10:24 AM",
+                getRelativeDateTimeString(en_US, tz, tomorrow2, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Sep 4, 2011, 10:22 AM
+        Calendar twoDaysLaterCalendar1 = Calendar.getInstance(tz, en_US);
+        twoDaysLaterCalendar1.set(2011, Calendar.SEPTEMBER, 4, 10, 22, 0);
+        long twoDaysLater1 = twoDaysLaterCalendar1.getTimeInMillis();
+        assertEquals("In 2 days, 10:22 AM",
+                getRelativeDateTimeString(en_US, tz, twoDaysLater1, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+
+        // Sep 4, 2011, 10:24 AM
+        Calendar twoDaysLaterCalendar2 = Calendar.getInstance(tz, en_US);
+        twoDaysLaterCalendar2.set(2011, Calendar.SEPTEMBER, 4, 10, 24, 0);
+        long twoDaysLater2 = twoDaysLaterCalendar2.getTimeInMillis();
+        assertEquals("In 2 days, 10:24 AM",
+                getRelativeDateTimeString(en_US, tz, twoDaysLater2, now, MINUTE_IN_MILLIS,
+                        WEEK_IN_MILLIS, 0));
+    }
+
+    // b/19822016: show / hide the year based on the dates in the arguments.
+    @Test
+    public void test_bug19822016() throws Exception {
+        Locale en_US = new Locale("en", "US");
+        TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
+        Calendar cal = Calendar.getInstance(tz, en_US);
+        // Feb 5, 2012 at 10:50 PST
+        cal.set(2012, Calendar.FEBRUARY, 5, 10, 50, 0);
+        long base = cal.getTimeInMillis();
+
+        assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+        assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+        assertEquals("11/27/2011, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+        assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+        assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+        assertEquals("January 6, 2012", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+        assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+        assertEquals("December 7, 2011", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+        assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+
+        // Feb 5, 2018 at 10:50 PST
+        cal.set(2018, Calendar.FEBRUARY, 5, 10, 50, 0);
+        base = cal.getTimeInMillis();
+        assertEquals("Feb 5, 5:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 5 * HOUR_IN_MILLIS, base, 0, MINUTE_IN_MILLIS, 0));
+        assertEquals("Jan 29, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 7 * DAY_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+        assertEquals("11/27/2017, 10:50 AM", getRelativeDateTimeString(en_US, tz,
+                base - 10 * WEEK_IN_MILLIS, base, 0, WEEK_IN_MILLIS, 0));
+
+        assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+        assertEquals("January 6", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+        assertEquals("January 6, 2018", getRelativeTimeSpanString(en_US, tz,
+                base - 30 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+        assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, 0));
+        assertEquals("December 7, 2017", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_SHOW_YEAR));
+        assertEquals("December 7", getRelativeTimeSpanString(en_US, tz,
+                base - 60 * DAY_IN_MILLIS, base, DAY_IN_MILLIS, FORMAT_NO_YEAR));
+    }
+
+    // Check for missing ICU data. http://b/25821045
+    @Test
+    public void test_bug25821045() {
+        final TimeZone tz = TimeZone.getDefault();
+        final long now = System.currentTimeMillis();
+        final long time = now + 1000;
+        final int minResolution = 1000 * 60;
+        final int transitionResolution = minResolution;
+        final int flags = FORMAT_ABBREV_RELATIVE;
+        // Exercise all available locales, forcing the ICU implementation to pre-cache the data.
+        // This
+        // highlights data issues. It can take a while.
+        for (Locale locale : Locale.getAvailableLocales()) {
+            // In (e.g.) ICU56 an exception is thrown on the first use for a locale if required
+            // data for
+            // the "other" plural is missing. It doesn't matter what is actually formatted.
+            try {
+                RelativeDateTimeFormatter.getRelativeDateTimeString(
+                        locale, tz, time, now, minResolution, transitionResolution, flags);
+            } catch (IllegalStateException e) {
+                fail("Failed to format for " + locale);
+            }
+        }
+    }
+
+    // Check for ICU data lookup fallback failure. http://b/25883157
+    @Test
+    public void test_bug25883157() {
+        final Locale locale = new Locale("en", "GB");
+        final TimeZone tz = TimeZone.getTimeZone("GMT");
+
+        final Calendar cal = Calendar.getInstance(tz, locale);
+        cal.set(2015, Calendar.JUNE, 19, 12, 0, 0);
+
+        final long base = cal.getTimeInMillis();
+        final long time = base + 2 * WEEK_IN_MILLIS;
+
+        assertEquals("In 2 wk", getRelativeTimeSpanString(
+                locale, tz, time, base, WEEK_IN_MILLIS, FORMAT_ABBREV_RELATIVE));
+    }
+
+    // http://b/63745717
+    @Test
+    public void test_combineDateAndTime_apostrophe() {
+        final Locale locale = new Locale("fr");
+        android.icu.text.RelativeDateTimeFormatter icuFormatter =
+                android.icu.text.RelativeDateTimeFormatter.getInstance(locale);
+        assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T"));
+        // Ensure single quote ' and curly braces {} are not interpreted in input values.
+        assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
+    }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index 76aa93f..edf473e 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -16,27 +16,25 @@
 
 package com.android.internal.util;
 
-import java.util.Collection;
-import java.util.Iterator;
-
 import android.os.Debug;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.test.TestLooper;
-
-import android.test.suitebuilder.annotation.Suppress;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.StateMachine.LogRec;
-
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Log;
 
+import com.android.internal.util.StateMachine.LogRec;
+
 import junit.framework.TestCase;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collection;
+import java.util.Iterator;
+
 /**
  * Test for StateMachine.
  */
@@ -2013,4 +2011,12 @@
     private static void tloge(String s) {
         Log.e(TAG, s);
     }
+
+    public void testDumpDoesNotThrowNpeAfterQuit() {
+        final Hsm1 sm = Hsm1.makeHsm1();
+        sm.quitNow();
+        final StringWriter stringWriter = new StringWriter();
+        final PrintWriter printWriter = new PrintWriter(stringWriter);
+        sm.dump(null, printWriter, new String[0]);
+    }
 }
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 5c2ef15..5e480a6 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,37 +24,10 @@
 
 #include <log/log.h>
 
-#include <SkBitmap.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <SkPaint.h>
-#include <SkBlendMode.h>
+#include <memory>
 
 namespace android {
 
-// --- WeakLooperCallback ---
-
-class WeakLooperCallback: public LooperCallback {
-protected:
-    virtual ~WeakLooperCallback() { }
-
-public:
-    explicit WeakLooperCallback(const wp<LooperCallback>& callback) :
-        mCallback(callback) {
-    }
-
-    virtual int handleEvent(int fd, int events, void* data) {
-        sp<LooperCallback> callback = mCallback.promote();
-        if (callback != NULL) {
-            return callback->handleEvent(fd, events, data);
-        }
-        return 0; // the client is gone, remove the callback
-    }
-
-private:
-    wp<LooperCallback> mCallback;
-};
-
 // --- PointerController ---
 
 // Time to wait before starting the fade when the pointer is inactive.
@@ -70,29 +43,50 @@
 // The number of events to be read at once for DisplayEventReceiver.
 static const int EVENT_BUFFER_SIZE = 100;
 
-// --- PointerController ---
+std::shared_ptr<PointerController> PointerController::create(
+        const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+        const sp<SpriteController>& spriteController) {
+    std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
+            new PointerController(policy, looper, spriteController));
 
-PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
-        const sp<Looper>& looper, const sp<SpriteController>& spriteController) :
-        mPolicy(policy), mLooper(looper), mSpriteController(spriteController) {
-    mHandler = new WeakMessageHandler(this);
-    mCallback = new WeakLooperCallback(this);
+    /*
+     * Now we need to hook up the constructed PointerController object to its callbacks.
+     *
+     * This must be executed after the constructor but before any other methods on PointerController
+     * in order to ensure that the fully constructed object is visible on the Looper thread, since
+     * that may be a different thread than where the PointerController is initially constructed.
+     *
+     * Unfortunately, this cannot be done as part of the constructor since we need to hand out
+     * weak_ptr's which themselves cannot be constructed until there's at least one shared_ptr.
+     */
 
-    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
-        mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
-                       Looper::EVENT_INPUT, mCallback, nullptr);
+    controller->mHandler->pointerController = controller;
+    controller->mCallback->pointerController = controller;
+    if (controller->mDisplayEventReceiver.initCheck() == NO_ERROR) {
+        controller->mLooper->addFd(controller->mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+                                   Looper::EVENT_INPUT, controller->mCallback, nullptr);
     } else {
         ALOGE("Failed to initialize DisplayEventReceiver.");
     }
+    return controller;
+}
 
+PointerController::PointerController(const sp<PointerControllerPolicyInterface>& policy,
+                                     const sp<Looper>& looper,
+                                     const sp<SpriteController>& spriteController)
+      : mPolicy(policy),
+        mLooper(looper),
+        mSpriteController(spriteController),
+        mHandler(new MessageHandler()),
+        mCallback(new LooperCallback()) {
     AutoMutex _l(mLock);
 
     mLocked.animationPending = false;
 
-    mLocked.presentation = PRESENTATION_POINTER;
+    mLocked.presentation = Presentation::POINTER;
     mLocked.presentationChanged = false;
 
-    mLocked.inactivityTimeout = INACTIVITY_TIMEOUT_NORMAL;
+    mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
 
     mLocked.pointerFadeDirection = 0;
     mLocked.pointerX = 0;
@@ -227,7 +221,7 @@
     removeInactivityTimeoutLocked();
 
     // Start fading.
-    if (transition == TRANSITION_IMMEDIATE) {
+    if (transition == Transition::IMMEDIATE) {
         mLocked.pointerFadeDirection = 0;
         mLocked.pointerAlpha = 0.0f;
         updatePointerLocked();
@@ -244,7 +238,7 @@
     resetInactivityTimeoutLocked();
 
     // Start unfading.
-    if (transition == TRANSITION_IMMEDIATE) {
+    if (transition == Transition::IMMEDIATE) {
         mLocked.pointerFadeDirection = 0;
         mLocked.pointerAlpha = 1.0f;
         updatePointerLocked();
@@ -268,7 +262,7 @@
         return;
     }
 
-    if (presentation == PRESENTATION_POINTER) {
+    if (presentation == Presentation::POINTER) {
         if (mLocked.additionalMouseResources.empty()) {
             mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
                                                   &mLocked.animationResources,
@@ -486,24 +480,35 @@
     updatePointerLocked();
 }
 
-void PointerController::handleMessage(const Message& message) {
+void PointerController::MessageHandler::handleMessage(const Message& message) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+
+    if (controller == nullptr) {
+        ALOGE("PointerController instance was released before processing message: what=%d",
+              message.what);
+        return;
+    }
     switch (message.what) {
     case MSG_INACTIVITY_TIMEOUT:
-        doInactivityTimeout();
+        controller->doInactivityTimeout();
         break;
     }
 }
 
-int PointerController::handleEvent(int /* fd */, int events, void* /* data */) {
+int PointerController::LooperCallback::handleEvent(int /* fd */, int events, void* /* data */) {
+    std::shared_ptr<PointerController> controller = pointerController.lock();
+    if (controller == nullptr) {
+        ALOGW("PointerController instance was released with pending callbacks.  events=0x%x",
+              events);
+        return 0; // Remove the callback, the PointerController is gone anyways
+    }
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
-        ALOGE("Display event receiver pipe was closed or an error occurred.  "
-              "events=0x%x", events);
+        ALOGE("Display event receiver pipe was closed or an error occurred.  events=0x%x", events);
         return 0; // remove the callback
     }
 
     if (!(events & Looper::EVENT_INPUT)) {
-        ALOGW("Received spurious callback for unhandled poll event.  "
-              "events=0x%x", events);
+        ALOGW("Received spurious callback for unhandled poll event.  events=0x%x", events);
         return 1; // keep the callback
     }
 
@@ -511,7 +516,7 @@
     ssize_t n;
     nsecs_t timestamp;
     DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+    while ((n = controller->mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
         for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
             if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
                 timestamp = buf[i].header.timestamp;
@@ -520,7 +525,7 @@
         }
     }
     if (gotVsync) {
-        doAnimate(timestamp);
+        controller->doAnimate(timestamp);
     }
     return 1;  // keep the callback
 }
@@ -619,7 +624,7 @@
 }
 
 void PointerController::doInactivityTimeout() {
-    fade(TRANSITION_GRADUAL);
+    fade(Transition::GRADUAL);
 }
 
 void PointerController::startAnimationLocked() {
@@ -633,8 +638,9 @@
 void PointerController::resetInactivityTimeoutLocked() {
     mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT);
 
-    nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT
-            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
+    nsecs_t timeout = mLocked.inactivityTimeout == InactivityTimeout::SHORT
+            ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT
+            : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL;
     mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT);
 }
 
@@ -661,7 +667,7 @@
     }
 
     if (mLocked.pointerIconChanged || mLocked.presentationChanged) {
-        if (mLocked.presentation == PRESENTATION_POINTER) {
+        if (mLocked.presentation == Presentation::POINTER) {
             if (mLocked.requestedPointerType == mPolicy->getDefaultPointerIconId()) {
                 mLocked.pointerSprite->setIcon(mLocked.pointerIcon);
             } else {
@@ -737,7 +743,7 @@
             return spot;
         }
     }
-    return NULL;
+    return nullptr;
 }
 
 void PointerController::releaseSpotLocked(Spot* spot) {
@@ -778,7 +784,7 @@
 
     mLocked.additionalMouseResources.clear();
     mLocked.animationResources.clear();
-    if (mLocked.presentation == PRESENTATION_POINTER) {
+    if (mLocked.presentation == Presentation::POINTER) {
         mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources,
                 &mLocked.animationResources, mLocked.viewport.displayId);
     }
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index ebc622b..14c0679 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -17,19 +17,20 @@
 #ifndef _UI_POINTER_CONTROLLER_H
 #define _UI_POINTER_CONTROLLER_H
 
-#include "SpriteController.h"
-
-#include <map>
-#include <vector>
-
-#include <ui/DisplayInfo.h>
+#include <PointerControllerInterface.h>
+#include <gui/DisplayEventReceiver.h>
 #include <input/DisplayViewport.h>
 #include <input/Input.h>
-#include <PointerControllerInterface.h>
+#include <ui/DisplayInfo.h>
 #include <utils/BitSet.h>
-#include <utils/RefBase.h>
 #include <utils/Looper.h>
-#include <gui/DisplayEventReceiver.h>
+#include <utils/RefBase.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "SpriteController.h"
 
 namespace android {
 
@@ -70,25 +71,22 @@
     virtual int32_t getCustomPointerIconId() = 0;
 };
 
-
 /*
  * Tracks pointer movements and draws the pointer sprite to a surface.
  *
  * Handles pointer acceleration and animation.
  */
-class PointerController : public PointerControllerInterface, public MessageHandler,
-                          public LooperCallback {
-protected:
-    virtual ~PointerController();
-
+class PointerController : public PointerControllerInterface {
 public:
-    enum InactivityTimeout {
-        INACTIVITY_TIMEOUT_NORMAL = 0,
-        INACTIVITY_TIMEOUT_SHORT = 1,
+    static std::shared_ptr<PointerController> create(
+            const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+            const sp<SpriteController>& spriteController);
+    enum class InactivityTimeout {
+        NORMAL = 0,
+        SHORT = 1,
     };
 
-    PointerController(const sp<PointerControllerPolicyInterface>& policy,
-            const sp<Looper>& looper, const sp<SpriteController>& spriteController);
+    virtual ~PointerController();
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -113,8 +111,8 @@
     void reloadPointerResources();
 
 private:
-    static const size_t MAX_RECYCLED_SPRITES = 12;
-    static const size_t MAX_SPOTS = 12;
+    static constexpr size_t MAX_RECYCLED_SPRITES = 12;
+    static constexpr size_t MAX_SPOTS = 12;
 
     enum {
         MSG_INACTIVITY_TIMEOUT,
@@ -130,8 +128,13 @@
         float x, y;
 
         inline Spot(uint32_t id, const sp<Sprite>& sprite)
-                : id(id), sprite(sprite), alpha(1.0f), scale(1.0f),
-                  x(0.0f), y(0.0f), lastIcon(NULL) { }
+              : id(id),
+                sprite(sprite),
+                alpha(1.0f),
+                scale(1.0f),
+                x(0.0f),
+                y(0.0f),
+                lastIcon(nullptr) {}
 
         void updateSprite(const SpriteIcon* icon, float x, float y, int32_t displayId);
 
@@ -139,12 +142,24 @@
         const SpriteIcon* lastIcon;
     };
 
+    class MessageHandler : public virtual android::MessageHandler {
+    public:
+        void handleMessage(const Message& message) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
+    class LooperCallback : public virtual android::LooperCallback {
+    public:
+        int handleEvent(int fd, int events, void* data) override;
+        std::weak_ptr<PointerController> pointerController;
+    };
+
     mutable Mutex mLock;
 
     sp<PointerControllerPolicyInterface> mPolicy;
     sp<Looper> mLooper;
     sp<SpriteController> mSpriteController;
-    sp<WeakMessageHandler> mHandler;
+    sp<MessageHandler> mHandler;
     sp<LooperCallback> mCallback;
 
     DisplayEventReceiver mDisplayEventReceiver;
@@ -181,14 +196,15 @@
         int32_t buttonState;
 
         std::map<int32_t /* displayId */, std::vector<Spot*>> spotsByDisplay;
-        std::vector<sp<Sprite> > recycledSprites;
+        std::vector<sp<Sprite>> recycledSprites;
     } mLocked GUARDED_BY(mLock);
 
+    PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
+                      const sp<SpriteController>& spriteController);
+
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
 
-    void handleMessage(const Message& message);
-    int handleEvent(int fd, int events, void* data);
     void doAnimate(nsecs_t timestamp);
     bool doFadingAnimationLocked(nsecs_t timestamp);
     bool doBitmapAnimationLocked(nsecs_t timestamp);
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index a157426..6e129a0 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -136,7 +136,7 @@
     sp<MockSprite> mPointerSprite;
     sp<MockPointerControllerPolicyInterface> mPolicy;
     sp<MockSpriteController> mSpriteController;
-    sp<PointerController> mPointerController;
+    std::shared_ptr<PointerController> mPointerController;
 
 private:
     void loopThread();
@@ -160,7 +160,7 @@
     EXPECT_CALL(*mSpriteController, createSprite())
             .WillOnce(Return(mPointerSprite));
 
-    mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+    mPointerController = PointerController::create(mPolicy, mLooper, mSpriteController);
 }
 
 PointerControllerTest::~PointerControllerTest() {
@@ -193,7 +193,7 @@
 
 TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
     EXPECT_CALL(*mPointerSprite, setVisible(true));
@@ -208,7 +208,7 @@
 
 TEST_F(PointerControllerTest, updatePointerIcon) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t type = CURSOR_TYPE_ADDITIONAL;
     std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
@@ -224,7 +224,7 @@
 
 TEST_F(PointerControllerTest, setCustomPointerIcon) {
     ensureDisplayViewportIsSet();
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
 
     int32_t style = CURSOR_TYPE_CUSTOM;
     float hotSpotX = 15;
@@ -246,13 +246,13 @@
 }
 
 TEST_F(PointerControllerTest, doesNotGetResourcesBeforeSettingViewport) {
-    mPointerController->setPresentation(PointerController::PRESENTATION_POINTER);
+    mPointerController->setPresentation(PointerController::Presentation::POINTER);
     mPointerController->setSpots(nullptr, nullptr, BitSet32(), -1);
     mPointerController->clearSpots();
     mPointerController->setPosition(1.0f, 1.0f);
     mPointerController->move(1.0f, 1.0f);
-    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
-    mPointerController->fade(PointerController::TRANSITION_IMMEDIATE);
+    mPointerController->unfade(PointerController::Transition::IMMEDIATE);
+    mPointerController->fade(PointerController::Transition::IMMEDIATE);
 
     EXPECT_TRUE(mPolicy->noResourcesAreLoaded());
 
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 3ad076f..a285692 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -44999,7 +44999,7 @@
   public abstract class CellLocation {
     ctor public CellLocation();
     method public static android.telephony.CellLocation getEmpty();
-    method public static void requestLocationUpdate();
+    method @Deprecated public static void requestLocationUpdate();
   }
 
   public abstract class CellSignalStrength {
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
index 754584e..6ddb122 100644
--- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -1,24 +1,6 @@
 // Signature format: 2.0
 package android.net {
 
-  public final class TetheredClient implements android.os.Parcelable {
-    ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
-    method public int describeContents();
-    method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
-    method @NonNull public android.net.MacAddress getMacAddress();
-    method public int getTetheringType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
-  }
-
-  public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method @NonNull public android.net.LinkAddress getAddress();
-    method @Nullable public String getHostname();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
-  }
-
   public final class TetheringConstants {
     field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
     field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -38,69 +20,15 @@
     method @NonNull public String[] getTetheringErroredIfaces();
     method public boolean isTetheringSupported();
     method public boolean isTetheringSupported(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
     method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
     method @Deprecated public int setUsbTethering(boolean);
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
     method @Deprecated public int tether(@NonNull String);
-    method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
     method @Deprecated public int untether(@NonNull String);
-    field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
-    field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
-    field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
-    field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
-    field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
-    field public static final int TETHERING_BLUETOOTH = 2; // 0x2
-    field public static final int TETHERING_ETHERNET = 5; // 0x5
-    field public static final int TETHERING_INVALID = -1; // 0xffffffff
-    field public static final int TETHERING_NCM = 4; // 0x4
-    field public static final int TETHERING_USB = 1; // 0x1
-    field public static final int TETHERING_WIFI = 0; // 0x0
-    field public static final int TETHERING_WIFI_P2P = 3; // 0x3
-    field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
-    field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
-    field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
-    field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
-    field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
-    field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
-    field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
-    field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
-    field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
-    field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
-    field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
-    field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
-    field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
-    field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
-    field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
-    field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
-    field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
-    field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
-    field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
-    field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
-  }
-
-  public static interface TetheringManager.OnTetheringEntitlementResultListener {
-    method public void onTetheringEntitlementResult(int);
-  }
-
-  public static interface TetheringManager.StartTetheringCallback {
-    method public default void onTetheringFailed(int);
-    method public default void onTetheringStarted();
   }
 
   public static interface TetheringManager.TetheringEventCallback {
-    method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
-    method public default void onError(@NonNull String, int);
-    method public default void onOffloadStatusChanged(int);
     method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
-    method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
-    method public default void onTetheringSupported(boolean);
-    method public default void onUpstreamChanged(@Nullable android.net.Network);
   }
 
   public static class TetheringManager.TetheringInterfaceRegexps {
@@ -109,21 +37,5 @@
     method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
   }
 
-  public static class TetheringManager.TetheringRequest {
-    method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
-    method @Nullable public android.net.LinkAddress getLocalIpv4Address();
-    method public boolean getShouldShowEntitlementUi();
-    method public int getTetheringType();
-    method public boolean isExemptFromEntitlementCheck();
-  }
-
-  public static class TetheringManager.TetheringRequest.Builder {
-    ctor public TetheringManager.TetheringRequest.Builder(int);
-    method @NonNull public android.net.TetheringManager.TetheringRequest build();
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
-    method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
-  }
-
 }
 
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 48be0d9..645b000 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -36,7 +34,6 @@
  * @hide
  */
 @SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public final class TetheredClient implements Parcelable {
     @NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 87e5c1e..49c7fe2 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -55,7 +55,6 @@
  * @hide
  */
 @SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
 @TestApi
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
diff --git a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 74df113..9bb01ae 100644
--- a/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/packages/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -42,6 +42,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.system.Os;
 import android.util.Log;
 
@@ -101,17 +102,21 @@
 
     private UiAutomation mUiAutomation =
             InstrumentationRegistry.getInstrumentation().getUiAutomation();
+    private boolean mRunTests;
 
     @Before
     public void setUp() throws Exception {
-        mHandlerThread = new HandlerThread(getClass().getSimpleName());
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-        mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
         // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
         // tethered client callbacks.
         mUiAutomation.adoptShellPermissionIdentity(
                 MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+        mRunTests = mTm.isTetheringSupported() && mEm != null;
+        assumeTrue(mRunTests);
+
+        mHandlerThread = new HandlerThread(getClass().getSimpleName());
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
     }
 
     private void cleanUp() throws Exception {
@@ -135,7 +140,7 @@
     @After
     public void tearDown() throws Exception {
         try {
-            cleanUp();
+            if (mRunTests) cleanUp();
         } finally {
             mUiAutomation.dropShellPermissionIdentity();
         }
@@ -224,9 +229,19 @@
 
     }
 
+    private boolean isAdbOverNetwork() {
+        // If adb TCP port opened, this test may running by adb over network.
+        return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1)
+                || (SystemProperties.getInt("service.adb.tcp.port", -1) > -1);
+    }
+
     @Test
     public void testPhysicalEthernet() throws Exception {
         assumeTrue(mEm.isAvailable());
+        // Do not run this test if adb is over network and ethernet is connected.
+        // It is likely the adb run over ethernet, the adb would break when ethernet is switching
+        // from client mode to server mode. See b/160389275.
+        assumeFalse(isAdbOverNetwork());
 
         // Get an interface to use.
         final String iface = mTetheredInterfaceRequester.getInterface();
diff --git a/packages/Tethering/tests/privileged/Android.bp b/packages/Tethering/tests/privileged/Android.bp
new file mode 100644
index 0000000..a0fb246
--- /dev/null
+++ b/packages/Tethering/tests/privileged/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+android_test {
+    name: "TetheringPrivilegedTests",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+    certificate: "networkstack",
+    platform_apis: true,
+    test_suites: [
+        "general-tests",
+        "mts",
+    ],
+    compile_multilib: "both",
+}
diff --git a/packages/Tethering/tests/privileged/AndroidManifest.xml b/packages/Tethering/tests/privileged/AndroidManifest.xml
new file mode 100644
index 0000000..49eba15d
--- /dev/null
+++ b/packages/Tethering/tests/privileged/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.networkstack.tethering.tests.privileged"
+          android:sharedUserId="android.uid.networkstack">
+
+    <!-- Note: do not add any privileged or signature permissions that are granted
+         to the network stack and its shared uid apps. Otherwise, the test APK will
+         install, but when the device is rebooted, it will bootloop because this
+         test APK is not in the privileged permission allow list -->
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.networkstack.tethering.tests.privileged"
+        android:label="Tethering privileged tests">
+    </instrumentation>
+</manifest>
diff --git a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
index 4e54aba..1bbc90d 100644
--- a/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
+++ b/packages/services/PacProcessor/src/com/android/net/IProxyService.aidl
@@ -21,7 +21,4 @@
     String resolvePacFile(String host, String url);
 
     oneway void setPacFile(String scriptContents);
-
-    oneway void startPacSystem();
-    oneway void stopPacSystem();
 }
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
index b006d6e..3c25bfd 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -83,15 +83,5 @@
             }
             mPacNative.setCurrentProxyScript(script);
         }
-
-        @Override
-        public void startPacSystem() throws RemoteException {
-            //TODO: remove
-        }
-
-        @Override
-        public void stopPacSystem() throws RemoteException {
-            //TODO: remove
-        }
     }
 }
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index ba61d1c0..7c7e742 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,7 +1,12 @@
 # Bug component: 656484
 
+aabhinav@google.com
 alsutton@google.com
 bryanmawhinney@google.com
+jstemmer@google.com
 nathch@google.com
+niagra@google.com
+niamhfw@google.com
+philippov@google.com
 rthakohov@google.com
 tobiast@google.com
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index f6ce2dc..de302fc 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -196,13 +196,7 @@
                 mPacUrl = Uri.EMPTY;
                 mCurrentPac = null;
                 if (mProxyService != null) {
-                    try {
-                        mProxyService.stopPacSystem();
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "Failed to stop PAC service", e);
-                    } finally {
-                        unbind();
-                    }
+                    unbind();
                 }
             }
             return DO_SEND_BROADCAST;
@@ -327,11 +321,6 @@
                     if (mProxyService == null) {
                         Log.e(TAG, "No proxy service");
                     } else {
-                        try {
-                            mProxyService.startPacSystem();
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Unable to reach ProxyService - PAC will not be started", e);
-                        }
                         mNetThreadHandler.post(mPacDownloader);
                     }
                 }
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index c5aa8d5..a75a80a 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -82,6 +82,7 @@
     private final PackageManager mPackageManager;
     private final UserManager mUserManager;
     private final INetd mNetd;
+    private final Dependencies mDeps;
 
     // Values are User IDs.
     @GuardedBy("this")
@@ -102,10 +103,30 @@
     @GuardedBy("this")
     private final Set<Integer> mAllApps = new HashSet<>();
 
-    public PermissionMonitor(Context context, INetd netd) {
+    /**
+     * Dependencies of PermissionMonitor, for injection in tests.
+     */
+    @VisibleForTesting
+    public static class Dependencies {
+        /**
+         * Get device first sdk version.
+         */
+        public int getDeviceFirstSdkInt() {
+            return Build.VERSION.FIRST_SDK_INT;
+        }
+    }
+
+    public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
+        this(context, netd, new Dependencies());
+    }
+
+    @VisibleForTesting
+    PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+            @NonNull final Dependencies deps) {
         mPackageManager = context.getPackageManager();
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mNetd = netd;
+        mDeps = deps;
     }
 
     // Intended to be called only once at startup, after the system is ready. Installs a broadcast
@@ -186,11 +207,6 @@
     }
 
     @VisibleForTesting
-    protected int getDeviceFirstSdkInt() {
-        return Build.VERSION.FIRST_SDK_INT;
-    }
-
-    @VisibleForTesting
     boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
         if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
             return false;
@@ -212,7 +228,7 @@
         if (app.applicationInfo != null) {
             // Backward compatibility for b/114245686, on devices that launched before Q daemons
             // and apps running as the system UID are exempted from this check.
-            if (app.applicationInfo.uid == SYSTEM_UID && getDeviceFirstSdkInt() < VERSION_Q) {
+            if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
                 return true;
             }
 
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 7711c6a..5f2c4a3 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -16,12 +16,14 @@
 
 package com.android.server.net;
 
+import static android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA;
 import static android.net.NetworkTemplate.getCollapsedRatType;
 
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Looper;
 import android.telephony.Annotation;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -196,7 +198,18 @@
 
         @Override
         public void onServiceStateChanged(@NonNull ServiceState ss) {
-            final int networkType = ss.getDataNetworkType();
+            // In 5G SA (Stand Alone) mode, the primary cell itself will be 5G hence telephony
+            // would report RAT = 5G_NR.
+            // However, in 5G NSA (Non Stand Alone) mode, the primary cell is still LTE and
+            // network allocates a secondary 5G cell so telephony reports RAT = LTE along with
+            // NR state as connected. In such case, attributes the data usage to NR.
+            // See b/160727498.
+            final boolean is5GNsa = (ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE
+                    || ss.getDataNetworkType() == TelephonyManager.NETWORK_TYPE_LTE_CA)
+                    && ss.getNrState() == NetworkRegistrationInfo.NR_STATE_CONNECTED;
+
+            final int networkType =
+                    (is5GNsa ? NETWORK_TYPE_5G_NSA : ss.getDataNetworkType());
             final int collapsedRatType = getCollapsedRatType(networkType);
             if (collapsedRatType == mLastCollapsedRatType) return;
 
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 7aaa074..1f445c9 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -233,7 +233,7 @@
     /* --- InputReaderPolicyInterface implementation --- */
 
     virtual void getReaderConfiguration(InputReaderConfiguration* outConfig);
-    virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId);
+    virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId);
     virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual std::string getDeviceAlias(const InputDeviceIdentifier& identifier);
@@ -306,7 +306,7 @@
         sp<SpriteController> spriteController;
 
         // Pointer controller singleton, created and destroyed as needed.
-        wp<PointerController> pointerController;
+        std::weak_ptr<PointerController> pointerController;
 
         // Input devices to be disabled
         SortedVector<int32_t> disabledInputDevices;
@@ -551,15 +551,16 @@
     } // release lock
 }
 
-sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t /* deviceId */) {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::obtainPointerController(
+        int32_t /* deviceId */) {
     ATRACE_CALL();
     AutoMutex _l(mLock);
 
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller == nullptr) {
         ensureSpriteControllerLocked();
 
-        controller = new PointerController(this, mLooper, mLocked.spriteController);
+        controller = PointerController::create(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
         updateInactivityTimeoutLocked();
     }
@@ -840,15 +841,14 @@
 }
 
 void NativeInputManager::updateInactivityTimeoutLocked() REQUIRES(mLock) {
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller == nullptr) {
         return;
     }
 
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
-    controller->setInactivityTimeout(lightsOut
-            ? PointerController::INACTIVITY_TIMEOUT_SHORT
-            : PointerController::INACTIVITY_TIMEOUT_NORMAL);
+    controller->setInactivityTimeout(lightsOut ? PointerController::InactivityTimeout::SHORT
+                                               : PointerController::InactivityTimeout::NORMAL);
 }
 
 void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -928,7 +928,7 @@
 
 void NativeInputManager::setPointerIconType(int32_t iconId) {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->updatePointerIcon(iconId);
     }
@@ -936,7 +936,7 @@
 
 void NativeInputManager::reloadPointerIcons() {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->reloadPointerResources();
     }
@@ -944,7 +944,7 @@
 
 void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
     AutoMutex _l(mLock);
-    sp<PointerController> controller = mLocked.pointerController.promote();
+    std::shared_ptr<PointerController> controller = mLocked.pointerController.lock();
     if (controller != nullptr) {
         controller->setCustomPointerIcon(icon);
     }
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 3c1e707..4af19b5 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -127,6 +127,14 @@
         return userContext.getContentResolver();
     }
 
+    private static boolean isUpdatedSystemApp(ApplicationInfo ai) {
+        if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Disable carrier apps until they are privileged
      * Must be public b/c framework unit tests can't access package-private methods.
@@ -138,7 +146,7 @@
             ContentResolver contentResolver, int userId,
             ArraySet<String> systemCarrierAppsDisabledUntilUsed,
             ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
-        List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper(
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(
                 packageManager, userId, systemCarrierAppsDisabledUntilUsed);
         if (candidates == null || candidates.isEmpty()) {
             return;
@@ -180,7 +188,7 @@
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (enabledSetting
+                    if (!isUpdatedSystemApp(ai) && enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             || enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
@@ -232,7 +240,7 @@
                 } else {  // No carrier privileges
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (enabledSetting
+                    if (!isUpdatedSystemApp(ai) && enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
                         Log.i(TAG, "Update state(" + packageName
@@ -363,31 +371,6 @@
         return apps;
     }
 
-    private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper(
-            IPackageManager packageManager,
-            int userId,
-            ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
-        if (systemCarrierAppsDisabledUntilUsed == null) {
-            return null;
-        }
-
-        int size = systemCarrierAppsDisabledUntilUsed.size();
-        if (size == 0) {
-            return null;
-        }
-
-        List<ApplicationInfo> apps = new ArrayList<>(size);
-        for (int i = 0; i < size; i++) {
-            String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
-            ApplicationInfo ai =
-                    getApplicationInfoIfNotUpdatedSystemApp(packageManager, userId, packageName);
-            if (ai != null) {
-                apps.add(ai);
-            }
-        }
-        return apps;
-    }
-
     private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
             IPackageManager packageManager,
             int userId,
@@ -400,11 +383,11 @@
                     systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i);
             for (int j = 0; j < associatedAppPackages.size(); j++) {
                 ApplicationInfo ai =
-                        getApplicationInfoIfNotUpdatedSystemApp(
+                        getApplicationInfoIfSystemApp(
                                 packageManager, userId, associatedAppPackages.get(j));
                 // Only update enabled state for the app on /system. Once it has been updated we
                 // shouldn't touch it.
-                if (ai != null) {
+                if (ai != null && !isUpdatedSystemApp(ai)) {
                     List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage);
                     if (appList == null) {
                         appList = new ArrayList<>();
@@ -418,26 +401,6 @@
     }
 
     @Nullable
-    private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp(
-            IPackageManager packageManager,
-            int userId,
-            String packageName) {
-        try {
-            ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
-                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                            | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
-                            | PackageManager.MATCH_SYSTEM_ONLY
-                            | PackageManager.MATCH_FACTORY_ONLY, userId);
-            if (ai != null) {
-                return ai;
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "Could not reach PackageManager", e);
-        }
-        return null;
-    }
-
-    @Nullable
     private static ApplicationInfo getApplicationInfoIfSystemApp(
             IPackageManager packageManager,
             int userId,
diff --git a/telephony/java/android/service/euicc/EuiccProfileInfo.java b/telephony/java/android/service/euicc/EuiccProfileInfo.java
index 8450a90..92e4197 100644
--- a/telephony/java/android/service/euicc/EuiccProfileInfo.java
+++ b/telephony/java/android/service/euicc/EuiccProfileInfo.java
@@ -29,6 +29,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
@@ -231,7 +232,9 @@
             mState = baseProfile.mState;
             mCarrierIdentifier = baseProfile.mCarrierIdentifier;
             mPolicyRules = baseProfile.mPolicyRules;
-            mAccessRules = Arrays.asList(baseProfile.mAccessRules);
+            mAccessRules = baseProfile.mAccessRules == null
+                            ? Collections.emptyList()
+                            : Arrays.asList(baseProfile.mAccessRules);
         }
 
         /** Builds the profile instance. */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d319e37..b75f9a1 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2495,15 +2495,15 @@
     /**
      * List of 4 customized 5G SS reference signal received quality (SSRSRQ) thresholds.
      * <p>
-     * Reference: 3GPP TS 38.215
+     * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
      * <p>
-     * 4 threshold integers must be within the boundaries [-20 dB, -3 dB], and the levels are:
+     * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
      * <UL>
-     *     <LI>"NONE: [-20, threshold1]"</LI>
+     *     <LI>"NONE: [-43, threshold1]"</LI>
      *     <LI>"POOR: (threshold1, threshold2]"</LI>
      *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
      *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
-     *     <LI>"EXCELLENT:  (threshold4, -3]"</LI>
+     *     <LI>"EXCELLENT:  (threshold4, 20]"</LI>
      * </UL>
      * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
@@ -4188,12 +4188,12 @@
                     -65,  /* SIGNAL_STRENGTH_GREAT */
                 });
         sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY,
-                // Boundaries: [-20 dB, -3 dB]
+                // Boundaries: [-43 dB, 20 dB]
                 new int[] {
-                    -16, /* SIGNAL_STRENGTH_POOR */
-                    -12, /* SIGNAL_STRENGTH_MODERATE */
-                    -9, /* SIGNAL_STRENGTH_GOOD */
-                    -6  /* SIGNAL_STRENGTH_GREAT */
+                    -31, /* SIGNAL_STRENGTH_POOR */
+                    -19, /* SIGNAL_STRENGTH_MODERATE */
+                    -7, /* SIGNAL_STRENGTH_GOOD */
+                    6  /* SIGNAL_STRENGTH_GREAT */
                 });
         sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY,
                 // Boundaries: [-23 dB, 40 dB]
diff --git a/telephony/java/android/telephony/CellLocation.java b/telephony/java/android/telephony/CellLocation.java
index 64776e3..61f68ce 100644
--- a/telephony/java/android/telephony/CellLocation.java
+++ b/telephony/java/android/telephony/CellLocation.java
@@ -16,7 +16,9 @@
 
 package android.telephony;
 
+import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -32,15 +34,36 @@
 public abstract class CellLocation {
 
     /**
-     * Request an update of the current location.  If the location has changed,
-     * a broadcast will be sent to everyone registered with {@link
-     * PhoneStateListener#LISTEN_CELL_LOCATION}.
+     * This method will not do anything.
+     *
+     * Whenever location changes, a callback will automatically be be sent to
+     * all registrants of {@link PhoneStateListener#LISTEN_CELL_LOCATION}.
+     *
+     * <p>This method is a no-op for callers targeting SDK level 31 or greater.
+     * <p>This method is a no-op for callers that target SDK level 29 or 30 and lack
+     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+     * <p>This method is a no-op for callers that target SDK level 28 or below and lack
+     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+     *
+     * Callers wishing to request a single location update should use
+     * {@link TelephonyManager#requestCellInfoUpdate}.
+     *
+     * @deprecated this method has undesirable side-effects, and it calls into the OS without
+     * access to a {@link android.content.Context Context}, meaning that certain safety checks and
+     * attribution are error-prone. Given that this method has numerous downsides, and given that
+     * there are long-available superior alternatives, callers are strongly discouraged from using
+     * this method.
      */
+    @Deprecated
     public static void requestLocationUpdate() {
+        // Since this object doesn't have a context, this is the best we can do.
+        final Context appContext = ActivityThread.currentApplication();
+        if (appContext == null) return; // should never happen
+
         try {
             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
             if (phone != null) {
-                phone.updateServiceLocation();
+                phone.updateServiceLocationWithPackageName(appContext.getOpPackageName());
             }
         } catch (RemoteException ex) {
             // ignore it
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 95fe90a..8e50bba 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -54,12 +54,12 @@
     };
 
     // Lifted from Default carrier configs and max range of SSRSRQ
-    // Boundaries: [-20 dB, -3 dB]
+    // Boundaries: [-43 dB, 20 dB]
     private int[] mSsRsrqThresholds = new int[] {
-            -16, /* SIGNAL_STRENGTH_POOR */
-            -12, /* SIGNAL_STRENGTH_MODERATE */
-            -9, /* SIGNAL_STRENGTH_GOOD */
-            -6  /* SIGNAL_STRENGTH_GREAT */
+            -31, /* SIGNAL_STRENGTH_POOR */
+            -19, /* SIGNAL_STRENGTH_MODERATE */
+            -7, /* SIGNAL_STRENGTH_GOOD */
+            6  /* SIGNAL_STRENGTH_GREAT */
     };
 
     // Lifted from Default carrier configs and max range of SSSINR
@@ -183,8 +183,8 @@
     }
 
     /**
-     * Reference: 3GPP TS 38.215.
-     * Range: -20 dB to -3 dB.
+     * Reference: 3GPP TS 38.215; 3GPP TS 38.133 section 10
+     * Range: -43 dB to 20 dB.
      * @return SS reference signal received quality, {@link CellInfo#UNAVAILABLE} means unreported
      * value.
      */
diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
index c667165..e91d6fc9 100644
--- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
+++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java
@@ -72,28 +72,20 @@
     /**
      * Provides network support info for LTE VoPS and LTE Emergency bearer support
      */
+    @Nullable
     private final LteVopsSupportInfo mLteVopsSupportInfo;
 
     /**
-     * Indicates if it's using carrier aggregation
-     *
-     * @hide
-     */
-    public boolean mIsUsingCarrierAggregation;
-
-    /**
      * @hide
      */
     DataSpecificRegistrationInfo(
             int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable,
-            boolean isEnDcAvailable, LteVopsSupportInfo lteVops,
-            boolean isUsingCarrierAggregation) {
+            boolean isEnDcAvailable, @Nullable LteVopsSupportInfo lteVops) {
         this.maxDataCalls = maxDataCalls;
         this.isDcNrRestricted = isDcNrRestricted;
         this.isNrAvailable = isNrAvailable;
         this.isEnDcAvailable = isEnDcAvailable;
         this.mLteVopsSupportInfo = lteVops;
-        this.mIsUsingCarrierAggregation = isUsingCarrierAggregation;
     }
 
     /**
@@ -102,32 +94,29 @@
      * @param dsri another data specific registration info
      * @hide
      */
-    DataSpecificRegistrationInfo(DataSpecificRegistrationInfo dsri) {
+    DataSpecificRegistrationInfo(@NonNull DataSpecificRegistrationInfo dsri) {
         maxDataCalls = dsri.maxDataCalls;
         isDcNrRestricted = dsri.isDcNrRestricted;
         isNrAvailable = dsri.isNrAvailable;
         isEnDcAvailable = dsri.isEnDcAvailable;
         mLteVopsSupportInfo = dsri.mLteVopsSupportInfo;
-        mIsUsingCarrierAggregation = dsri.mIsUsingCarrierAggregation;
     }
 
-    private DataSpecificRegistrationInfo(Parcel source) {
+    private DataSpecificRegistrationInfo(/* @NonNull */ Parcel source) {
         maxDataCalls = source.readInt();
         isDcNrRestricted = source.readBoolean();
         isNrAvailable = source.readBoolean();
         isEnDcAvailable = source.readBoolean();
         mLteVopsSupportInfo = LteVopsSupportInfo.CREATOR.createFromParcel(source);
-        mIsUsingCarrierAggregation = source.readBoolean();
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(/* @NonNull */ Parcel dest, int flags) {
         dest.writeInt(maxDataCalls);
         dest.writeBoolean(isDcNrRestricted);
         dest.writeBoolean(isNrAvailable);
         dest.writeBoolean(isEnDcAvailable);
         mLteVopsSupportInfo.writeToParcel(dest, flags);
-        dest.writeBoolean(mIsUsingCarrierAggregation);
     }
 
     @Override
@@ -144,8 +133,7 @@
                 .append(" isDcNrRestricted = " + isDcNrRestricted)
                 .append(" isNrAvailable = " + isNrAvailable)
                 .append(" isEnDcAvailable = " + isEnDcAvailable)
-                .append(" " + mLteVopsSupportInfo.toString())
-                .append(" mIsUsingCarrierAggregation = " + mIsUsingCarrierAggregation)
+                .append(" " + mLteVopsSupportInfo)
                 .append(" }")
                 .toString();
     }
@@ -153,7 +141,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(maxDataCalls, isDcNrRestricted, isNrAvailable, isEnDcAvailable,
-                mLteVopsSupportInfo, mIsUsingCarrierAggregation);
+                mLteVopsSupportInfo);
     }
 
     @Override
@@ -167,8 +155,7 @@
                 && this.isDcNrRestricted == other.isDcNrRestricted
                 && this.isNrAvailable == other.isNrAvailable
                 && this.isEnDcAvailable == other.isEnDcAvailable
-                && this.mLteVopsSupportInfo.equals(other.mLteVopsSupportInfo)
-                && this.mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation;
+                && Objects.equals(mLteVopsSupportInfo, other.mLteVopsSupportInfo);
     }
 
     public static final @NonNull Parcelable.Creator<DataSpecificRegistrationInfo> CREATOR =
@@ -192,23 +179,4 @@
         return mLteVopsSupportInfo;
     }
 
-    /**
-     * Set the flag indicating if using carrier aggregation.
-     *
-     * @param isUsingCarrierAggregation {@code true} if using carrier aggregation.
-     * @hide
-     */
-    public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) {
-        mIsUsingCarrierAggregation = isUsingCarrierAggregation;
-    }
-
-    /**
-     * Get whether network has configured carrier aggregation or not.
-     *
-     * @return {@code true} if using carrier aggregation.
-     * @hide
-     */
-    public boolean isUsingCarrierAggregation() {
-        return mIsUsingCarrierAggregation;
-    }
 }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 31a83c9..68753e1 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -218,6 +218,9 @@
     @NonNull
     private String mRplmn;
 
+    // Updated based on the accessNetworkTechnology
+    private boolean mIsUsingCarrierAggregation;
+
     /**
      * @param domain Network domain. Must be a {@link Domain}. For transport type
      * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, this must set to {@link #DOMAIN_PS}.
@@ -251,7 +254,7 @@
         mRegistrationState = registrationState;
         mRoamingType = (registrationState == REGISTRATION_STATE_ROAMING)
                 ? ServiceState.ROAMING_TYPE_UNKNOWN : ServiceState.ROAMING_TYPE_NOT_ROAMING;
-        mAccessNetworkTechnology = accessNetworkTechnology;
+        setAccessNetworkTechnology(accessNetworkTechnology);
         mRejectCause = rejectCause;
         mAvailableServices = (availableServices != null)
                 ? new ArrayList<>(availableServices) : new ArrayList<>();
@@ -290,13 +293,11 @@
                                    @Nullable CellIdentity cellIdentity, @Nullable String rplmn,
                                    int maxDataCalls, boolean isDcNrRestricted,
                                    boolean isNrAvailable, boolean isEndcAvailable,
-                                   LteVopsSupportInfo lteVopsSupportInfo,
-                                   boolean isUsingCarrierAggregation) {
+                                   LteVopsSupportInfo lteVopsSupportInfo) {
         this(domain, transportType, registrationState, accessNetworkTechnology, rejectCause,
                 emergencyOnly, availableServices, cellIdentity, rplmn);
         mDataSpecificInfo = new DataSpecificRegistrationInfo(
-                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo,
-                isUsingCarrierAggregation);
+                maxDataCalls, isDcNrRestricted, isNrAvailable, isEndcAvailable, lteVopsSupportInfo);
         updateNrState();
     }
 
@@ -317,6 +318,7 @@
                 DataSpecificRegistrationInfo.class.getClassLoader());
         mNrState = source.readInt();
         mRplmn = source.readString();
+        mIsUsingCarrierAggregation = source.readBoolean();
     }
 
     /**
@@ -331,6 +333,7 @@
         mRegistrationState = nri.mRegistrationState;
         mRoamingType = nri.mRoamingType;
         mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+        mIsUsingCarrierAggregation = nri.mIsUsingCarrierAggregation;
         mRejectCause = nri.mRejectCause;
         mEmergencyOnly = nri.mEmergencyOnly;
         mAvailableServices = new ArrayList<>(nri.mAvailableServices);
@@ -484,9 +487,7 @@
         if (tech == TelephonyManager.NETWORK_TYPE_LTE_CA) {
             // For old device backward compatibility support
             tech = TelephonyManager.NETWORK_TYPE_LTE;
-            if (mDataSpecificInfo != null) {
-                mDataSpecificInfo.setIsUsingCarrierAggregation(true);
-            }
+            mIsUsingCarrierAggregation = true;
         }
         mAccessNetworkTechnology = tech;
     }
@@ -511,6 +512,27 @@
     }
 
     /**
+     * Set whether network has configured carrier aggregation or not.
+     *
+     * @param isUsingCarrierAggregation set whether or not carrier aggregation is used.
+     *
+     * @hide
+     */
+    public void setIsUsingCarrierAggregation(boolean isUsingCarrierAggregation) {
+        mIsUsingCarrierAggregation = isUsingCarrierAggregation;
+    }
+
+    /**
+     * Get whether network has configured carrier aggregation or not.
+     *
+     * @return {@code true} if using carrier aggregation.
+     * @hide
+     */
+    public boolean isUsingCarrierAggregation() {
+        return mIsUsingCarrierAggregation;
+    }
+
+    /**
      * @hide
      */
     @Nullable
@@ -616,6 +638,7 @@
                 .append(" dataSpecificInfo=").append(mDataSpecificInfo)
                 .append(" nrState=").append(nrStateToString(mNrState))
                 .append(" rRplmn=").append(mRplmn)
+                .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation)
                 .append("}").toString();
     }
 
@@ -623,7 +646,8 @@
     public int hashCode() {
         return Objects.hash(mDomain, mTransportType, mRegistrationState, mRoamingType,
                 mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices,
-                mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn);
+                mCellIdentity, mVoiceSpecificInfo, mDataSpecificInfo, mNrState, mRplmn,
+                mIsUsingCarrierAggregation);
     }
 
     @Override
@@ -643,6 +667,7 @@
                 && mRejectCause == other.mRejectCause
                 && mEmergencyOnly == other.mEmergencyOnly
                 && mAvailableServices.equals(other.mAvailableServices)
+                && mIsUsingCarrierAggregation == other.mIsUsingCarrierAggregation
                 && Objects.equals(mCellIdentity, other.mCellIdentity)
                 && Objects.equals(mVoiceSpecificInfo, other.mVoiceSpecificInfo)
                 && Objects.equals(mDataSpecificInfo, other.mDataSpecificInfo)
@@ -669,6 +694,7 @@
         dest.writeParcelable(mDataSpecificInfo, 0);
         dest.writeInt(mNrState);
         dest.writeString(mRplmn);
+        dest.writeBoolean(mIsUsingCarrierAggregation);
     }
 
     /**
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index a9abe89..dd37ec3 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -266,7 +266,6 @@
      *
      * @return the ApnSetting that was used to configure this data connection.
      */
-    // FIXME: This shouldn't be nullable; update once the ApnSetting is supplied correctly
     public @Nullable ApnSetting getApnSetting() {
         return mApnSetting;
     }
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5c84297..243b24b 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1392,29 +1392,14 @@
 
     /** @hide */
     public boolean isUsingCarrierAggregation() {
-        boolean isUsingCa = false;
-        NetworkRegistrationInfo nri = getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (nri != null) {
-            DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
-            if (dsri != null) {
-                isUsingCa = dsri.isUsingCarrierAggregation();
-            }
-        }
-        return isUsingCa || getCellBandwidths().length > 1;
-    }
+        if (getCellBandwidths().length > 1) return true;
 
-    /** @hide */
-    public void setIsUsingCarrierAggregation(boolean ca) {
-        NetworkRegistrationInfo nri = getNetworkRegistrationInfo(
-                NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
-        if (nri != null) {
-            DataSpecificRegistrationInfo dsri = nri.getDataSpecificInfo();
-            if (dsri != null) {
-                dsri.setIsUsingCarrierAggregation(ca);
-                addNetworkRegistrationInfo(nri);
+        synchronized (mNetworkRegistrationInfos) {
+            for (NetworkRegistrationInfo nri : mNetworkRegistrationInfos) {
+                if (nri.isUsingCarrierAggregation()) return true;
             }
         }
+        return false;
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsCbEtwsInfo.java b/telephony/java/android/telephony/SmsCbEtwsInfo.java
index 2a7f7ad..a98916d 100644
--- a/telephony/java/android/telephony/SmsCbEtwsInfo.java
+++ b/telephony/java/android/telephony/SmsCbEtwsInfo.java
@@ -27,6 +27,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.DateTimeException;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.util.Arrays;
@@ -173,7 +174,7 @@
     /**
      * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
      * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
-     * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
+     * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present or invalid.
      */
     public long getPrimaryNotificationTimestamp() {
         if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
@@ -201,18 +202,23 @@
         // timezoneOffset is in quarter hours.
         int timeZoneOffsetSeconds = timezoneOffset * 15 * 60;
 
-        LocalDateTime localDateTime = LocalDateTime.of(
-                // We only need to support years above 2000.
-                year + 2000,
-                month /* 1-12 */,
-                day,
-                hour,
-                minute,
-                second);
+        try {
+            LocalDateTime localDateTime = LocalDateTime.of(
+                    // We only need to support years above 2000.
+                    year + 2000,
+                    month /* 1-12 */,
+                    day,
+                    hour,
+                    minute,
+                    second);
 
-        long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
-        // Convert to milliseconds, ignore overflow.
-        return epochSeconds * 1000;
+            long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
+            // Convert to milliseconds, ignore overflow.
+            return epochSeconds * 1000;
+        } catch (DateTimeException ex) {
+            // No-op
+        }
+        return 0;
     }
 
     /**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 7456aab..abcc82b 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -615,10 +615,12 @@
         }
 
         if (priority < 0x00 || priority > 0x03) {
+            Log.e(TAG, "Invalid Priority " + priority);
             priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
         }
 
         if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            Log.e(TAG, "Invalid Validity Period " + validityPeriod);
             validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
         }
 
@@ -1174,10 +1176,12 @@
         }
 
         if (priority < 0x00 || priority > 0x03) {
+            Log.e(TAG, "Invalid Priority " + priority);
             priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
         }
 
         if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+            Log.e(TAG, "Invalid Validity Period " + validityPeriod);
             validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
         }
 
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 7266fc0..392670b 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -255,28 +255,6 @@
     }
 
     /**
-     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
-     * +CMT unsolicited response (PDU mode, of course)
-     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
-     *
-     * Only public for debugging and for RIL
-     *
-     * {@hide}
-     */
-    public static SmsMessage newFromCMT(byte[] pdu) {
-        // received SMS in 3GPP format
-        SmsMessageBase wrappedMessage =
-                com.android.internal.telephony.gsm.SmsMessage.newFromCMT(pdu);
-
-        if (wrappedMessage != null) {
-            return new SmsMessage(wrappedMessage);
-        } else {
-            Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null");
-            return null;
-        }
-    }
-
-    /**
      * Creates an SmsMessage from an SMS EF record.
      *
      * @param index Index of SMS EF record.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 6897706..4c7253c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -110,8 +110,6 @@
 import com.android.internal.telephony.SmsApplication;
 import com.android.telephony.Rlog;
 
-import java.io.FileInputStream;
-import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -124,8 +122,6 @@
 import java.util.UUID;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Provides access to information about the telephony services on
@@ -2189,58 +2185,6 @@
     }
 
     /**
-     * Enables location update notifications.  {@link PhoneStateListener#onCellLocationChanged
-     * PhoneStateListener.onCellLocationChanged} will be called on location updates.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
-    public void enableLocationUpdates() {
-        enableLocationUpdates(getSubId());
-    }
-
-    /**
-     * Enables location update notifications for a subscription.
-     * {@link PhoneStateListener#onCellLocationChanged
-     * PhoneStateListener.onCellLocationChanged} will be called on location updates.
-     *
-     * @param subId for which the location updates are enabled
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
-    public void enableLocationUpdates(int subId) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.enableLocationUpdatesForSubscriber(subId);
-        } catch (RemoteException ex) {
-        } catch (NullPointerException ex) {
-        }
-    }
-
-    /**
-     * Disables location update notifications.  {@link PhoneStateListener#onCellLocationChanged
-     * PhoneStateListener.onCellLocationChanged} will be called on location updates.
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.CONTROL_LOCATION_UPDATES)
-    public void disableLocationUpdates() {
-        disableLocationUpdates(getSubId());
-    }
-
-    /** @hide */
-    public void disableLocationUpdates(int subId) {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.disableLocationUpdatesForSubscriber(subId);
-        } catch (RemoteException ex) {
-        } catch (NullPointerException ex) {
-        }
-    }
-
-    /**
      * Returns the neighboring cell information of the device.
      *
      * @return List of NeighboringCellInfo or null if info unavailable.
@@ -2440,7 +2384,8 @@
             return PhoneConstants.PHONE_TYPE_CDMA;
 
         case RILConstants.NETWORK_MODE_LTE_ONLY:
-            if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+            if (TelephonyProperties.lte_on_cdma_device().orElse(
+                    PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) {
                 return PhoneConstants.PHONE_TYPE_CDMA;
             } else {
                 return PhoneConstants.PHONE_TYPE_GSM;
@@ -2451,35 +2396,6 @@
     }
 
     /**
-     * The contents of the /proc/cmdline file
-     */
-    @UnsupportedAppUsage
-    private static String getProcCmdLine()
-    {
-        String cmdline = "";
-        FileInputStream is = null;
-        try {
-            is = new FileInputStream("/proc/cmdline");
-            byte [] buffer = new byte[2048];
-            int count = is.read(buffer);
-            if (count > 0) {
-                cmdline = new String(buffer, 0, count);
-            }
-        } catch (IOException e) {
-            Rlog.d(TAG, "No /proc/cmdline exception=" + e);
-        } finally {
-            if (is != null) {
-                try {
-                    is.close();
-                } catch (IOException e) {
-                }
-            }
-        }
-        Rlog.d(TAG, "/proc/cmdline=" + cmdline);
-        return cmdline;
-    }
-
-    /**
      * @return The max value for the timeout passed in {@link #requestNumberVerification}.
      * @hide
      */
@@ -2488,56 +2404,6 @@
         return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS;
     }
 
-    /** Kernel command line */
-    private static final String sKernelCmdLine = getProcCmdLine();
-
-    /** Pattern for selecting the product type from the kernel command line */
-    private static final Pattern sProductTypePattern =
-        Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
-
-    /** The ProductType used for LTE on CDMA devices */
-    private static final String sLteOnCdmaProductType =
-            TelephonyProperties.lte_on_cdma_product_type().orElse("");
-
-    /**
-     * Return if the current radio is LTE on CDMA. This
-     * is a tri-state return value as for a period of time
-     * the mode may be unknown.
-     *
-     * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
-     * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public static int getLteOnCdmaModeStatic() {
-        int retVal;
-        int curVal;
-        String productType = "";
-
-        curVal = TelephonyProperties.lte_on_cdma_device().orElse(
-            PhoneConstants.LTE_ON_CDMA_UNKNOWN);
-        retVal = curVal;
-        if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
-            Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
-            if (matcher.find()) {
-                productType = matcher.group(1);
-                if (sLteOnCdmaProductType.equals(productType)) {
-                    retVal = PhoneConstants.LTE_ON_CDMA_TRUE;
-                } else {
-                    retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
-                }
-            } else {
-                retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
-            }
-        }
-
-        Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal +
-                " product_type='" + productType +
-                "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
-        return retVal;
-    }
-
     //
     //
     // Current Network
@@ -8109,6 +7975,140 @@
         return false;
     }
 
+    /** @hide */
+    @IntDef({
+            ALLOWED_NETWORK_TYPES_REASON_POWER
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AllowedNetworkTypesReason{}
+
+    /**
+     * To indicate allowed network type change is requested by power manager.
+     * Power Manger configuration won't affect the settings configured through
+     * {@link setAllowedNetworkTypes} and will result in allowing network types that are in both
+     * configurations (i.e intersection of both sets).
+     * @hide
+     */
+    public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 0;
+
+    /**
+     * Set the allowed network types of the device and
+     * provide the reason triggering the allowed network change.
+     * This can be called for following reasons
+     * <ol>
+     * <li>Allowed network types control by power manager
+     * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
+     * </ol>
+     * This API will result in allowing an intersection of allowed network types for all reasons,
+     * including the configuration done through {@link setAllowedNetworkTypes}.
+     * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types
+     * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}.
+     * Thus resultant network type configured on modem will be an intersection of the network types
+     * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes}
+     * and {@link #setPreferredNetworkTypeBitmask}.
+     *
+     * @param reason the reason the allowed network type change is taking place
+     * @param allowedNetworkTypes The bitmask of allowed network types.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason,
+            @NetworkTypeBitMask long allowedNetworkTypes) {
+        if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
+            throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setAllowedNetworkTypesForReason(getSubId(), reason,
+                        allowedNetworkTypes);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setAllowedNetworkTypesForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the allowed network types for certain reason.
+     *
+     * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
+     * specific reason. For effective allowed network types configured on device,
+     * query {@link getEffectiveAllowedNetworkTypes}
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *s
+     * @param reason the reason the allowed network type change is taking place
+     * @return the allowed network type bitmask
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
+            @AllowedNetworkTypesReason int reason) {
+        if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
+            throw new IllegalArgumentException("invalid AllowedNetworkTypesReason.");
+        }
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getAllowedNetworkTypesForReason(getSubId(), reason);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getAllowedNetworkTypesForReason RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return -1;
+    }
+
+    /**
+     * Get bit mask of all network types.
+     *
+     * @return bit mask of all network types
+     * @hide
+     */
+    public static @NetworkTypeBitMask long getAllNetworkTypesBitmask() {
+        return NETWORK_STANDARDS_FAMILY_BITMASK_3GPP | NETWORK_STANDARDS_FAMILY_BITMASK_3GPP2;
+    }
+
+    /**
+     * Get the allowed network types configured on the device.
+     * This API will return an intersection of allowed network types for all reasons,
+     * including the configuration done through setAllowedNetworkTypes
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @return the allowed network type bitmask
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @NetworkTypeBitMask long getEffectiveAllowedNetworkTypes() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getEffectiveAllowedNetworkTypes(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getEffectiveAllowedNetworkTypes RemoteException", ex);
+            ex.rethrowFromSystemServer();
+        }
+        return -1;
+    }
+
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
@@ -8969,17 +8969,14 @@
         return RADIO_POWER_UNAVAILABLE;
     }
 
-    /** @hide */
+    /**
+     * This method should not be used due to privacy and stability concerns.
+     *
+     * @hide
+     */
     @SystemApi
-    @SuppressLint("Doclava125")
     public void updateServiceLocation() {
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.updateServiceLocation();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#updateServiceLocation", e);
-        }
+        Log.e(TAG, "Do not call TelephonyManager#updateServiceLocation()");
     }
 
     /** @hide */
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5a6b997..bf81ddc 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -222,42 +222,29 @@
     boolean setRadioPower(boolean turnOn);
 
     /**
-     * Request to update location information in service state
+     * This method has been removed due to security and stability issues.
      */
     @UnsupportedAppUsage
     void updateServiceLocation();
 
     /**
-     * Request to update location information for a subscrition in service state
-     * @param subId user preferred subId.
+     * Version of updateServiceLocation that records the caller and validates permissions.
      */
-    void updateServiceLocationForSubscriber(int subId);
+    void updateServiceLocationWithPackageName(String callingPkg);
 
     /**
-     * Enable location update notifications.
+     * This method has been removed due to security and stability issues.
      */
     @UnsupportedAppUsage
     void enableLocationUpdates();
 
     /**
-     * Enable location update notifications.
-     * @param subId user preferred subId.
-     */
-    void enableLocationUpdatesForSubscriber(int subId);
-
-    /**
-     * Disable location update notifications.
+     * This method has been removed due to security and stability issues.
      */
     @UnsupportedAppUsage
     void disableLocationUpdates();
 
     /**
-     * Disable location update notifications.
-     * @param subId user preferred subId.
-     */
-    void disableLocationUpdatesForSubscriber(int subId);
-
-    /**
      * Allow mobile data connections.
      */
     @UnsupportedAppUsage
@@ -956,6 +943,35 @@
     boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes);
 
     /**
+     * Get the allowed network types for certain reason.
+     *
+     * @param subId the id of the subscription.
+     * @param reason the reason the allowed network type change is taking place
+     * @return allowedNetworkTypes the allowed network types.
+     */
+    long getAllowedNetworkTypesForReason(int subId, int reason);
+
+    /**
+     * Get the effective allowed network types on the device. This API will
+     * return an intersection of allowed network types for all reasons,
+     * including the configuration done through setAllowedNetworkTypes
+     *
+     * @param subId the id of the subscription.
+     * @return allowedNetworkTypes the allowed network types.
+     */
+     long getEffectiveAllowedNetworkTypes(int subId);
+
+    /**
+     * Set the allowed network types and provide the reason triggering the allowed network change.
+     *
+     * @param subId the id of the subscription.
+     * @param reason the reason the allowed network type change is taking place
+     * @param allowedNetworkTypes the allowed network types.
+     * @return true on success; false on any failure.
+     */
+    boolean setAllowedNetworkTypesForReason(int subId, int reason, long allowedNetworkTypes);
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 3bd8cdd..f8ab87d 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -65,13 +65,7 @@
             return "";
         }
 
-        if (!mIs7BitTranslationTableLoaded) {
-            mTranslationTableCommon = new SparseIntArray();
-            mTranslationTableGSM = new SparseIntArray();
-            mTranslationTableCDMA = new SparseIntArray();
-            load7BitTranslationTableFromXml();
-            mIs7BitTranslationTableLoaded = true;
-        }
+        ensure7BitTranslationTableLoaded();
 
         if ((mTranslationTableCommon != null && mTranslationTableCommon.size() > 0) ||
                 (mTranslationTableGSM != null && mTranslationTableGSM.size() > 0) ||
@@ -115,6 +109,8 @@
          */
         int translation = -1;
 
+        ensure7BitTranslationTableLoaded();
+
         if (mTranslationTableCommon != null) {
             translation = mTranslationTableCommon.get(c, -1);
         }
@@ -155,6 +151,18 @@
         }
     }
 
+    private static void ensure7BitTranslationTableLoaded() {
+        synchronized (Sms7BitEncodingTranslator.class) {
+            if (!mIs7BitTranslationTableLoaded) {
+                mTranslationTableCommon = new SparseIntArray();
+                mTranslationTableGSM = new SparseIntArray();
+                mTranslationTableCDMA = new SparseIntArray();
+                load7BitTranslationTableFromXml();
+                mIs7BitTranslationTableLoaded = true;
+            }
+        }
+    }
+
     /**
      * Load the whole translation table file from the framework resource
      * encoded in XML.
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 3f85de3..109dd3b 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -35,6 +35,7 @@
 import com.android.telephony.Rlog;
 
 import java.io.ByteArrayOutputStream;
+import java.time.DateTimeException;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -315,10 +316,16 @@
         }
 
         public long toMillis() {
-            LocalDateTime localDateTime =
-                    LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
-            Instant instant = localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
-            return instant.toEpochMilli();
+            try {
+                LocalDateTime localDateTime =
+                        LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
+                Instant instant =
+                        localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
+                return instant.toEpochMilli();
+            } catch (DateTimeException ex) {
+                Rlog.e(LOG_TAG, "Invalid timestamp", ex);
+            }
+            return 0;
         }
 
 
@@ -1093,7 +1100,7 @@
             bData.hasUserDataHeader = (inStream.read(1) == 1);
             inStream.skip(3);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1462,7 +1469,7 @@
             bData.reportReq      = (inStream.read(1) == 1);
             inStream.skip(4);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1481,7 +1488,7 @@
             decodeSuccess = true;
             bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1500,7 +1507,7 @@
             decodeSuccess = true;
             bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1587,7 +1594,7 @@
             bData.errorClass = inStream.read(2);
             bData.messageStatus = inStream.read(6);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1607,7 +1614,7 @@
             decodeSuccess = true;
             bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1626,7 +1633,7 @@
             decodeSuccess = true;
             bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1646,7 +1653,7 @@
             bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
                     inStream.readByteArray(6 * 8));
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1665,7 +1672,7 @@
             decodeSuccess = true;
             bData.deferredDeliveryTimeRelative = inStream.read(8);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1685,7 +1692,7 @@
             decodeSuccess = true;
             bData.validityPeriodRelative = inStream.read(8);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1706,7 +1713,7 @@
             bData.privacy = inStream.read(2);
             inStream.skip(6);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1726,7 +1733,7 @@
             decodeSuccess = true;
             bData.language = inStream.read(8);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1747,7 +1754,7 @@
             bData.displayMode = inStream.read(2);
             inStream.skip(6);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1768,7 +1775,7 @@
             bData.priority = inStream.read(2);
             inStream.skip(6);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1789,7 +1796,7 @@
             bData.alert = inStream.read(2);
             inStream.skip(6);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1809,7 +1816,7 @@
             decodeSuccess = true;
             bData.userResponseCode = inStream.read(8);
         }
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ")");
@@ -1871,7 +1878,7 @@
             decodeSuccess = true;
         }
 
-        if ((! decodeSuccess) || (paramBits > 0)) {
+        if ((!decodeSuccess) || (paramBits > 0)) {
             Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
                       (decodeSuccess ? "succeeded" : "failed") +
                       " (extra bits = " + paramBits + ')');
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 122f085..7d5710e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -43,6 +43,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
+import java.time.DateTimeException;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -91,14 +92,15 @@
 
     private int mVoiceMailCount = 0;
 
+    /** TP-Validity-Period-Format (TP-VPF). See TS 23.040, 9.2.3.3 */
     private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
     private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
     private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
     private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
 
-    //Validity Period min - 5 mins
+    // Validity Period min - 5 mins
     private static final int VALIDITY_PERIOD_MIN = 5;
-    //Validity Period max - 63 weeks
+    // Validity Period max - 63 weeks
     private static final int VALIDITY_PERIOD_MAX = 635040;
 
     private static final int INVALID_VALIDITY_PERIOD = -1;
@@ -140,38 +142,6 @@
     }
 
     /**
-     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
-     * +CMT unsolicited response (PDU mode, of course)
-     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
-     *
-     * Only public for debugging
-     *
-     * {@hide}
-     */
-    public static SmsMessage newFromCMT(byte[] pdu) {
-        try {
-            SmsMessage msg = new SmsMessage();
-            msg.parsePdu(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        }
-    }
-
-    /** @hide */
-    public static SmsMessage newFromCDS(byte[] pdu) {
-        try {
-            SmsMessage msg = new SmsMessage();
-            msg.parsePdu(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
-            return null;
-        }
-    }
-
-    /**
      * Creates an SmsMessage from an SMS EF record.
      *
      * @param index Index of SMS EF record.
@@ -225,20 +195,20 @@
     }
 
     /**
-     * Get Encoded Relative Validty Period Value from Validity period in mins.
+     * Gets Encoded Relative Validity Period Value from Validity period in mins.
      *
      * @param validityPeriod Validity period in mins.
      *
      * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
-     * ||relValidityPeriod (TP-VP)  ||                 ||  validityPeriod   ||
-     *
-     *      0 to 143                            --->       (TP-VP + 1) x 5 minutes
-     *
-     *      144 to 167                         --->        12 hours + ((TP-VP -143) x 30 minutes)
-     *
-     *      168 to 196                         --->        (TP-VP - 166) x 1 day
-     *
-     *      197 to 255                         --->        (TP-VP - 192) x 1 week
+     * ------------------------------------------------------------
+     *        TP-VP       |            Validity period
+     *  (Relative format) |                 value
+     * ------------------------------------------------------------
+     *  0 to 143          | (TP-VP + 1) x 5 minutes
+     *  144 to 167        | 12 hours + ((TP-VP -143) x 30 minutes)
+     *  168 to 196        | (TP-VP - 166) x 1 day
+     *  197 to 255        | (TP-VP - 192) x 1 week
+     * ------------------------------------------------------------
      *
      * @return relValidityPeriod Encoded Relative Validity Period Value.
      * @hide
@@ -246,19 +216,16 @@
     public static int getRelativeValidityPeriod(int validityPeriod) {
         int relValidityPeriod = INVALID_VALIDITY_PERIOD;
 
-        if (validityPeriod < VALIDITY_PERIOD_MIN  || validityPeriod > VALIDITY_PERIOD_MAX) {
-            Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
-            return relValidityPeriod;
-        }
-
-        if (validityPeriod <= 720) {
-            relValidityPeriod = (validityPeriod  / 5) - 1;
-        } else if (validityPeriod <= 1440) {
-            relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
-        } else if (validityPeriod <= 43200) {
-            relValidityPeriod = (validityPeriod  / 1440) + 166;
-        } else if (validityPeriod <= 635040) {
-            relValidityPeriod = (validityPeriod  / 10080) + 192;
+        if (validityPeriod >= VALIDITY_PERIOD_MIN) {
+            if (validityPeriod <= 720) {
+                relValidityPeriod = (validityPeriod / 5) - 1;
+            } else if (validityPeriod <= 1440) {
+                relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
+            } else if (validityPeriod <= 43200) {
+                relValidityPeriod = (validityPeriod / 1440) + 166;
+            } else if (validityPeriod <= VALIDITY_PERIOD_MAX) {
+                relValidityPeriod = (validityPeriod / 10080) + 192;
+            }
         }
         return relValidityPeriod;
     }
@@ -368,17 +335,19 @@
 
         SubmitPdu ret = new SubmitPdu();
 
-        int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
-        int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
+        int relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod);
 
-        // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
-        //bit 4:3 = 10 - TP-VP field present - relative format
-        if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
-            validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
+        byte mtiByte = 0x01; // SMS-SUBMIT
+
+        if (header != null) {
+            // Set TP-UDHI
+            mtiByte |= 0x40;
         }
 
-        byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
-                (header != null ? 0x40 : 0x00));
+        if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
+            // Set TP-Validity-Period-Format (TP-VPF)
+            mtiByte |= VALIDITY_PERIOD_FORMAT_RELATIVE << 3;
+        }
 
         ByteArrayOutputStream bo = getSubmitPduHead(
                 scAddress, destinationAddress, mtiByte,
@@ -450,8 +419,8 @@
             bo.write(0x08);
         }
 
-        if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
-            // ( TP-Validity-Period - relative format)
+        // TP-Validity-Period (TP-VP)
+        if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
             bo.write(relativeValidityPeriod);
         }
 
@@ -888,10 +857,9 @@
         }
 
         /**
-         * Parses an SC timestamp and returns a currentTimeMillis()-style
-         * timestamp
+         * Parses an SC timestamp and returns a currentTimeMillis()-style timestamp, or 0 if
+         * invalid.
          */
-
         long getSCTimestampMillis() {
             // TP-Service-Centre-Time-Stamp
             int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
@@ -917,16 +885,22 @@
 
             // It's 2006.  Should I really support years < 2000?
             int fullYear = year >= 90 ? year + 1900 : year + 2000;
-            LocalDateTime localDateTime = LocalDateTime.of(
-                    fullYear,
-                    month /* 1-12 */,
-                    day,
-                    hour,
-                    minute,
-                    second);
-            long epochSeconds = localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
-            // Convert to milliseconds.
-            return epochSeconds * 1000;
+            try {
+                LocalDateTime localDateTime = LocalDateTime.of(
+                        fullYear,
+                        month /* 1-12 */,
+                        day,
+                        hour,
+                        minute,
+                        second);
+                long epochSeconds =
+                        localDateTime.toEpochSecond(ZoneOffset.UTC) - timeZoneOffsetSeconds;
+                // Convert to milliseconds.
+                return epochSeconds * 1000;
+            } catch (DateTimeException ex) {
+                Rlog.e(LOG_TAG, "Invalid timestamp", ex);
+            }
+            return 0;
         }
 
         /**
@@ -1277,6 +1251,7 @@
         mRecipientAddress = p.getAddress();
         // TP-Service-Centre-Time-Stamp
         mScTimeMillis = p.getSCTimestampMillis();
+        // TP-Discharge-Time
         p.getSCTimestampMillis();
         // TP-Status
         mStatus = p.getByte();
@@ -1335,6 +1310,7 @@
                     + " data coding scheme: " + mDataCodingScheme);
         }
 
+        // TP-Service-Centre-Time-Stamp
         mScTimeMillis = p.getSCTimestampMillis();
 
         if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
@@ -1377,23 +1353,17 @@
 
         // TP-Validity-Period-Format
         int validityPeriodLength = 0;
-        int validityPeriodFormat = ((firstByte>>3) & 0x3);
-        if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
-        {
+        int validityPeriodFormat = ((firstByte >> 3) & 0x3);
+        if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_NONE) {
             validityPeriodLength = 0;
-        }
-        else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
-        {
+        } else if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
             validityPeriodLength = 1;
-        }
-        else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
-        {
+        } else { // VALIDITY_PERIOD_FORMAT_ENHANCED or VALIDITY_PERIOD_FORMAT_ABSOLUTE
             validityPeriodLength = 7;
         }
 
         // TP-Validity-Period is not used on phone, so just ignore it for now.
-        while (validityPeriodLength-- > 0)
-        {
+        while (validityPeriodLength-- > 0) {
             p.getByte();
         }
 
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 5dd0fda..9ba56e4 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -26,6 +26,7 @@
 import android.net.NetworkStats.ROAMING_ALL
 import android.net.NetworkTemplate.MATCH_MOBILE
 import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
 import android.net.NetworkTemplate.NETWORK_TYPE_ALL
 import android.net.NetworkTemplate.buildTemplateMobileWithRatType
 import android.telephony.TelephonyManager
@@ -145,11 +146,13 @@
         assertParcelSane(templateWifi, 8)
     }
 
-    // Verify NETWORK_TYPE_ALL does not conflict with TelephonyManager#NETWORK_TYPE_* constants.
+    // Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with
+    // TelephonyManager#NETWORK_TYPE_* constants.
     @Test
-    fun testNetworkTypeAll() {
+    fun testNetworkTypeConstants() {
         for (ratType in TelephonyManager.getAllNetworkTypes()) {
             assertNotEquals(NETWORK_TYPE_ALL, ratType)
+            assertNotEquals(NETWORK_TYPE_5G_NSA, ratType)
         }
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 5eea0e8..fdc6084 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -114,6 +114,7 @@
     @Mock private INetd mNetdService;
     @Mock private PackageManagerInternal mMockPmi;
     @Mock private UserManager mUserManager;
+    @Mock private PermissionMonitor.Dependencies mDeps;
 
     private PermissionMonitor mPermissionMonitor;
 
@@ -128,7 +129,7 @@
                         new UserInfo(MOCK_USER2, "", 0),
                 }));
 
-        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService));
+        mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
         LocalServices.addService(PackageManagerInternal.class, mMockPmi);
@@ -283,14 +284,14 @@
 
     @Test
     public void testHasRestrictedNetworkPermissionSystemUid() {
-        doReturn(VERSION_P).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
         assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
         assertTrue(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
-        doReturn(VERSION_Q).when(mPermissionMonitor).getDeviceFirstSdkInt();
+        doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
         assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
         assertFalse(hasRestrictedNetworkPermission(
                 PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index c813269..9531b0a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -17,6 +17,7 @@
 package com.android.server.net;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
@@ -30,7 +31,9 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.net.NetworkTemplate;
 import android.os.test.TestLooper;
+import android.telephony.NetworkRegistrationInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
@@ -61,7 +64,6 @@
     private static final String TEST_IMSI3 = "466929999999999";
 
     @Mock private Context mContext;
-    @Mock private PhoneStateListener mPhoneStateListener;
     @Mock private SubscriptionManager mSubscriptionManager;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private NetworkStatsSubscriptionsMonitor.Delegate mDelegate;
@@ -215,4 +217,55 @@
         verify(mTelephonyManager, times(2)).listen(any(), eq(PhoneStateListener.LISTEN_NONE));
         assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
     }
+
+
+    @Test
+    public void test5g() {
+        mMonitor.start();
+        // Insert sim1, verify RAT type is NETWORK_TYPE_UNKNOWN, and never get any callback
+        // before changing RAT type. Also capture listener for later use.
+        addTestSub(TEST_SUBID1, TEST_IMSI1);
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        final RatTypeListener listener = CollectionUtils
+                .find(ratTypeListenerCaptor.getAllValues(), it -> it.getSubId() == TEST_SUBID1);
+        assertNotNull(listener);
+
+        // Set RAT type to 5G NSA (non-standalone) mode, verify the monitor outputs
+        // NETWORK_TYPE_5G_NSA.
+        final ServiceState serviceState = mock(ServiceState.class);
+        when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE);
+        when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+        listener.onServiceStateChanged(serviceState);
+        assertRatTypeChangedForSub(TEST_IMSI1, NetworkTemplate.NETWORK_TYPE_5G_NSA);
+        reset(mDelegate);
+
+        // Set RAT type to LTE without NR connected, the RAT type should be downgraded to LTE.
+        when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+        listener.onServiceStateChanged(serviceState);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+        reset(mDelegate);
+
+        // Verify NR connected with other RAT type does not take effect.
+        when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_UMTS);
+        when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_CONNECTED);
+        listener.onServiceStateChanged(serviceState);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
+
+        // Set RAT type to 5G standalone mode, the RAT type should be NR.
+        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_NR);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+        reset(mDelegate);
+
+        // Set NR state to none in standalone mode does not change anything.
+        when(serviceState.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_NR);
+        when(serviceState.getNrState()).thenReturn(NetworkRegistrationInfo.NR_STATE_NONE);
+        listener.onServiceStateChanged(serviceState);
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
+    }
 }