Merge "Modify isShowing method in Dialog"
diff --git a/Android.bp b/Android.bp
index 010f496..80df8c5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -478,7 +478,8 @@
 	    "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
         "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
-        "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
+        "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
+        "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
         "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
         "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
@@ -784,3 +785,174 @@
         "fontTools",
     ],
 }
+
+// TODO: Don't rely on this list once droiddoc can take a list of packages to document
+frameworks_base_subdirs = [
+    "core/java",
+    "graphics/java",
+    "location/java",
+    "media/java",
+    "media/mca/effect/java",
+    "media/mca/filterfw/java",
+    "media/mca/filterpacks/java",
+    "drm/java",
+    "opengl/java",
+    "sax/java",
+    "telecomm/java",
+    "telephony/java",
+    "wifi/java",
+    "lowpan/java",
+    "keystore/java",
+    "rs/java",
+]
+
+packages_to_document = [
+    "android",
+    "javax/microedition/khronos",
+    "org/apache/http/conn",
+    "org/apache/http/params",
+]
+
+// The since flag (-since N.xml API_LEVEL) is used to add API Level information
+// to the reference documentation. Must be in order of oldest to newest.
+//
+// Conscrypt (com.android.org.conscrypt) is an implementation detail and should
+// not be referenced in the documentation.
+framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " +
+     "-hidePackage com.android.okhttp -hidePackage com.android.org.conscrypt -hidePackage com.android.server " +
+     "-since $(location api/1.xml) 1 " +
+     "-since $(location api/2.xml) 2 " +
+     "-since $(location api/3.xml) 3 " +
+     "-since $(location api/4.xml) 4 " +
+     "-since $(location api/5.xml) 5 " +
+     "-since $(location api/6.xml) 6 " +
+     "-since $(location api/7.xml) 7 " +
+     "-since $(location api/8.xml) 8 " +
+     "-since $(location api/9.xml) 9 " +
+     "-since $(location api/10.xml) 10 " +
+     "-since $(location api/11.xml) 11 " +
+     "-since $(location api/12.xml) 12 " +
+     "-since $(location api/13.xml) 13 " +
+     "-since $(location api/14.txt) 14 " +
+     "-since $(location api/15.txt) 15 " +
+     "-since $(location api/16.txt) 16 " +
+     "-since $(location api/17.txt) 17 " +
+     "-since $(location api/18.txt) 18 " +
+     "-since $(location api/19.txt) 19 " +
+     "-since $(location api/20.txt) 20 " +
+     "-since $(location api/21.txt) 21 " +
+     "-since $(location api/22.txt) 22 " +
+     "-since $(location api/23.txt) 23 " +
+     "-since $(location api/24.txt) 24 " +
+     "-since $(location api/25.txt) 25 " +
+     "-since $(location api/26.txt) 26 " +
+     "-since $(location api/27.txt) 27 " +
+     "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
+     "-overview $(location core/java/overview.html) " +
+     // Federate Support Library references against local API file.
+     "-federate SupportLib https://developer.android.com " +
+     "-federationapi SupportLib $(location current/support-api.txt) "
+
+doc_defaults {
+    name: "framework-docs-default",
+    srcs: [
+        // test mock src files.
+        "test-mock/src/android/test/mock/**/*.java",
+        // test runner excluding mock src files.
+        "test-runner/src/**/*.java",
+        "test-base/src/**/*.java",
+        ":opt-telephony-srcs",
+        ":opt-net-voip-srcs",
+        ":openjdk_javadoc_files",
+        ":non_openjdk_javadoc_files",
+        ":android_icu4j_src_files_for_docs",
+        ":gen-ojluni-jaif-annotated-srcs",
+    ],
+    exclude_srcs: [
+        ":annotated_ojluni_files",
+    ],
+    srcs_lib: "framework",
+    srcs_lib_whitelist_dirs: frameworks_base_subdirs,
+    srcs_lib_whitelist_pkgs: packages_to_document,
+    libs: [
+        "core-oj",
+        "core-libart",
+        "conscrypt",
+        "bouncycastle",
+        "okhttp",
+        "ext",
+        "framework",
+        "voip-common",
+        "android.test.mock",
+    ],
+    local_sourcepaths: frameworks_base_subdirs,
+    html_dirs: [
+        "docs/html",
+    ],
+    knowntags: [
+        "docs/knowntags.txt",
+        ":known-oj-tags",
+    ],
+    custom_template: "droiddoc-templates-sdk",
+    hdf: [
+        "dac true",
+        "sdk.codename O",
+        "sdk.preview.version 1",
+        "sdk.version 7.0",
+        "sdk.rel.id 1",
+        "sdk.preview 0",
+    ],
+    resourcesdir: "docs/html/reference/images",
+    resourcesoutdir: "reference/android/images",
+    installable: false,
+}
+
+droiddoc {
+    name: "api-stubs-docs",
+    defaults: ["framework-docs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+        ":api-version-xml",
+        "core/java/overview.html",
+        ":current-support-api",
+    ],
+    api_filename: "public_api.txt",
+    private_api_filename: "private.txt",
+    private_dex_api_filename: "private-dex.txt",
+    removed_api_filename: "removed.txt",
+    args: framework_docs_args + " -referenceonly -nodocs",
+}
+
+droiddoc {
+    name: "system-api-stubs-docs",
+    defaults: ["framework-docs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+        ":api-version-xml",
+        "core/java/overview.html",
+        ":current-support-api",
+    ],
+    api_tag_name: "SYSTEM",
+    api_filename: "system-api.txt",
+    private_api_filename: "system-private.txt",
+    private_dex_api_filename: "system-private-dex.txt",
+    removed_api_filename: "system-removed.txt",
+    exact_api_filename: "system-exact.txt",
+    args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.SystemApi -nodocs",
+}
+
+droiddoc {
+    name: "test-api-stubs-docs",
+    defaults: ["framework-docs-default"],
+    arg_files: [
+        "core/res/AndroidManifest.xml",
+        ":api-version-xml",
+        "core/java/overview.html",
+        ":current-support-api",
+    ],
+    api_tag_name: "TEST",
+    api_filename: "test-api.txt",
+    removed_api_filename: "test-removed.txt",
+    exact_api_filename: "test-exact.txt",
+    args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.TestApi -nodocs",
+}
diff --git a/Android.mk b/Android.mk
index 147d2cf..b5b4627 100644
--- a/Android.mk
+++ b/Android.mk
@@ -260,116 +260,9 @@
 		-federate SupportLib https://developer.android.com \
 		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
 
-# ====  the api stubs and current.xml ===========================
-include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := api-stubs
-
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_stubs_current_intermediates/src
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-referenceonly \
-		-api $(INTERNAL_PLATFORM_API_FILE) \
-		-privateApi $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
-		-privateDexApi $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-		-removedApi $(INTERNAL_PLATFORM_REMOVED_API_FILE) \
-		-nodocs
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_API_FILE) \
-                                          $(INTERNAL_PLATFORM_PRIVATE_API_FILE) \
-                                          $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
-
-# ====  the system api stubs ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := system-api-stubs
-
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_system_stubs_current_intermediates/src
-
-LOCAL_DROIDDOC_OPTIONS:=\
-		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-		-referenceonly \
-		-showAnnotation android.annotation.SystemApi \
-		-api $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
-		-privateApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
-		-privateDexApi $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE) \
-		-removedApi $(INTERNAL_PLATFORM_SYSTEM_REMOVED_API_FILE) \
-		-exactApi $(INTERNAL_PLATFORM_SYSTEM_EXACT_API_FILE) \
-		-nodocs
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-$(full_target): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_SYSTEM_API_FILE) \
-                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_API_FILE) \
-                                          $(INTERNAL_PLATFORM_SYSTEM_PRIVATE_DEX_API_FILE)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
-
-# ====  the test api stubs ===================================
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
-LOCAL_GENERATED_SOURCES:=$(framework_docs_LOCAL_GENERATED_SOURCES)
-LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
-LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
-LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
-LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
-LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
-
-LOCAL_MODULE := test-api-stubs
-
-LOCAL_DROIDDOC_STUB_OUT_DIR := $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_test_stubs_current_intermediates/src
-
-LOCAL_DROIDDOC_OPTIONS:=\
-               $(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
-               -referenceonly \
-               -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/android_test_stubs_current_intermediates/src \
-               -showAnnotation android.annotation.TestApi \
-               -api $(INTERNAL_PLATFORM_TEST_API_FILE) \
-               -removedApi $(INTERNAL_PLATFORM_TEST_REMOVED_API_FILE) \
-               -exactApi $(INTERNAL_PLATFORM_TEST_EXACT_API_FILE) \
-               -nodocs
-
-LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=external/doclava/res/assets/templates-sdk
-
-LOCAL_UNINSTALLABLE_MODULE := true
-
-include $(BUILD_DROIDDOC)
-
-$(INTERNAL_PLATFORM_TEST_API_FILE): $(full_target)
 $(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_TEST_API_FILE))
 
 # ====  check javadoc comments but don't generate docs ========
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 50a5974..75b7491 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,4 +6,5 @@
                       packages/PrintSpooler/
                       services/print/
                       services/usb/
+                      telephony/
 
diff --git a/api/current.txt b/api/current.txt
index 2250379..7f076cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7922,7 +7922,7 @@
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method public int getConnectionState(android.bluetooth.BluetoothDevice);
     method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
-    method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback);
+    method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, java.util.concurrent.Executor, android.bluetooth.BluetoothHidDevice.Callback);
     method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
     method public boolean reportError(android.bluetooth.BluetoothDevice, byte);
     method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]);
@@ -7952,49 +7952,8 @@
     field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0
   }
 
-  public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable {
-    ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int);
-    method public int describeContents();
-    method public int[] toArray();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR;
-    field public static final int MAX = -1; // 0xffffffff
-    field public static final int SERVICE_BEST_EFFORT = 1; // 0x1
-    field public static final int SERVICE_GUARANTEED = 2; // 0x2
-    field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0
-    field public final int delayVariation;
-    field public final int latency;
-    field public final int peakBandwidth;
-    field public final int serviceType;
-    field public final int tokenBucketSize;
-    field public final int tokenRate;
-  }
-
-  public static class BluetoothHidDeviceAppQosSettings.Builder {
-    ctor public BluetoothHidDeviceAppQosSettings.Builder();
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings build();
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int);
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int);
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int);
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int);
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int);
-    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int);
-  }
-
-  public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable {
-    ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR;
-    field public final java.lang.String description;
-    field public final byte[] descriptors;
-    field public final java.lang.String name;
-    field public final java.lang.String provider;
-    field public final byte subclass;
-  }
-
-  public abstract class BluetoothHidDeviceCallback {
-    ctor public BluetoothHidDeviceCallback();
+  public static abstract class BluetoothHidDevice.Callback {
+    ctor public BluetoothHidDevice.Callback();
     method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean);
     method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int);
     method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int);
@@ -8004,6 +7963,35 @@
     method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice);
   }
 
+  public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable {
+    ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int);
+    method public int describeContents();
+    method public int getDelayVariation();
+    method public int getLatency();
+    method public int getPeakBandwidth();
+    method public int getServiceType();
+    method public int getTokenBucketSize();
+    method public int getTokenRate();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR;
+    field public static final int MAX = -1; // 0xffffffff
+    field public static final int SERVICE_BEST_EFFORT = 1; // 0x1
+    field public static final int SERVICE_GUARANTEED = 2; // 0x2
+    field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0
+  }
+
+  public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable {
+    ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]);
+    method public int describeContents();
+    method public java.lang.String getDescription();
+    method public byte[] getDescriptors();
+    method public java.lang.String getName();
+    method public java.lang.String getProvider();
+    method public byte getSubclass();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR;
+  }
+
   public final class BluetoothManager {
     method public android.bluetooth.BluetoothAdapter getAdapter();
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -25736,17 +25724,17 @@
     field public static final int RESTRICT_BACKGROUND_STATUS_DISABLED = 1; // 0x1
     field public static final int RESTRICT_BACKGROUND_STATUS_ENABLED = 3; // 0x3
     field public static final int RESTRICT_BACKGROUND_STATUS_WHITELISTED = 2; // 0x2
-    field public static final int TYPE_BLUETOOTH = 7; // 0x7
-    field public static final int TYPE_DUMMY = 8; // 0x8
-    field public static final int TYPE_ETHERNET = 9; // 0x9
-    field public static final int TYPE_MOBILE = 0; // 0x0
-    field public static final int TYPE_MOBILE_DUN = 4; // 0x4
+    field public static final deprecated int TYPE_BLUETOOTH = 7; // 0x7
+    field public static final deprecated int TYPE_DUMMY = 8; // 0x8
+    field public static final deprecated int TYPE_ETHERNET = 9; // 0x9
+    field public static final deprecated int TYPE_MOBILE = 0; // 0x0
+    field public static final deprecated int TYPE_MOBILE_DUN = 4; // 0x4
     field public static final deprecated int TYPE_MOBILE_HIPRI = 5; // 0x5
     field public static final deprecated int TYPE_MOBILE_MMS = 2; // 0x2
     field public static final deprecated int TYPE_MOBILE_SUPL = 3; // 0x3
-    field public static final int TYPE_VPN = 17; // 0x11
-    field public static final int TYPE_WIFI = 1; // 0x1
-    field public static final int TYPE_WIMAX = 6; // 0x6
+    field public static final deprecated int TYPE_VPN = 17; // 0x11
+    field public static final deprecated int TYPE_WIFI = 1; // 0x1
+    field public static final deprecated int TYPE_WIMAX = 6; // 0x6
   }
 
   public static class ConnectivityManager.NetworkCallback {
@@ -25840,8 +25828,8 @@
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public void close() throws java.io.IOException;
+    method public java.io.FileDescriptor getFileDescriptor();
     method public int getPort();
-    method public java.io.FileDescriptor getSocket();
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -26023,16 +26011,16 @@
     method public int describeContents();
     method public android.net.NetworkInfo.DetailedState getDetailedState();
     method public java.lang.String getExtraInfo();
-    method public java.lang.String getReason();
-    method public android.net.NetworkInfo.State getState();
+    method public deprecated java.lang.String getReason();
+    method public deprecated android.net.NetworkInfo.State getState();
     method public int getSubtype();
     method public java.lang.String getSubtypeName();
-    method public int getType();
-    method public java.lang.String getTypeName();
-    method public boolean isAvailable();
+    method public deprecated int getType();
+    method public deprecated java.lang.String getTypeName();
+    method public deprecated boolean isAvailable();
     method public boolean isConnected();
-    method public boolean isConnectedOrConnecting();
-    method public boolean isFailover();
+    method public deprecated boolean isConnectedOrConnecting();
+    method public deprecated boolean isFailover();
     method public deprecated boolean isRoaming();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
@@ -27546,7 +27534,7 @@
     field public static final java.lang.String EXTRA_ID = "android.nfc.extra.ID";
     field public static final java.lang.String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
     field public static final java.lang.String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
-    field public static final java.lang.String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    field public static final java.lang.String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
     field public static final int FLAG_READER_NFC_A = 1; // 0x1
     field public static final int FLAG_READER_NFC_B = 2; // 0x2
@@ -37023,17 +37011,17 @@
 
 package android.se.omapi {
 
-  public class Channel {
+  public final class Channel implements java.nio.channels.Channel {
     method public void close();
     method public byte[] getSelectResponse();
     method public android.se.omapi.Session getSession();
     method public boolean isBasicChannel();
-    method public boolean isClosed();
+    method public boolean isOpen();
     method public boolean selectNext() throws java.io.IOException;
     method public byte[] transmit(byte[]) throws java.io.IOException;
   }
 
-  public class Reader {
+  public final class Reader {
     method public void closeSessions();
     method public java.lang.String getName();
     method public android.se.omapi.SEService getSEService();
@@ -37041,21 +37029,19 @@
     method public android.se.omapi.Session openSession() throws java.io.IOException;
   }
 
-  public class SEService {
-    ctor public SEService(android.content.Context, android.se.omapi.SEService.SecureElementListener);
+  public final class SEService {
+    ctor public SEService(android.content.Context, java.util.concurrent.Executor, android.se.omapi.SEService.OnConnectedListener);
     method public android.se.omapi.Reader[] getReaders();
     method public java.lang.String getVersion();
     method public boolean isConnected();
     method public void shutdown();
   }
 
-  public static abstract class SEService.SecureElementListener extends android.os.Binder {
-    ctor public SEService.SecureElementListener();
-    method public android.os.IBinder asBinder();
-    method public void serviceConnected();
+  public static abstract interface SEService.OnConnectedListener {
+    method public abstract void onConnected();
   }
 
-  public class Session {
+  public final class Session {
     method public void close();
     method public void closeChannels();
     method public byte[] getATR();
@@ -39194,7 +39180,7 @@
     method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
     field public static final int HANDOVER_FAILURE_DEST_APP_REJECTED = 1; // 0x1
     field public static final int HANDOVER_FAILURE_NOT_SUPPORTED = 2; // 0x2
-    field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4; // 0x4
+    field public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4; // 0x4
     field public static final int HANDOVER_FAILURE_UNKNOWN = 5; // 0x5
     field public static final int HANDOVER_FAILURE_USER_REJECTED = 3; // 0x3
   }
@@ -39406,7 +39392,7 @@
     method public final void putExtras(android.os.Bundle);
     method public final void removeExtras(java.util.List<java.lang.String>);
     method public final void removeExtras(java.lang.String...);
-    method public void requestBluetoothAudio(java.lang.String);
+    method public void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
     method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
     method public final void sendRemoteRttRequest();
     method public final void sendRttInitiationFailure(int);
@@ -39617,7 +39603,7 @@
     method public void onCanAddCallChanged(boolean);
     method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
     method public void onSilenceRinger();
-    method public final void requestBluetoothAudio(java.lang.String);
+    method public final void requestBluetoothAudio(android.bluetooth.BluetoothDevice);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -40209,6 +40195,8 @@
 
   public abstract class CellIdentity implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.CharSequence getOperatorAlphaLong();
+    method public java.lang.CharSequence getOperatorAlphaShort();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentity> CREATOR;
   }
@@ -40218,8 +40206,6 @@
     method public int getLatitude();
     method public int getLongitude();
     method public int getNetworkId();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getSystemId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityCdma> CREATOR;
@@ -40235,8 +40221,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public deprecated int getPsc();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityGsm> CREATOR;
@@ -40251,8 +40235,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getPci();
     method public int getTac();
     method public void writeToParcel(android.os.Parcel, int);
@@ -40277,8 +40259,6 @@
     method public deprecated int getMnc();
     method public java.lang.String getMncString();
     method public java.lang.String getMobileNetworkOperator();
-    method public java.lang.CharSequence getOperatorAlphaLong();
-    method public java.lang.CharSequence getOperatorAlphaShort();
     method public int getPsc();
     method public int getUarfcn();
     method public void writeToParcel(android.os.Parcel, int);
@@ -40408,19 +40388,21 @@
   }
 
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
-    method public int cancelDownload(android.telephony.mbms.DownloadRequest);
+    method public void addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener);
+    method public void addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener);
+    method public void cancelDownload(android.telephony.mbms.DownloadRequest);
     method public void close();
     method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, android.telephony.mbms.MbmsDownloadSessionCallback);
     method public static android.telephony.MbmsDownloadSession create(android.content.Context, java.util.concurrent.Executor, int, android.telephony.mbms.MbmsDownloadSessionCallback);
-    method public int download(android.telephony.mbms.DownloadRequest);
+    method public void download(android.telephony.mbms.DownloadRequest);
     method public java.io.File getTempFileRootDirectory();
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
-    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStateCallback);
+    method public void removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener);
+    method public void removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener);
     method public void requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
     method public void requestUpdateFileServices(java.util.List<java.lang.String>);
     method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
     method public void setTempFileRootDirectory(java.io.File);
-    method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
     field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
     field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
     field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
@@ -40607,17 +40589,17 @@
     ctor public ServiceState(android.os.Parcel);
     method protected void copyFrom(android.telephony.ServiceState);
     method public int describeContents();
+    method public int getCdmaNetworkId();
+    method public int getCdmaSystemId();
     method public int[] getCellBandwidths();
     method public int getChannelNumber();
     method public int getDuplexMode();
     method public boolean getIsManualSelection();
-    method public int getNetworkId();
     method public java.lang.String getOperatorAlphaLong();
     method public java.lang.String getOperatorAlphaShort();
     method public java.lang.String getOperatorNumeric();
     method public boolean getRoaming();
     method public int getState();
-    method public int getSystemId();
     method public void setIsManualSelection(boolean);
     method public void setOperatorName(java.lang.String, java.lang.String, java.lang.String);
     method public void setRoaming(boolean);
@@ -40831,8 +40813,6 @@
     method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
     method public android.telephony.TelephonyManager createForSubscriptionId(int);
     method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
-    method public int getAndroidCarrierIdForSubscription();
-    method public java.lang.CharSequence getAndroidCarrierNameForSubscription();
     method public int getCallState();
     method public android.os.PersistableBundle getCarrierConfig();
     method public deprecated android.telephony.CellLocation getCellLocation();
@@ -40863,6 +40843,8 @@
     method public int getPhoneType();
     method public android.telephony.ServiceState getServiceState();
     method public android.telephony.SignalStrength getSignalStrength();
+    method public int getSimCarrierId();
+    method public java.lang.CharSequence getSimCarrierIdName();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
     method public java.lang.String getSimOperatorName();
@@ -40884,12 +40866,11 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataSupported();
-    method public deprecated boolean isDataEnabled();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
     method public deprecated boolean isTtyModeSupported();
-    method public boolean isUserMobileDataEnabled();
     method public boolean isVoiceCapable();
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
@@ -40900,13 +40881,12 @@
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
-    method public deprecated void setDataEnabled(boolean);
+    method public void setDataEnabled(boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public void setNetworkSelectionModeAutomatic();
     method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
-    method public void setUserMobileDataEnabled(boolean);
     method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -41077,21 +41057,21 @@
   public class ApnSetting implements android.os.Parcelable {
     method public int describeContents();
     method public java.lang.String getApnName();
+    method public int getApnTypeBitmask();
     method public int getAuthType();
     method public java.lang.String getEntryName();
     method public int getId();
-    method public int getMmsPort();
-    method public java.net.InetAddress getMmsProxy();
-    method public java.net.URL getMmsc();
-    method public java.lang.String getMvnoType();
+    method public java.net.InetAddress getMmsProxyAddress();
+    method public int getMmsProxyPort();
+    method public android.net.Uri getMmsc();
+    method public int getMvnoType();
     method public int getNetworkTypeBitmask();
     method public java.lang.String getOperatorNumeric();
     method public java.lang.String getPassword();
-    method public int getPort();
-    method public java.lang.String getProtocol();
-    method public java.net.InetAddress getProxy();
-    method public java.lang.String getRoamingProtocol();
-    method public java.util.List<java.lang.String> getTypes();
+    method public int getProtocol();
+    method public java.net.InetAddress getProxyAddress();
+    method public int getProxyPort();
+    method public int getRoamingProtocol();
     method public java.lang.String getUser();
     method public boolean isEnabled();
     method public void writeToParcel(android.os.Parcel, int);
@@ -41100,46 +41080,45 @@
     field public static final int AUTH_TYPE_PAP = 1; // 0x1
     field public static final int AUTH_TYPE_PAP_OR_CHAP = 3; // 0x3
     field public static final android.os.Parcelable.Creator<android.telephony.data.ApnSetting> CREATOR;
-    field public static final java.lang.String MVNO_TYPE_GID = "gid";
-    field public static final java.lang.String MVNO_TYPE_ICCID = "iccid";
-    field public static final java.lang.String MVNO_TYPE_IMSI = "imsi";
-    field public static final java.lang.String MVNO_TYPE_SPN = "spn";
-    field public static final java.lang.String PROTOCOL_IP = "IP";
-    field public static final java.lang.String PROTOCOL_IPV4V6 = "IPV4V6";
-    field public static final java.lang.String PROTOCOL_IPV6 = "IPV6";
-    field public static final java.lang.String PROTOCOL_PPP = "PPP";
-    field public static final java.lang.String TYPE_ALL = "*";
-    field public static final java.lang.String TYPE_CBS = "cbs";
-    field public static final java.lang.String TYPE_DEFAULT = "default";
-    field public static final java.lang.String TYPE_DUN = "dun";
-    field public static final java.lang.String TYPE_EMERGENCY = "emergency";
-    field public static final java.lang.String TYPE_FOTA = "fota";
-    field public static final java.lang.String TYPE_HIPRI = "hipri";
-    field public static final java.lang.String TYPE_IA = "ia";
-    field public static final java.lang.String TYPE_IMS = "ims";
-    field public static final java.lang.String TYPE_MMS = "mms";
-    field public static final java.lang.String TYPE_SUPL = "supl";
+    field public static final int MVNO_TYPE_GID = 2; // 0x2
+    field public static final int MVNO_TYPE_ICCID = 3; // 0x3
+    field public static final int MVNO_TYPE_IMSI = 1; // 0x1
+    field public static final int MVNO_TYPE_SPN = 0; // 0x0
+    field public static final int PROTOCOL_IP = 0; // 0x0
+    field public static final int PROTOCOL_IPV4V6 = 2; // 0x2
+    field public static final int PROTOCOL_IPV6 = 1; // 0x1
+    field public static final int PROTOCOL_PPP = 3; // 0x3
+    field public static final int TYPE_CBS = 128; // 0x80
+    field public static final int TYPE_DEFAULT = 17; // 0x11
+    field public static final int TYPE_DUN = 8; // 0x8
+    field public static final int TYPE_EMERGENCY = 512; // 0x200
+    field public static final int TYPE_FOTA = 32; // 0x20
+    field public static final int TYPE_HIPRI = 16; // 0x10
+    field public static final int TYPE_IA = 256; // 0x100
+    field public static final int TYPE_IMS = 64; // 0x40
+    field public static final int TYPE_MMS = 2; // 0x2
+    field public static final int TYPE_SUPL = 4; // 0x4
   }
 
   public static class ApnSetting.Builder {
     ctor public ApnSetting.Builder();
     method public android.telephony.data.ApnSetting build();
     method public android.telephony.data.ApnSetting.Builder setApnName(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setApnTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setAuthType(int);
     method public android.telephony.data.ApnSetting.Builder setCarrierEnabled(boolean);
     method public android.telephony.data.ApnSetting.Builder setEntryName(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setMmsPort(int);
-    method public android.telephony.data.ApnSetting.Builder setMmsProxy(java.net.InetAddress);
-    method public android.telephony.data.ApnSetting.Builder setMmsc(java.net.URL);
-    method public android.telephony.data.ApnSetting.Builder setMvnoType(java.lang.String);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setMmsProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setMmsc(android.net.Uri);
+    method public android.telephony.data.ApnSetting.Builder setMvnoType(int);
     method public android.telephony.data.ApnSetting.Builder setNetworkTypeBitmask(int);
     method public android.telephony.data.ApnSetting.Builder setOperatorNumeric(java.lang.String);
     method public android.telephony.data.ApnSetting.Builder setPassword(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setPort(int);
-    method public android.telephony.data.ApnSetting.Builder setProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setProxy(java.net.InetAddress);
-    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(java.lang.String);
-    method public android.telephony.data.ApnSetting.Builder setTypes(java.util.List<java.lang.String>);
+    method public android.telephony.data.ApnSetting.Builder setProtocol(int);
+    method public android.telephony.data.ApnSetting.Builder setProxyAddress(java.net.InetAddress);
+    method public android.telephony.data.ApnSetting.Builder setProxyPort(int);
+    method public android.telephony.data.ApnSetting.Builder setRoamingProtocol(int);
     method public android.telephony.data.ApnSetting.Builder setUser(java.lang.String);
   }
 
@@ -41178,6 +41157,7 @@
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
     field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+    field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
     field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
   }
 
@@ -41275,6 +41255,11 @@
 
 package android.telephony.mbms {
 
+  public class DownloadProgressListener {
+    ctor public DownloadProgressListener();
+    method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
+  }
+
   public final class DownloadRequest implements android.os.Parcelable {
     method public int describeContents();
     method public android.net.Uri getDestinationUri();
@@ -41298,15 +41283,9 @@
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
-  public class DownloadStateCallback {
-    ctor public DownloadStateCallback();
-    ctor public DownloadStateCallback(int);
-    method public final boolean isFilterFlagSet(int);
-    method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
-    method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
-    field public static final int ALL_UPDATES = 0; // 0x0
-    field public static final int PROGRESS_UPDATES = 1; // 0x1
-    field public static final int STATE_UPDATES = 2; // 0x2
+  public class DownloadStatusListener {
+    ctor public DownloadStatusListener();
+    method public void onStatusUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
   }
 
   public final class FileInfo implements android.os.Parcelable {
@@ -41341,6 +41320,7 @@
     field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
     field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
     field public static final int SUCCESS = 0; // 0x0
+    field public static final int UNKNOWN = -1; // 0xffffffff
   }
 
   public static class MbmsErrors.DownloadErrors {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2f1b140..3fc10f7 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2611,8 +2611,10 @@
   }
 
   public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+    method public void addAddress(android.net.LinkAddress) throws java.io.IOException;
     method public void close();
     method public java.lang.String getInterfaceName();
+    method public void removeAddress(android.net.LinkAddress) throws java.io.IOException;
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -4293,6 +4295,7 @@
     method public java.lang.String getCdmaMin(int);
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
+    method public int getDataActivationState();
     method public deprecated boolean getDataEnabled();
     method public deprecated boolean getDataEnabled(int);
     method public int getSimApplicationState();
@@ -4300,6 +4303,7 @@
     method public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
     method public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
     method public android.os.Bundle getVisualVoicemailSettings();
+    method public int getVoiceActivationState();
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
     method public boolean isDataConnectivityPossible();
@@ -4311,10 +4315,12 @@
     method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
     method public boolean needsOtaServiceProvisioning();
     method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
+    method public void setDataActivationState(int);
     method public deprecated void setDataEnabled(int, boolean);
     method public boolean setRadio(boolean);
     method public boolean setRadioPower(boolean);
     method public deprecated void setVisualVoicemailEnabled(android.telecom.PhoneAccountHandle, boolean);
+    method public void setVoiceActivationState(int);
     method public deprecated void silenceRinger();
     method public boolean supplyPin(java.lang.String);
     method public int[] supplyPinReportResult(java.lang.String);
@@ -4333,6 +4339,11 @@
     field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
     field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
     field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
+    field public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; // 0x2
+    field public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; // 0x1
+    field public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; // 0x3
+    field public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; // 0x4
+    field public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; // 0x0
     field public static final int SIM_STATE_LOADED = 10; // 0xa
     field public static final int SIM_STATE_PRESENT = 11; // 0xb
   }
@@ -4348,12 +4359,13 @@
   }
 
   public class UiccSlotInfo implements android.os.Parcelable {
-    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int);
+    ctor public UiccSlotInfo(boolean, boolean, java.lang.String, int, int, boolean);
     method public int describeContents();
     method public java.lang.String getCardId();
     method public int getCardStateInfo();
     method public boolean getIsActive();
     method public boolean getIsEuicc();
+    method public boolean getIsExtendedApduSupported();
     method public int getLogicalSlotIdx();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1
@@ -4521,6 +4533,7 @@
 
   public class EuiccManager {
     method public void continueOperation(android.content.Intent, android.os.Bundle);
+    method public void eraseSubscriptions(android.app.PendingIntent);
     method public void getDefaultDownloadableSubscriptionList(android.app.PendingIntent);
     method public void getDownloadableSubscriptionMetadata(android.telephony.euicc.DownloadableSubscription, android.app.PendingIntent);
     method public int getOtaStatus();
@@ -4531,7 +4544,6 @@
     field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
     field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
     field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
-    field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
     field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
   }
 
@@ -5209,19 +5221,24 @@
   }
 
   public final class ImsFeatureConfiguration implements android.os.Parcelable {
-    ctor public ImsFeatureConfiguration();
     method public int describeContents();
-    method public int[] getServiceFeatures();
+    method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR;
   }
 
   public static class ImsFeatureConfiguration.Builder {
     ctor public ImsFeatureConfiguration.Builder();
-    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int);
+    method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int);
     method public android.telephony.ims.stub.ImsFeatureConfiguration build();
   }
 
+  public static final class ImsFeatureConfiguration.FeatureSlotPair {
+    ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int);
+    field public final int featureType;
+    field public final int slotId;
+  }
+
   public class ImsMultiEndpointImplBase {
     ctor public ImsMultiEndpointImplBase();
     method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>);
@@ -5329,18 +5346,20 @@
 
   public class MbmsDownloadServiceBase extends android.os.Binder {
     ctor public MbmsDownloadServiceBase();
+    method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public void dispose(int) throws android.os.RemoteException;
     method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
-    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+    method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
     method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
     method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
-    method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/api/test-current.txt b/api/test-current.txt
index bf3d0c2..ce6810f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -436,6 +436,10 @@
     field public static final java.lang.String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override";
   }
 
+  public class ServiceState implements android.os.Parcelable {
+    method public void setCdmaSystemAndNetworkId(int, int);
+  }
+
 }
 
 package android.telephony.mbms {
@@ -466,18 +470,20 @@
 
   public class MbmsDownloadServiceBase extends android.os.Binder {
     ctor public MbmsDownloadServiceBase();
+    method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public void dispose(int) throws android.os.RemoteException;
     method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
     method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
     method public void onAppCallbackDied(int, int);
-    method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+    method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException;
+    method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException;
     method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
     method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
     method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
     method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
-    method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
   }
 
   public class MbmsStreamingServiceBase extends android.os.Binder {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 1671337..12083b6 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -13,6 +13,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <android-base/macros.h>
 #include <binder/IPCThreadState.h>
 #include <hwbinder/IPCThreadState.h>
 #include <utils/Log.h>
@@ -137,27 +138,12 @@
 }
 
 static void maybeCreateDalvikCache() {
-#if defined(__aarch64__)
-    static const char kInstructionSet[] = "arm64";
-#elif defined(__x86_64__)
-    static const char kInstructionSet[] = "x86_64";
-#elif defined(__arm__)
-    static const char kInstructionSet[] = "arm";
-#elif defined(__i386__)
-    static const char kInstructionSet[] = "x86";
-#elif defined (__mips__) && !defined(__LP64__)
-    static const char kInstructionSet[] = "mips";
-#elif defined (__mips__) && defined(__LP64__)
-    static const char kInstructionSet[] = "mips64";
-#else
-#error "Unknown instruction set"
-#endif
     const char* androidRoot = getenv("ANDROID_DATA");
     LOG_ALWAYS_FATAL_IF(androidRoot == NULL, "ANDROID_DATA environment variable unset");
 
     char dalvikCacheDir[PATH_MAX];
     const int numChars = snprintf(dalvikCacheDir, PATH_MAX,
-            "%s/dalvik-cache/%s", androidRoot, kInstructionSet);
+            "%s/dalvik-cache/" ABI_STRING, androidRoot);
     LOG_ALWAYS_FATAL_IF((numChars >= PATH_MAX || numChars < 0),
             "Error constructing dalvik cache : %s", strerror(errno));
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 565eaeb..bae2a26 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -612,7 +612,7 @@
                         streamingOutput);
                 profiling = true;
             } catch (RuntimeException e) {
-                Slog.w(TAG, "Profiling failed on path " + profileFile);
+                Slog.w(TAG, "Profiling failed on path " + profileFile, e);
                 try {
                     profileFd.close();
                     profileFd = null;
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index be4e207..6aabe18 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -676,6 +676,10 @@
         if (!getLeAccess()) {
             return null;
         }
+        if (!isMultipleAdvertisementSupported()) {
+            Log.e(TAG, "Bluetooth LE advertising not supported");
+            return null;
+        }
         synchronized (mLock) {
             if (sBluetoothLeAdvertiser == null) {
                 sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
index 647e0d0..8f8083e 100644
--- a/core/java/android/bluetooth/BluetoothHearingAid.java
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -379,6 +380,76 @@
     }
 
     /**
+     * Select a connected device as active.
+     *
+     * The active device selection is per profile. An active device's
+     * purpose is profile-specific. For example, Hearing Aid audio
+     * streaming is to the active Hearing Aid device. If a remote device
+     * is not connected, it cannot be selected as active.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is not connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that the
+     * {@link #ACTION_ACTIVE_DEVICE_CHANGED} intent will be broadcasted
+     * with the active device.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device the remote Bluetooth device. Could be null to clear
+     * the active device and stop streaming audio to a Bluetooth device.
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean setActiveDevice(@Nullable BluetoothDevice device) {
+        if (DBG) log("setActiveDevice(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                mService.setActiveDevice(device);
+                return true;
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Check whether the device is active.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * permission.
+     *
+     * @return the connected device that is active or null if no device
+     * is active
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isActiveDevice(@Nullable BluetoothDevice device) {
+        if (VDBG) log("isActiveDevice()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && ((device == null) || isValidDevice(device))) {
+                return mService.isActiveDevice(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
      * Set priority of the profile
      *
      * <p> The device should already be paired.
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 2fab305..e017c42 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -27,8 +27,8 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Provides the public APIs to control the Bluetooth HID Device profile.
@@ -37,7 +37,6 @@
  * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
  */
 public final class BluetoothHidDevice implements BluetoothProfile {
-
     private static final String TAG = BluetoothHidDevice.class.getSimpleName();
 
     /**
@@ -62,106 +61,327 @@
             "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
 
     /**
-     * Constants representing device subclass.
+     * Constant representing unspecified HID device subclass.
      *
      * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     *     BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
      */
     public static final byte SUBCLASS1_NONE = (byte) 0x00;
+    /**
+     * Constant representing keyboard subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_KEYBOARD = (byte) 0x40;
+    /**
+     * Constant representing mouse subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_MOUSE = (byte) 0x80;
+    /**
+     * Constant representing combo keyboard and mouse subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS1_COMBO = (byte) 0xC0;
 
+    /**
+     * Constant representing uncategorized HID device subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_UNCATEGORIZED = (byte) 0x00;
+    /**
+     * Constant representing joystick subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_JOYSTICK = (byte) 0x01;
+    /**
+     * Constant representing gamepad subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02;
+    /**
+     * Constant representing remote control subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03;
+    /**
+     * Constant representing sensing device subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04;
+    /**
+     * Constant representing digitizer tablet subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05;
+    /**
+     * Constant representing card reader subclass.
+     *
+     * @see #registerApp (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
+     *     BluetoothHidDeviceAppQosSettings, Executor, Callback)
+     */
     public static final byte SUBCLASS2_CARD_READER = (byte) 0x06;
 
     /**
-     * Constants representing report types.
+     * Constant representing HID Input Report type.
      *
-     * @see BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)
-     * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
-     * @see BluetoothHidDeviceCallback#onInterruptData(BluetoothDevice, byte, byte[])
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
      */
     public static final byte REPORT_TYPE_INPUT = (byte) 1;
+    /**
+     * Constant representing HID Output Report type.
+     *
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
+     */
     public static final byte REPORT_TYPE_OUTPUT = (byte) 2;
+    /**
+     * Constant representing HID Feature Report type.
+     *
+     * @see Callback#onGetReport(BluetoothDevice, byte, byte, int)
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onInterruptData(BluetoothDevice, byte, byte[])
+     */
     public static final byte REPORT_TYPE_FEATURE = (byte) 3;
 
     /**
-     * Constants representing error response for Set Report.
+     * Constant representing success response for Set Report.
      *
-     * @see BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
      */
     public static final byte ERROR_RSP_SUCCESS = (byte) 0;
+    /**
+     * Constant representing error response for Set Report due to "not ready".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_NOT_READY = (byte) 1;
+    /**
+     * Constant representing error response for Set Report due to "invalid report ID".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_INVALID_RPT_ID = (byte) 2;
+    /**
+     * Constant representing error response for Set Report due to "unsupported request".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_UNSUPPORTED_REQ = (byte) 3;
+    /**
+     * Constant representing error response for Set Report due to "invalid parameter".
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_INVALID_PARAM = (byte) 4;
+    /**
+     * Constant representing error response for Set Report with unknown reason.
+     *
+     * @see Callback#onSetReport(BluetoothDevice, byte, byte, byte[])
+     */
     public static final byte ERROR_RSP_UNKNOWN = (byte) 14;
 
     /**
-     * Constants representing protocol mode used set by host. Default is always {@link
+     * Constant representing boot protocol mode used set by host. Default is always {@link
      * #PROTOCOL_REPORT_MODE} unless notified otherwise.
      *
-     * @see BluetoothHidDeviceCallback#onSetProtocol(BluetoothDevice, byte)
+     * @see Callback#onSetProtocol(BluetoothDevice, byte)
      */
     public static final byte PROTOCOL_BOOT_MODE = (byte) 0;
+    /**
+     * Constant representing report protocol mode used set by host. Default is always {@link
+     * #PROTOCOL_REPORT_MODE} unless notified otherwise.
+     *
+     * @see Callback#onSetProtocol(BluetoothDevice, byte)
+     */
     public static final byte PROTOCOL_REPORT_MODE = (byte) 1;
 
+    /**
+     * The template class that applications use to call callback functions on events from the HID
+     * host. Callback functions are wrapped in this class and registered to the Android system
+     * during app registration.
+     */
+    public abstract static class Callback {
+
+        private static final String TAG = "BluetoothHidDevCallback";
+
+        /**
+         * Callback called when application registration state changes. Usually it's called due to
+         * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
+         * Executor, Callback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
+         * unsolicited in case e.g. Bluetooth was turned off in which case application is
+         * unregistered automatically.
+         *
+         * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently
+         *     has Virtual Cable established with device. Only valid when application is registered,
+         *     can be <code>null</code>.
+         * @param registered <code>true</code> if application is registered, <code>false</code>
+         *     otherwise.
+         */
+        public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
+            Log.d(
+                    TAG,
+                    "onAppStatusChanged: pluggedDevice="
+                            + pluggedDevice
+                            + " registered="
+                            + registered);
+        }
+
+        /**
+         * Callback called when connection state with remote host was changed. Application can
+         * assume than Virtual Cable is established when called with {@link
+         * BluetoothProfile#STATE_CONNECTED} <code>state</code>.
+         *
+         * @param device {@link BluetoothDevice} object representing host device which connection
+         *     state was changed.
+         * @param state Connection state as defined in {@link BluetoothProfile}.
+         */
+        public void onConnectionStateChanged(BluetoothDevice device, int state) {
+            Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
+        }
+
+        /**
+         * Callback called when GET_REPORT is received from remote host. Should be replied by
+         * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
+         * byte[])}.
+         *
+         * @param type Requested Report Type.
+         * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
+         * @param bufferSize Requested buffer size, application shall respond with at least given
+         *     number of bytes.
+         */
+        public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
+            Log.d(
+                    TAG,
+                    "onGetReport: device="
+                            + device
+                            + " type="
+                            + type
+                            + " id="
+                            + id
+                            + " bufferSize="
+                            + bufferSize);
+        }
+
+        /**
+         * Callback called when SET_REPORT is received from remote host. In case received data are
+         * invalid, application shall respond with {@link
+         * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
+         *
+         * @param type Report Type.
+         * @param id Report Id.
+         * @param data Report data.
+         */
+        public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
+            Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
+        }
+
+        /**
+         * Callback called when SET_PROTOCOL is received from remote host. Application shall use
+         * this information to send only reports valid for given protocol mode. By default, {@link
+         * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
+         *
+         * @param protocol Protocol Mode.
+         */
+        public void onSetProtocol(BluetoothDevice device, byte protocol) {
+            Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
+        }
+
+        /**
+         * Callback called when report data is received over interrupt channel. Report Type is
+         * assumed to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
+         *
+         * @param reportId Report Id.
+         * @param data Report data.
+         */
+        public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
+            Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
+        }
+
+        /**
+         * Callback called when Virtual Cable is removed. After this callback is received connection
+         * will be disconnected automatically.
+         */
+        public void onVirtualCableUnplug(BluetoothDevice device) {
+            Log.d(TAG, "onVirtualCableUnplug: device=" + device);
+        }
+    }
+
     private Context mContext;
-
     private ServiceListener mServiceListener;
-
     private volatile IBluetoothHidDevice mService;
-
     private BluetoothAdapter mAdapter;
 
-    private static class BluetoothHidDeviceCallbackWrapper
-            extends IBluetoothHidDeviceCallback.Stub {
+    private static class CallbackWrapper extends IBluetoothHidDeviceCallback.Stub {
 
-        private BluetoothHidDeviceCallback mCallback;
+        private final Executor mExecutor;
+        private final Callback mCallback;
 
-        public BluetoothHidDeviceCallbackWrapper(BluetoothHidDeviceCallback callback) {
+        CallbackWrapper(Executor executor, Callback callback) {
+            mExecutor = executor;
             mCallback = callback;
         }
 
         @Override
         public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-            mCallback.onAppStatusChanged(pluggedDevice, registered);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onAppStatusChanged(pluggedDevice, registered));
         }
 
         @Override
         public void onConnectionStateChanged(BluetoothDevice device, int state) {
-            mCallback.onConnectionStateChanged(device, state);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onConnectionStateChanged(device, state));
         }
 
         @Override
         public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-            mCallback.onGetReport(device, type, id, bufferSize);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onGetReport(device, type, id, bufferSize));
         }
 
         @Override
         public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-            mCallback.onSetReport(device, type, id, data);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onSetReport(device, type, id, data));
         }
 
         @Override
         public void onSetProtocol(BluetoothDevice device, byte protocol) {
-            mCallback.onSetProtocol(device, protocol);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onSetProtocol(device, protocol));
         }
 
         @Override
         public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-            mCallback.onInterruptData(device, reportId, data);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onInterruptData(device, reportId, data));
         }
 
         @Override
         public void onVirtualCableUnplug(BluetoothDevice device) {
-            mCallback.onVirtualCableUnplug(device);
+            clearCallingIdentity();
+            mExecutor.execute(() -> mCallback.onVirtualCableUnplug(device));
         }
     }
 
@@ -213,8 +433,6 @@
             };
 
     BluetoothHidDevice(Context context, ServiceListener listener) {
-        Log.v(TAG, "BluetoothHidDevice");
-
         mContext = context;
         mServiceListener = listener;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
@@ -245,7 +463,6 @@
     }
 
     void doUnbind() {
-        Log.d(TAG, "Unbinding HidDevService");
         if (mService != null) {
             mService = null;
             try {
@@ -257,8 +474,6 @@
     }
 
     void close() {
-        Log.v(TAG, "close()");
-
         IBluetoothManager mgr = mAdapter.getBluetoothManager();
         if (mgr != null) {
             try {
@@ -277,8 +492,6 @@
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        Log.v(TAG, "getConnectedDevices()");
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -290,14 +503,12 @@
             Log.w(TAG, "Proxy not attached to service");
         }
 
-        return new ArrayList<BluetoothDevice>();
+        return new ArrayList<>();
     }
 
     /** {@inheritDoc} */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -309,14 +520,12 @@
             Log.w(TAG, "Proxy not attached to service");
         }
 
-        return new ArrayList<BluetoothDevice>();
+        return new ArrayList<>();
     }
 
     /** {@inheritDoc} */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        Log.v(TAG, "getConnectionState(): device=" + device);
-
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
@@ -336,9 +545,9 @@
      * when application is registered. Only one application can be registered at one time. When an
      * application is registered, the HID Host service will be disabled until it is unregistered.
      * When no longer used, application should be unregistered using {@link #unregisterApp()}. The
-     * registration status should be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
-     * the return value of this method.
+     * app will be automatically unregistered if it is not foreground. The registration status
+     * should be tracked by the application by handling callback from Callback#onAppStatusChanged.
+     * The app registration status is not related to the return value of this method.
      *
      * @param sdp {@link BluetoothHidDeviceAppSdpSettings} object of HID Device SDP record. The HID
      *     Device SDP record is required.
@@ -348,27 +557,36 @@
      * @param outQos {@link BluetoothHidDeviceAppQosSettings} object of Outgoing QoS Settings. The
      *     Outgoing QoS Settings is not required. Use null or default
      *     BluetoothHidDeviceAppQosSettings.Builder for default values.
-     * @param callback {@link BluetoothHidDeviceCallback} object to which callback messages will be
-     *     sent. The BluetoothHidDeviceCallback object is required.
+     * @param executor {@link Executor} object on which callback will be executed. The Executor
+     *     object is required.
+     * @param callback {@link Callback} object to which callback messages will be sent. The Callback
+     *     object is required.
      * @return true if the command is successfully sent; otherwise false.
      */
-    public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
-            BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
-            BluetoothHidDeviceCallback callback) {
-        Log.v(TAG, "registerApp(): sdp=" + sdp + " inQos=" + inQos + " outQos=" + outQos
-                        + " callback=" + callback);
-
+    public boolean registerApp(
+            BluetoothHidDeviceAppSdpSettings sdp,
+            BluetoothHidDeviceAppQosSettings inQos,
+            BluetoothHidDeviceAppQosSettings outQos,
+            Executor executor,
+            Callback callback) {
         boolean result = false;
 
-        if (sdp == null || callback == null) {
-            return false;
+        if (sdp == null) {
+            throw new IllegalArgumentException("sdp parameter cannot be null");
+        }
+
+        if (executor == null) {
+            throw new IllegalArgumentException("executor parameter cannot be null");
+        }
+
+        if (callback == null) {
+            throw new IllegalArgumentException("callback parameter cannot be null");
         }
 
         final IBluetoothHidDevice service = mService;
         if (service != null) {
             try {
-                BluetoothHidDeviceCallbackWrapper cbw =
-                        new BluetoothHidDeviceCallbackWrapper(callback);
+                CallbackWrapper cbw = new CallbackWrapper(executor, callback);
                 result = service.registerApp(sdp, inQos, outQos, cbw);
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
@@ -384,16 +602,13 @@
      * Unregisters application. Active connection will be disconnected and no new connections will
      * be allowed until registered again using {@link #registerApp
      * (BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceAppQosSettings,
-     * BluetoothHidDeviceAppQosSettings, BluetoothHidDeviceCallback)} The registration status should
-     * be tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onAppStatusChanged. The app registration status is not related to
-     * the return value of this method.
+     * BluetoothHidDeviceAppQosSettings, Executor, Callback)}. The registration status should be
+     * tracked by the application by handling callback from Callback#onAppStatusChanged. The app
+     * registration status is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean unregisterApp() {
-        Log.v(TAG, "unregisterApp()");
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -437,7 +652,7 @@
 
     /**
      * Sends report to remote host as reply for GET_REPORT request from {@link
-     * BluetoothHidDeviceCallback#onGetReport(BluetoothDevice, byte, byte, int)}.
+     * Callback#onGetReport(BluetoothDevice, byte, byte, int)}.
      *
      * @param type Report Type, as in request.
      * @param id Report Id, as in request.
@@ -445,8 +660,6 @@
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        Log.v(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -465,14 +678,12 @@
 
     /**
      * Sends error handshake message as reply for invalid SET_REPORT request from {@link
-     * BluetoothHidDeviceCallback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
+     * Callback#onSetReport(BluetoothDevice, byte, byte, byte[])}.
      *
      * @param error Error to be sent for SET_REPORT via HANDSHAKE.
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean reportError(BluetoothDevice device, byte error) {
-        Log.v(TAG, "reportError(): device=" + device + " error=" + error);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -490,42 +701,14 @@
     }
 
     /**
-     * Sends Virtual Cable Unplug to currently connected host.
-     *
-     * @return
-     * {@hide}
-     */
-    public boolean unplug(BluetoothDevice device) {
-        Log.v(TAG, "unplug(): device=" + device);
-
-        boolean result = false;
-
-        final IBluetoothHidDevice service = mService;
-        if (service != null) {
-            try {
-                result = service.unplug(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-
-        return result;
-    }
-
-    /**
      * Initiates connection to host which is currently paired with this device. If the application
      * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
-     * tracked by the application by handling callback from
-     * BluetoothHidDeviceCallback#onConnectionStateChanged. The connection state is not related to
-     * the return value of this method.
+     * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
+     * connection state is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean connect(BluetoothDevice device) {
-        Log.v(TAG, "connect(): device=" + device);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
@@ -544,14 +727,12 @@
 
     /**
      * Disconnects from currently connected host. The connection state should be tracked by the
-     * application by handling callback from BluetoothHidDeviceCallback#onConnectionStateChanged.
-     * The connection state is not related to the return value of this method.
+     * application by handling callback from Callback#onConnectionStateChanged. The connection state
+     * is not related to the return value of this method.
      *
      * @return true if the command is successfully sent; otherwise false.
      */
     public boolean disconnect(BluetoothDevice device) {
-        Log.v(TAG, "disconnect(): device=" + device);
-
         boolean result = false;
 
         final IBluetoothHidDevice service = mService;
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index c05df2d..a485b89 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -29,12 +29,12 @@
  */
 public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
 
-    public final int serviceType;
-    public final int tokenRate;
-    public final int tokenBucketSize;
-    public final int peakBandwidth;
-    public final int latency;
-    public final int delayVariation;
+    private final int mServiceType;
+    private final int mTokenRate;
+    private final int mTokenBucketSize;
+    private final int mPeakBandwidth;
+    private final int mLatency;
+    private final int mDelayVariation;
 
     public static final int SERVICE_NO_TRAFFIC = 0x00;
     public static final int SERVICE_BEST_EFFORT = 0x01;
@@ -44,38 +44,53 @@
 
     /**
      * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
-     * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
-     * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters.
+     * Settings is optional. Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and
+     * Appendix D for parameters.
      *
-     * @param serviceType L2CAP service type
-     * @param tokenRate L2CAP token rate
-     * @param tokenBucketSize L2CAP token bucket size
-     * @param peakBandwidth L2CAP peak bandwidth
-     * @param latency L2CAP latency
-     * @param delayVariation L2CAP delay variation
+     * @param serviceType L2CAP service type, default = SERVICE_BEST_EFFORT
+     * @param tokenRate L2CAP token rate, default = 0
+     * @param tokenBucketSize L2CAP token bucket size, default = 0
+     * @param peakBandwidth L2CAP peak bandwidth, default = 0
+     * @param latency L2CAP latency, default = MAX
+     * @param delayVariation L2CAP delay variation, default = MAX
      */
-    public BluetoothHidDeviceAppQosSettings(int serviceType, int tokenRate, int tokenBucketSize,
-            int peakBandwidth, int latency, int delayVariation) {
-        this.serviceType = serviceType;
-        this.tokenRate = tokenRate;
-        this.tokenBucketSize = tokenBucketSize;
-        this.peakBandwidth = peakBandwidth;
-        this.latency = latency;
-        this.delayVariation = delayVariation;
+    public BluetoothHidDeviceAppQosSettings(
+            int serviceType,
+            int tokenRate,
+            int tokenBucketSize,
+            int peakBandwidth,
+            int latency,
+            int delayVariation) {
+        mServiceType = serviceType;
+        mTokenRate = tokenRate;
+        mTokenBucketSize = tokenBucketSize;
+        mPeakBandwidth = peakBandwidth;
+        mLatency = latency;
+        mDelayVariation = delayVariation;
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHidDeviceAppQosSettings) {
-            BluetoothHidDeviceAppQosSettings qos = (BluetoothHidDeviceAppQosSettings) o;
-            return this.serviceType == qos.serviceType
-                    && this.tokenRate == qos.tokenRate
-                    && this.tokenBucketSize == qos.tokenBucketSize
-                    && this.peakBandwidth == qos.peakBandwidth
-                    && this.latency == qos.latency
-                    && this.delayVariation == qos.delayVariation;
-        }
-        return false;
+    public int getServiceType() {
+        return mServiceType;
+    }
+
+    public int getTokenRate() {
+        return mTokenRate;
+    }
+
+    public int getTokenBucketSize() {
+        return mTokenBucketSize;
+    }
+
+    public int getPeakBandwidth() {
+        return mPeakBandwidth;
+    }
+
+    public int getLatency() {
+        return mLatency;
+    }
+
+    public int getDelayVariation() {
+        return mDelayVariation;
     }
 
     @Override
@@ -106,104 +121,11 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(serviceType);
-        out.writeInt(tokenRate);
-        out.writeInt(tokenBucketSize);
-        out.writeInt(peakBandwidth);
-        out.writeInt(latency);
-        out.writeInt(delayVariation);
-    }
-
-    /** @return an int array representation of this instance */
-    public int[] toArray() {
-        return new int[] {
-            serviceType, tokenRate, tokenBucketSize, peakBandwidth, latency, delayVariation
-        };
-    }
-
-    /** A helper to build the BluetoothHidDeviceAppQosSettings object. */
-    public static class Builder {
-        // Optional parameters - initialized to default values
-        private int mServiceType = SERVICE_BEST_EFFORT;
-        private int mTokenRate = 0;
-        private int mTokenBucketSize = 0;
-        private int mPeakBandwidth = 0;
-        private int mLatency = MAX;
-        private int mDelayVariation = MAX;
-
-        /**
-         * Set the service type.
-         *
-         * @param val service type. Should be one of {SERVICE_NO_TRAFFIC, SERVICE_BEST_EFFORT,
-         *     SERVICE_GUARANTEED}, with SERVICE_BEST_EFFORT being the default one.
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified service type.
-         */
-        public Builder serviceType(int val) {
-            mServiceType = val;
-            return this;
-        }
-        /**
-         * Set the token rate.
-         *
-         * @param val token rate
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified token rate.
-         */
-        public Builder tokenRate(int val) {
-            mTokenRate = val;
-            return this;
-        }
-
-        /**
-         * Set the bucket size.
-         *
-         * @param val bucket size
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified bucket size.
-         */
-        public Builder tokenBucketSize(int val) {
-            mTokenBucketSize = val;
-            return this;
-        }
-
-        /**
-         * Set the peak bandwidth.
-         *
-         * @param val peak bandwidth
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified peak bandwidth.
-         */
-        public Builder peakBandwidth(int val) {
-            mPeakBandwidth = val;
-            return this;
-        }
-        /**
-         * Set the latency.
-         *
-         * @param val latency
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified latency.
-         */
-        public Builder latency(int val) {
-            mLatency = val;
-            return this;
-        }
-
-        /**
-         * Set the delay variation.
-         *
-         * @param val delay variation
-         * @return BluetoothHidDeviceAppQosSettings Builder with specified delay variation.
-         */
-        public Builder delayVariation(int val) {
-            mDelayVariation = val;
-            return this;
-        }
-
-        /**
-         * Build the BluetoothHidDeviceAppQosSettings object.
-         *
-         * @return BluetoothHidDeviceAppQosSettings object with current settings.
-         */
-        public BluetoothHidDeviceAppQosSettings build() {
-            return new BluetoothHidDeviceAppQosSettings(mServiceType, mTokenRate, mTokenBucketSize,
-                    mPeakBandwidth, mLatency, mDelayVariation);
-        }
+        out.writeInt(mServiceType);
+        out.writeInt(mTokenRate);
+        out.writeInt(mTokenBucketSize);
+        out.writeInt(mPeakBandwidth);
+        out.writeInt(mLatency);
+        out.writeInt(mDelayVariation);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 562c559..237082e 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -19,7 +19,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
 
 /**
  * Represents the Service Discovery Protocol (SDP) settings for a Bluetooth HID Device application.
@@ -31,11 +30,11 @@
  */
 public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
 
-    public final String name;
-    public final String description;
-    public final String provider;
-    public final byte subclass;
-    public final byte[] descriptors;
+    private final String mName;
+    private final String mDescription;
+    private final String mProvider;
+    private final byte mSubclass;
+    private final byte[] mDescriptors;
 
     /**
      * Create a BluetoothHidDeviceAppSdpSettings object for the Bluetooth SDP record.
@@ -52,24 +51,31 @@
      */
     public BluetoothHidDeviceAppSdpSettings(
             String name, String description, String provider, byte subclass, byte[] descriptors) {
-        this.name = name;
-        this.description = description;
-        this.provider = provider;
-        this.subclass = subclass;
-        this.descriptors = descriptors.clone();
+        mName = name;
+        mDescription = description;
+        mProvider = provider;
+        mSubclass = subclass;
+        mDescriptors = descriptors.clone();
     }
 
-    @Override
-    public boolean equals(Object o) {
-        if (o instanceof BluetoothHidDeviceAppSdpSettings) {
-            BluetoothHidDeviceAppSdpSettings sdp = (BluetoothHidDeviceAppSdpSettings) o;
-            return this.name.equals(sdp.name)
-                    && this.description.equals(sdp.description)
-                    && this.provider.equals(sdp.provider)
-                    && this.subclass == sdp.subclass
-                    && Arrays.equals(this.descriptors, sdp.descriptors);
-        }
-        return false;
+    public String getName() {
+        return mName;
+    }
+
+    public String getDescription() {
+        return mDescription;
+    }
+
+    public String getProvider() {
+        return mProvider;
+    }
+
+    public byte getSubclass() {
+        return mSubclass;
+    }
+
+    public byte[] getDescriptors() {
+        return mDescriptors;
     }
 
     @Override
@@ -99,10 +105,10 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        out.writeString(name);
-        out.writeString(description);
-        out.writeString(provider);
-        out.writeByte(subclass);
-        out.writeByteArray(descriptors);
+        out.writeString(mName);
+        out.writeString(mDescription);
+        out.writeString(mProvider);
+        out.writeByte(mSubclass);
+        out.writeByteArray(mDescriptors);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
deleted file mode 100644
index e71b00f..0000000
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2016 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.bluetooth;
-
-import android.util.Log;
-
-/**
- * The template class that applications use to call callback functions on events from the HID host.
- * Callback functions are wrapped in this class and registered to the Android system during app
- * registration.
- *
- * <p>{@see BluetoothHidDevice}
- */
-public abstract class BluetoothHidDeviceCallback {
-
-    private static final String TAG = "BluetoothHidDevCallback";
-
-    /**
-     * Callback called when application registration state changes. Usually it's called due to
-     * either {@link BluetoothHidDevice#registerApp (String, String, String, byte, byte[],
-     * BluetoothHidDeviceCallback)} or {@link BluetoothHidDevice#unregisterApp()} , but can be also
-     * unsolicited in case e.g. Bluetooth was turned off in which case application is unregistered
-     * automatically.
-     *
-     * @param pluggedDevice {@link BluetoothDevice} object which represents host that currently has
-     *     Virtual Cable established with device. Only valid when application is registered, can be
-     *     <code>null</code>.
-     * @param registered <code>true</code> if application is registered, <code>false</code>
-     *     otherwise.
-     */
-    public void onAppStatusChanged(BluetoothDevice pluggedDevice, boolean registered) {
-        Log.d(TAG,
-                "onAppStatusChanged: pluggedDevice=" + pluggedDevice + " registered=" + registered);
-    }
-
-    /**
-     * Callback called when connection state with remote host was changed. Application can assume
-     * than Virtual Cable is established when called with {@link BluetoothProfile#STATE_CONNECTED}
-     * <code>state</code>.
-     *
-     * @param device {@link BluetoothDevice} object representing host device which connection state
-     *     was changed.
-     * @param state Connection state as defined in {@link BluetoothProfile}.
-     */
-    public void onConnectionStateChanged(BluetoothDevice device, int state) {
-        Log.d(TAG, "onConnectionStateChanged: device=" + device + " state=" + state);
-    }
-
-    /**
-     * Callback called when GET_REPORT is received from remote host. Should be replied by
-     * application using {@link BluetoothHidDevice#replyReport(BluetoothDevice, byte, byte,
-     * byte[])}.
-     *
-     * @param type Requested Report Type.
-     * @param id Requested Report Id, can be 0 if no Report Id are defined in descriptor.
-     * @param bufferSize Requested buffer size, application shall respond with at least given number
-     *     of bytes.
-     */
-    public void onGetReport(BluetoothDevice device, byte type, byte id, int bufferSize) {
-        Log.d(TAG, "onGetReport: device=" + device + " type=" + type + " id=" + id + " bufferSize="
-                + bufferSize);
-    }
-
-    /**
-     * Callback called when SET_REPORT is received from remote host. In case received data are
-     * invalid, application shall respond with {@link
-     * BluetoothHidDevice#reportError(BluetoothDevice, byte)}.
-     *
-     * @param type Report Type.
-     * @param id Report Id.
-     * @param data Report data.
-     */
-    public void onSetReport(BluetoothDevice device, byte type, byte id, byte[] data) {
-        Log.d(TAG, "onSetReport: device=" + device + " type=" + type + " id=" + id);
-    }
-
-    /**
-     * Callback called when SET_PROTOCOL is received from remote host. Application shall use this
-     * information to send only reports valid for given protocol mode. By default, {@link
-     * BluetoothHidDevice#PROTOCOL_REPORT_MODE} shall be assumed.
-     *
-     * @param protocol Protocol Mode.
-     */
-    public void onSetProtocol(BluetoothDevice device, byte protocol) {
-        Log.d(TAG, "onSetProtocol: device=" + device + " protocol=" + protocol);
-    }
-
-    /**
-     * Callback called when report data is received over interrupt channel. Report Type is assumed
-     * to be {@link BluetoothHidDevice#REPORT_TYPE_OUTPUT}.
-     *
-     * @param reportId Report Id.
-     * @param data Report data.
-     */
-    public void onInterruptData(BluetoothDevice device, byte reportId, byte[] data) {
-        Log.d(TAG, "onInterruptData: device=" + device + " reportId=" + reportId);
-    }
-
-    /**
-     * Callback called when Virtual Cable is removed. After this callback is
-     * received connection will be disconnected automatically.
-     */
-    public void onVirtualCableUnplug(BluetoothDevice device) {
-        Log.d(TAG, "onVirtualCableUnplug: device=" + device);
-    }
-}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 656188f..6aeb94d 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -38,7 +38,7 @@
      * This extra represents the current connection state of the profile of the
      * Bluetooth device.
      */
-    public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+    String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
 
     /**
      * Extra for the connection state intents of the individual profiles.
@@ -46,123 +46,130 @@
      * This extra represents the previous connection state of the profile of the
      * Bluetooth device.
      */
-    public static final String EXTRA_PREVIOUS_STATE =
+    String EXTRA_PREVIOUS_STATE =
             "android.bluetooth.profile.extra.PREVIOUS_STATE";
 
     /** The profile is in disconnected state */
-    public static final int STATE_DISCONNECTED = 0;
+    int STATE_DISCONNECTED = 0;
     /** The profile is in connecting state */
-    public static final int STATE_CONNECTING = 1;
+    int STATE_CONNECTING = 1;
     /** The profile is in connected state */
-    public static final int STATE_CONNECTED = 2;
+    int STATE_CONNECTED = 2;
     /** The profile is in disconnecting state */
-    public static final int STATE_DISCONNECTING = 3;
+    int STATE_DISCONNECTING = 3;
 
     /**
      * Headset and Handsfree profile
      */
-    public static final int HEADSET = 1;
+    int HEADSET = 1;
 
     /**
      * A2DP profile.
      */
-    public static final int A2DP = 2;
+    int A2DP = 2;
 
     /**
      * Health Profile
      */
-    public static final int HEALTH = 3;
+    int HEALTH = 3;
 
     /**
      * HID Host
      *
      * @hide
      */
-    public static final int HID_HOST = 4;
+    int HID_HOST = 4;
 
     /**
      * PAN Profile
      *
      * @hide
      */
-    public static final int PAN = 5;
+    int PAN = 5;
 
     /**
      * PBAP
      *
      * @hide
      */
-    public static final int PBAP = 6;
+    int PBAP = 6;
 
     /**
      * GATT
      */
-    public static final int GATT = 7;
+    int GATT = 7;
 
     /**
      * GATT_SERVER
      */
-    public static final int GATT_SERVER = 8;
+    int GATT_SERVER = 8;
 
     /**
      * MAP Profile
      *
      * @hide
      */
-    public static final int MAP = 9;
+    int MAP = 9;
 
     /*
      * SAP Profile
      * @hide
      */
-    public static final int SAP = 10;
+    int SAP = 10;
 
     /**
      * A2DP Sink Profile
      *
      * @hide
      */
-    public static final int A2DP_SINK = 11;
+    int A2DP_SINK = 11;
 
     /**
      * AVRCP Controller Profile
      *
      * @hide
      */
-    public static final int AVRCP_CONTROLLER = 12;
+    int AVRCP_CONTROLLER = 12;
+
+    /**
+     * AVRCP Target Profile
+     *
+     * @hide
+     */
+    int AVRCP = 13;
 
     /**
      * Headset Client - HFP HF Role
      *
      * @hide
      */
-    public static final int HEADSET_CLIENT = 16;
+    int HEADSET_CLIENT = 16;
 
     /**
      * PBAP Client
      *
      * @hide
      */
-    public static final int PBAP_CLIENT = 17;
+    int PBAP_CLIENT = 17;
 
     /**
      * MAP Messaging Client Equipment (MCE)
      *
      * @hide
      */
-    public static final int MAP_CLIENT = 18;
+    int MAP_CLIENT = 18;
 
     /**
      * HID Device
      */
-    public static final int HID_DEVICE = 19;
+    int HID_DEVICE = 19;
 
     /**
      * Object Push Profile (OPP)
      *
      * @hide
      */
-    public static final int OPP = 20;
+    int OPP = 20;
 
     /**
      * Hearing Aid Device
@@ -185,7 +192,7 @@
      *
      * @hide
      **/
-    public static final int PRIORITY_AUTO_CONNECT = 1000;
+    int PRIORITY_AUTO_CONNECT = 1000;
 
     /**
      * Default priority for devices that allow incoming
@@ -194,7 +201,7 @@
      * @hide
      **/
     @SystemApi
-    public static final int PRIORITY_ON = 100;
+    int PRIORITY_ON = 100;
 
     /**
      * Default priority for devices that does not allow incoming
@@ -203,14 +210,14 @@
      * @hide
      **/
     @SystemApi
-    public static final int PRIORITY_OFF = 0;
+    int PRIORITY_OFF = 0;
 
     /**
      * Default priority when not set or when the device is unpaired
      *
      * @hide
      */
-    public static final int PRIORITY_UNDEFINED = -1;
+    int PRIORITY_UNDEFINED = -1;
 
     /**
      * Get connected devices for this specific profile.
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 8ea81a4..ebc88ff 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1046,6 +1046,58 @@
     /** @hide */
     public String[] splitClassLoaderNames;
 
+    /**
+     * Represents the default policy. The actual policy used will depend on other properties of
+     * the application, e.g. the target SDK version.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DEFAULT = -1;
+    /**
+     * No API enforcement; the app can access the entire internal private API. Only for use by
+     * system apps.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
+    /**
+     * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
+     * black lists.
+     * @hide
+     * */
+    public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+    /**
+     * Dark grey list enforcement. Enforces the dark grey and black lists
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK = 2;
+    /**
+     * Blacklist enforcement only.
+     * @hide
+     */
+    public static final int HIDDEN_API_ENFORCEMENT_BLACK = 3;
+
+    private static final int HIDDEN_API_ENFORCEMENT_MAX = HIDDEN_API_ENFORCEMENT_BLACK;
+
+    /**
+     * Values in this IntDef MUST be kept in sync with enum hiddenapi::EnforcementPolicy in
+     * art/runtime/hidden_api.h
+     * @hide
+     */
+    @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
+            HIDDEN_API_ENFORCEMENT_DEFAULT,
+            HIDDEN_API_ENFORCEMENT_NONE,
+            HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+            HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
+            HIDDEN_API_ENFORCEMENT_BLACK,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HiddenApiEnforcementPolicy {}
+
+    private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+        return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
+    }
+
+    private int mHiddenApiPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
@@ -1133,6 +1185,7 @@
             if (category != CATEGORY_UNDEFINED) {
                 pw.println(prefix + "category=" + category);
             }
+            pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy());
         }
         super.dumpBack(pw, prefix);
     }
@@ -1228,6 +1281,7 @@
         targetSandboxVersion = orig.targetSandboxVersion;
         classLoaderName = orig.classLoaderName;
         splitClassLoaderNames = orig.splitClassLoaderNames;
+        mHiddenApiPolicy = orig.mHiddenApiPolicy;
     }
 
     public String toString() {
@@ -1298,6 +1352,7 @@
         dest.writeInt(targetSandboxVersion);
         dest.writeString(classLoaderName);
         dest.writeStringArray(splitClassLoaderNames);
+        dest.writeInt(mHiddenApiPolicy);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1365,6 +1420,7 @@
         targetSandboxVersion = source.readInt();
         classLoaderName = source.readString();
         splitClassLoaderNames = source.readStringArray();
+        mHiddenApiPolicy = source.readInt();
     }
 
     /**
@@ -1456,14 +1512,31 @@
         }
     }
 
+    private boolean isPackageWhitelistedForHiddenApis() {
+        return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
+    }
+
     /**
      * @hide
      */
-    public boolean isAllowedToUseHiddenApi() {
-        boolean whitelisted =
-                SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
-        return isSystemApp() || // TODO get rid of this once the whitelist has been populated
-                (whitelisted && (isSystemApp() || isUpdatedSystemApp()));
+    public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
+        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+            return mHiddenApiPolicy;
+        }
+        if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+            return HIDDEN_API_ENFORCEMENT_NONE;
+        }
+        return HIDDEN_API_ENFORCEMENT_BLACK;
+    }
+
+    /**
+     * @hide
+     */
+    public void setHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
+        if (!isValidHiddenApiEnforcementPolicy(policy)) {
+            throw new IllegalArgumentException("Invalid API enforcement policy: " + policy);
+        }
+        mHiddenApiPolicy = policy;
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3a8a254..40f47b0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -453,133 +453,177 @@
     public static final int TYPE_NONE        = -1;
 
     /**
-     * The Mobile data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route)
+     * A Mobile data connection. Devices may support more than one.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_MOBILE      = 0;
+
     /**
-     * The WIFI data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A WIFI data connection. Devices may support more than one.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_WIFI        = 1;
+
     /**
      * An MMS-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is used by applications needing to talk to the carrier's
      * Multimedia Messaging Service servers.
      *
-     * @deprecated Applications should instead use
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
      */
     @Deprecated
     public static final int TYPE_MOBILE_MMS  = 2;
+
     /**
      * A SUPL-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is used by applications needing to talk to the carrier's
      * Secure User Plane Location servers for help locating the device.
      *
-     * @deprecated Applications should instead use
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
      *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
      *         provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
      */
     @Deprecated
     public static final int TYPE_MOBILE_SUPL = 3;
+
     /**
      * A DUN-specific Mobile data connection.  This network type may use the
      * same network interface as {@link #TYPE_MOBILE} or it may use a different
      * one.  This is sometimes by the system when setting up an upstream connection
      * for tethering so that the carrier is aware of DUN traffic.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasCapability} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
+     *         provides the {@link NetworkCapabilities#NET_CAPABILITY_DUN} capability.
      */
+    @Deprecated
     public static final int TYPE_MOBILE_DUN  = 4;
+
     /**
      * A High Priority Mobile data connection.  This network type uses the
      * same network interface as {@link #TYPE_MOBILE} but the routing setup
      * is different.
      *
-     * @deprecated Applications should instead use
-     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
-     *         uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
     @Deprecated
     public static final int TYPE_MOBILE_HIPRI = 5;
+
     /**
-     * The WiMAX data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A WiMAX data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_WIMAX       = 6;
 
     /**
-     * The Bluetooth data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * A Bluetooth data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_BLUETOOTH   = 7;
 
     /**
      * Dummy data connection.  This should not be used on shipping devices.
+     * @deprecated This is not used any more.
      */
+    @Deprecated
     public static final int TYPE_DUMMY       = 8;
 
     /**
-     * The Ethernet data connection.  When active, all data traffic
-     * will use this network type's interface by default
-     * (it has a default route).
+     * An Ethernet data connection.
+     *
+     * @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
+     *         {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
+     *         appropriate network. {@see NetworkCapabilities} for supported transports.
      */
+    @Deprecated
     public static final int TYPE_ETHERNET    = 9;
 
     /**
      * Over the air Administration.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_FOTA = 10;
 
     /**
      * IP Multimedia Subsystem.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IMS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_IMS  = 11;
 
     /**
      * Carrier Branded Services.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_CBS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_CBS  = 12;
 
     /**
      * A Wi-Fi p2p connection. Only requesting processes will have access to
      * the peers connected.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_WIFI_P2P} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_WIFI_P2P    = 13;
 
     /**
      * The network to use for initially attaching to the network
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_IA} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_IA = 14;
 
     /**
      * Emergency PDN connection for emergency services.  This
      * may include IMS and MMS in emergency situations.
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_EIMS} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_MOBILE_EMERGENCY = 15;
 
     /**
      * The network that uses proxy to achieve connectivity.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static final int TYPE_PROXY = 16;
 
     /**
      * A virtual network using one or more native bearers.
      * It may or may not be providing security services.
+     * @deprecated Applications should use {@link NetworkCapabilities#TRANSPORT_VPN} instead.
      */
+    @Deprecated
     public static final int TYPE_VPN = 17;
 
     /** {@hide} */
@@ -686,8 +730,10 @@
      * @param type the type needing naming
      * @return a String for the given type, or a string version of the type ("87")
      * if no name is known.
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static String getNetworkTypeName(int type) {
         switch (type) {
           case TYPE_NONE:
@@ -738,8 +784,10 @@
      * This should be replaced in the future by a network property.
      * @param networkType the type to check
      * @return a boolean - {@code true} if uses cellular network, else {@code false}
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * {@hide}
      */
+    @Deprecated
     public static boolean isNetworkTypeMobile(int networkType) {
         switch (networkType) {
             case TYPE_MOBILE:
@@ -761,8 +809,10 @@
     /**
      * Checks if the given network type is backed by a Wi-Fi radio.
      *
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     public static boolean isNetworkTypeWifi(int networkType) {
         switch (networkType) {
             case TYPE_WIFI:
@@ -811,6 +861,10 @@
      * You should always check {@link NetworkInfo#isConnected()} before initiating
      * network traffic. This may return {@code null} when there is no default
      * network.
+     * Note that if the default network is a VPN, this method will return the
+     * NetworkInfo for one of its underlying networks instead, or null if the
+     * VPN agent did not specify any. Apps interested in learning about VPNs
+     * should use {@link #getNetworkInfo(android.net.Network)} instead.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
@@ -968,7 +1022,11 @@
      *        which you're interested.
      * @return a {@link NetworkInfo} object for the requested
      *        network type or {@code null} if the type is not
-     *        supported by the device.
+     *        supported by the device. If {@code networkType} is
+     *        TYPE_VPN and a VPN is active for the calling app,
+     *        then this method will try to return one of the
+     *        underlying networks for the VPN or null if the
+     *        VPN agent didn't specify any.
      *
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks} and
@@ -1529,6 +1587,8 @@
      * IllegalArgumentException if no mapping from the legacy type to
      * NetworkCapabilities is known.
      *
+     * @deprecated Types are deprecated. Use {@link NetworkCallback} or {@link NetworkRequest}
+     *     to find the network instead.
      * @hide
      */
     public static NetworkCapabilities networkCapabilitiesForType(int type) {
@@ -1925,13 +1985,6 @@
      * services.jar, possibly in com.android.server.net. */
 
     /** {@hide} */
-    public static final boolean checkChangePermission(Context context) {
-        int uid = Binder.getCallingUid();
-        return Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
-                .getPackageNameForUid(context, uid), false /* throwException */);
-    }
-
-    /** {@hide} */
     public static final void enforceChangePermission(Context context) {
         int uid = Binder.getCallingUid();
         Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings
@@ -2380,6 +2433,7 @@
      *
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
+     * @deprecated Types are deprecated. Use {@link #reportNetworkConnectivity} instead.
      * {@hide}
      */
     public void reportInetCondition(int networkType, int percentage) {
@@ -2511,9 +2565,10 @@
      *
      * @param networkType The network type we'd like to check
      * @return {@code true} if supported, else {@code false}
-     *
+     * @deprecated Types are deprecated. Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
     public boolean isNetworkSupported(int networkType) {
         try {
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 3ce0283..3a3ddcc 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.IpSecConfig;
 import android.net.IpSecUdpEncapResponse;
@@ -48,11 +49,11 @@
 
     void addAddressToTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void removeAddressFromTunnelInterface(
             int tunnelResourceId,
-            String localAddr);
+            in LinkAddress localAddr);
 
     void deleteTunnelInterface(int resourceId);
 
diff --git a/core/java/android/net/INetdEventCallback.aidl b/core/java/android/net/INetdEventCallback.aidl
index 1fd9423..1e75bf4 100644
--- a/core/java/android/net/INetdEventCallback.aidl
+++ b/core/java/android/net/INetdEventCallback.aidl
@@ -20,8 +20,9 @@
 oneway interface INetdEventCallback {
 
     // Possible addNetdEventCallback callers.
-    const int CALLBACK_CALLER_DEVICE_POLICY = 0;
-    const int CALLBACK_CALLER_NETWORK_WATCHLIST = 1;
+    const int CALLBACK_CALLER_CONNECTIVITY_SERVICE = 0;
+    const int CALLBACK_CALLER_DEVICE_POLICY = 1;
+    const int CALLBACK_CALLER_NETWORK_WATCHLIST = 2;
 
     /**
      * Reports a single DNS lookup function call.
@@ -39,6 +40,18 @@
             int uid);
 
     /**
+     * Represents a private DNS validation success or failure.
+     * This method must not block or perform long-running operations.
+     *
+     * @param netId the ID of the network the validation was performed on.
+     * @param ipAddress the IP address for which validation was performed.
+     * @param hostname the hostname for which validation was performed.
+     * @param validated whether or not validation was successful.
+     */
+    void onPrivateDnsValidationEvent(int netId, String ipAddress, String hostname,
+            boolean validated);
+
+    /**
      * Reports a single connect library call.
      * This method must not block or perform long-running operations.
      *
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 90e3ffd..eab7041 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -44,6 +44,16 @@
 
     /** Return data layer snapshot of UID network usage. */
     NetworkStats getDataLayerSnapshotForUid(int uid);
+
+    /** Get a detailed snapshot of stats since boot for all UIDs.
+    *
+    * <p>Results will not always be limited to stats on requiredIfaces when specified: stats for
+    * interfaces stacked on the specified interfaces, or for interfaces on which the specified
+    * interfaces are stacked on, will also be included.
+    * @param requiredIfaces Interface names to get data for, or {@link NetworkStats#INTERFACES_ALL}.
+    */
+    NetworkStats getDetailedUidStats(in String[] requiredIfaces);
+
     /** Return set of any ifaces associated with mobile networks since boot. */
     String[] getMobileIfaces();
 
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index c69a4d4..8034bb6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -38,6 +38,13 @@
     private static final String TAG = "IpSecAlgorithm";
 
     /**
+     * Null cipher.
+     *
+     * @hide
+     */
+    public static final String CRYPT_NULL = "ecb(cipher_null)";
+
+    /**
      * AES-CBC Encryption/Ciphering Algorithm.
      *
      * <p>Valid lengths for this key are {128, 192, 256}.
@@ -49,7 +56,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 128 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
      */
     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
 
@@ -58,7 +66,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 160 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
      */
     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
 
@@ -66,7 +75,8 @@
      * SHA256 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 256 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
      */
     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
 
@@ -74,7 +84,8 @@
      * SHA384 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 384 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
 
@@ -82,7 +93,8 @@
      * SHA512 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 512 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
@@ -105,6 +117,7 @@
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
+        AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
         AUTH_CRYPT_AES_GCM
     })
@@ -119,11 +132,14 @@
      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
      * defined as constants in this class.
      *
+     * <p>For algorithms that produce an integrity check value, the truncation length is a required
+     * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
+     *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key) {
-        this(algorithm, key, key.length * 8);
+    public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
+        this(algorithm, key, 0);
     }
 
     /**
@@ -137,7 +153,8 @@
      * @param key key padded to a multiple of 8 bits.
      * @param truncLenBits number of bits of output hash to use.
      */
-    public IpSecAlgorithm(@AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
+    public IpSecAlgorithm(
+            @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) {
         mName = algorithm;
         mKey = key.clone();
         mTruncLenBits = truncLenBits;
@@ -145,11 +162,13 @@
     }
 
     /** Get the algorithm name */
+    @NonNull
     public String getName() {
         return mName;
     }
 
     /** Get the key for this algorithm */
+    @NonNull
     public byte[] getKey() {
         return mKey.clone();
     }
@@ -218,6 +237,7 @@
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
             default:
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
@@ -263,6 +283,7 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
         return new StringBuilder()
                 .append("{mName=")
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index b609847..cc22771 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -58,14 +58,18 @@
     private static final String TAG = "IpSecManager";
 
     /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic towards the host.
+     * Used when applying a transform to direct traffic through an {@link IpSecTransform}
+     * towards the host.
+     *
+     * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
      */
     public static final int DIRECTION_IN = 0;
 
     /**
-     * For direction-specific attributes of an {@link IpSecTransform}, indicates that an attribute
-     * applies to traffic from the host.
+     * Used when applying a transform to direct traffic through an {@link IpSecTransform}
+     * away from the host.
+     *
+     * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}.
      */
     public static final int DIRECTION_OUT = 1;
 
@@ -249,8 +253,9 @@
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
      */
-    public SecurityParameterIndex allocateSecurityParameterIndex(InetAddress destinationAddress)
-            throws ResourceUnavailableException {
+    @NonNull
+    public SecurityParameterIndex allocateSecurityParameterIndex(
+                @NonNull InetAddress destinationAddress) throws ResourceUnavailableException {
         try {
             return new SecurityParameterIndex(
                     mService,
@@ -269,15 +274,17 @@
      *
      * @param destinationAddress the destination address for traffic bearing the requested SPI.
      *     For inbound traffic, the destination should be an address currently assigned on-device.
-     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI
+     * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
+     *     reserved and may not be used. See RFC 4303 Section 2.1.
      * @return the reserved SecurityParameterIndex
      * @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
      *     currently allocated for this user
      * @throws {@link #SpiUnavailableException} indicating that the requested SPI could not be
      *     reserved
      */
+    @NonNull
     public SecurityParameterIndex allocateSecurityParameterIndex(
-            InetAddress destinationAddress, int requestedSpi)
+            @NonNull InetAddress destinationAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
             throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
@@ -299,22 +306,39 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a stream socket
-     * @param direction the policy direction either {@link #DIRECTION_IN} or {@link #DIRECTION_OUT}
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            Socket socket, int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull Socket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
+        // Ensure creation of FD. See b/77548890 for more details.
+        socket.getSoLinger();
+
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -334,19 +358,21 @@
      *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a datagram socket
-     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            DatagramSocket socket, int direction, IpSecTransform transform) throws IOException {
+    public void applyTransportModeTransform(@NonNull DatagramSocket socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         applyTransportModeTransform(socket.getFileDescriptor$(), direction, transform);
     }
 
@@ -364,22 +390,36 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
-     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
-     * inbound traffic on the old transform will continue to be decrypted until that transform is
-     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
-     * where both transforms are valid until both endpoints are using the new transform and all
-     * in-flight packets have been received.
+     * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
+     * will be removed and the new transform will take effect immediately, sending all traffic on
+     * the new transform; however, when applying a transform in the inbound direction, traffic
+     * on the old transform will continue to be decrypted and delivered until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows lossless rekey
+     * procedures where both transforms are valid until both endpoints are using the new transform
+     * and all in-flight packets have been received.
      *
      * @param socket a socket file descriptor
-     * @param direction the policy direction either DIRECTION_IN or DIRECTION_OUT
+     * @param direction the direction in which the transform should be applied
      * @param transform a transport mode {@code IpSecTransform}
      * @throws IOException indicating that the transform could not be applied
      */
-    public void applyTransportModeTransform(
-            FileDescriptor socket, int direction, IpSecTransform transform)
-            throws IOException {
+    public void applyTransportModeTransform(@NonNull FileDescriptor socket,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         // We dup() the FileDescriptor here because if we don't, then the ParcelFileDescriptor()
         // constructor takes control and closes the user's FD when we exit the method.
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
@@ -390,21 +430,6 @@
     }
 
     /**
-     * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
-     * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to
-     * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic).
-     * Applications should probably not use this API directly. Instead, they should use {@link
-     * VpnService} to provide VPN capability in a more generic fashion.
-     *
-     * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
-     *
-     * @param net a {@link Network} that will be tunneled via IP Sec.
-     * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
-     * @hide
-     */
-    public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
-
-    /**
      * Remove an IPsec transform from a stream socket.
      *
      * <p>Once removed, traffic on the socket will not be encrypted. Removing transforms from a
@@ -417,8 +442,10 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(Socket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull Socket socket) throws IOException {
+        // Ensure creation of FD. See b/77548890 for more details.
+        socket.getSoLinger();
+
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -435,8 +462,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(DatagramSocket socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull DatagramSocket socket) throws IOException {
         removeTransportModeTransforms(socket.getFileDescriptor$());
     }
 
@@ -453,8 +479,7 @@
      * @param socket a socket that previously had a transform applied to it
      * @throws IOException indicating that the transform could not be removed from the socket
      */
-    public void removeTransportModeTransforms(FileDescriptor socket)
-            throws IOException {
+    public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
             mService.removeTransportModeTransforms(pfd);
         } catch (RemoteException e) {
@@ -484,7 +509,7 @@
      * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
      * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
      * caller. The caller should not close the {@code FileDescriptor} returned by {@link
-     * #getSocket}, but should use {@link #close} instead.
+     * #getFileDescriptor}, but should use {@link #close} instead.
      *
      * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
      * of the next user who binds to that port. To prevent this scenario, these sockets are held
@@ -523,8 +548,8 @@
             mCloseGuard.open("constructor");
         }
 
-        /** Get the wrapped socket. */
-        public FileDescriptor getSocket() {
+        /** Get the encapsulation socket's file descriptor. */
+        public FileDescriptor getFileDescriptor() {
             if (mPfd == null) {
                 return null;
             }
@@ -592,6 +617,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
             throws IOException, ResourceUnavailableException {
         /*
@@ -621,6 +647,7 @@
     // safely usable for Encapsulation without allowing a user to possibly unbind from/close
     // the port, which could potentially impact the traffic of the next user who binds to that
     // socket.
+    @NonNull
     public UdpEncapsulationSocket openUdpEncapsulationSocket()
             throws IOException, ResourceUnavailableException {
         return new UdpEncapsulationSocket(mService, 0);
@@ -649,6 +676,7 @@
         private int mResourceId = INVALID_RESOURCE_ID;
 
         /** Get the underlying SPI held by this object. */
+        @NonNull
         public String getInterfaceName() {
             return mInterfaceName;
         }
@@ -660,10 +688,15 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
-         * @throws IOException if the address could not be added
          * @hide
          */
-        public void addAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        public void addAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.addAddressToTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -672,10 +705,15 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
-         * @throws IOException if the address could not be removed
          * @hide
          */
-        public void removeAddress(LinkAddress address) throws IOException {
+        @SystemApi
+        public void removeAddress(@NonNull LinkAddress address) throws IOException {
+            try {
+                mService.removeAddressFromTunnelInterface(mResourceId, address);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         private IpSecTunnelInterface(@NonNull IIpSecService service,
@@ -762,6 +800,7 @@
      * @hide
      */
     @SystemApi
+    @NonNull
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
     public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
             @NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
@@ -770,7 +809,12 @@
     }
 
     /**
-     * Apply a transform to the IpSecTunnelInterface
+     * Apply an active Tunnel Mode IPsec Transform to a {@link IpSecTunnelInterface}, which will
+     * tunnel all traffic for the given direction through the underlying network's interface with
+     * IPsec (applies an outer IP header and IPsec Header to all traffic, and expects an additional
+     * IP header and IPsec Header on all inbound traffic).
+     * <p>Applications should probably not use this API directly.
+     *
      *
      * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
      *        transform.
@@ -783,8 +827,8 @@
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
-    public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
-            IpSecTransform transform) throws IOException {
+    public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
+            @PolicyDirection int direction, @NonNull IpSecTransform transform) throws IOException {
         try {
             mService.applyTunnelModeTransform(
                     tunnel.getResourceId(), direction, transform.getResourceId());
@@ -792,6 +836,7 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
     /**
      * Construct an instance of IpSecManager within an application context.
      *
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 60e96f9..cf58647 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -350,6 +350,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setEncryption(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an encryption algorithm.
             Preconditions.checkNotNull(algo);
@@ -364,6 +365,7 @@
          *
          * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthentication(@NonNull IpSecAlgorithm algo) {
             // TODO: throw IllegalArgumentException if algo is not an authentication algorithm.
             Preconditions.checkNotNull(algo);
@@ -384,6 +386,7 @@
          * @param algo {@link IpSecAlgorithm} specifying the authenticated encryption algorithm to
          *     be applied.
          */
+        @NonNull
         public IpSecTransform.Builder setAuthenticatedEncryption(@NonNull IpSecAlgorithm algo) {
             Preconditions.checkNotNull(algo);
             mConfig.setAuthenticatedEncryption(algo);
@@ -403,6 +406,7 @@
          * @param remotePort the UDP port number of the remote host that will send and receive
          *     encapsulated traffic. In the case of IKEv2, this should be port 4500.
          */
+        @NonNull
         public IpSecTransform.Builder setIpv4Encapsulation(
                 @NonNull IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
             Preconditions.checkNotNull(localSocket);
@@ -436,6 +440,7 @@
          *     collides with an existing transform
          * @throws IOException indicating other errors
          */
+        @NonNull
         public IpSecTransform buildTransportModeTransform(
                 @NonNull InetAddress sourceAddress,
                 @NonNull IpSecManager.SecurityParameterIndex spi)
@@ -472,6 +477,7 @@
          * @hide
          */
         @SystemApi
+        @NonNull
         @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
         public IpSecTransform buildTunnelModeTransform(
                 @NonNull InetAddress sourceAddress,
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3683d34..f14847f 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -25,6 +25,8 @@
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.DatagramSocket;
@@ -136,9 +138,15 @@
             for (int i = 0; i < hostAddresses.length; i++) {
                 try {
                     Socket socket = createSocket();
-                    if (localAddress != null) socket.bind(localAddress);
-                    socket.connect(new InetSocketAddress(hostAddresses[i], port));
-                    return socket;
+                    boolean failed = true;
+                    try {
+                        if (localAddress != null) socket.bind(localAddress);
+                        socket.connect(new InetSocketAddress(hostAddresses[i], port));
+                        failed = false;
+                        return socket;
+                    } finally {
+                        if (failed) IoUtils.closeQuietly(socket);
+                    }
                 } catch (IOException e) {
                     if (i == (hostAddresses.length - 1)) throw e;
                 }
@@ -155,15 +163,27 @@
         public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
                 int localPort) throws IOException {
             Socket socket = createSocket();
-            socket.bind(new InetSocketAddress(localAddress, localPort));
-            socket.connect(new InetSocketAddress(address, port));
+            boolean failed = true;
+            try {
+                socket.bind(new InetSocketAddress(localAddress, localPort));
+                socket.connect(new InetSocketAddress(address, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
         @Override
         public Socket createSocket(InetAddress host, int port) throws IOException {
             Socket socket = createSocket();
-            socket.connect(new InetSocketAddress(host, port));
+            boolean failed = true;
+            try {
+                socket.connect(new InetSocketAddress(host, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
@@ -175,7 +195,13 @@
         @Override
         public Socket createSocket() throws IOException {
             Socket socket = new Socket();
-            bindSocket(socket);
+            boolean failed = true;
+            try {
+                bindSocket(socket);
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
     }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 2fc8c68..19f0c90 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -315,7 +315,7 @@
 
     /**
      * Capabilities that suggest that a network is restricted.
-     * {@see #maybeMarkCapabilitiesRestricted}.
+     * {@see #maybeMarkCapabilitiesRestricted}, {@see #FORCE_RESTRICTED_CAPABILITIES}
      */
     @VisibleForTesting
     /* package */ static final long RESTRICTED_CAPABILITIES =
@@ -326,7 +326,13 @@
             (1 << NET_CAPABILITY_IA) |
             (1 << NET_CAPABILITY_IMS) |
             (1 << NET_CAPABILITY_RCS) |
-            (1 << NET_CAPABILITY_XCAP) |
+            (1 << NET_CAPABILITY_XCAP);
+
+    /**
+     * Capabilities that force network to be restricted.
+     * {@see #maybeMarkCapabilitiesRestricted}.
+     */
+    private static final long FORCE_RESTRICTED_CAPABILITIES =
             (1 << NET_CAPABILITY_OEM_PAID);
 
     /**
@@ -381,7 +387,7 @@
     /**
      * Removes (if found) the given capability from this {@code NetworkCapability} instance.
      * <p>
-     * Note that this method removes capabilities that was added via {@link #addCapability(int)},
+     * Note that this method removes capabilities that were added via {@link #addCapability(int)},
      * {@link #addUnwantedCapability(int)} or {@link #setCapabilities(int[], int[])} .
      *
      * @param capability the capability to be removed.
@@ -470,6 +476,7 @@
                 && ((mUnwantedNetworkCapabilities & (1 << capability)) != 0);
     }
 
+    /** Note this method may result in having the same capability in wanted and unwanted lists. */
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
         this.mUnwantedNetworkCapabilities |= nc.mUnwantedNetworkCapabilities;
@@ -530,16 +537,21 @@
      * @hide
      */
     public void maybeMarkCapabilitiesRestricted() {
+        // Check if we have any capability that forces the network to be restricted.
+        final boolean forceRestrictedCapability =
+                (mNetworkCapabilities & FORCE_RESTRICTED_CAPABILITIES) != 0;
+
         // Verify there aren't any unrestricted capabilities.  If there are we say
-        // the whole thing is unrestricted.
+        // the whole thing is unrestricted unless it is forced to be restricted.
         final boolean hasUnrestrictedCapabilities =
-                ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0);
+                (mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0;
 
         // Must have at least some restricted capabilities.
         final boolean hasRestrictedCapabilities =
-                ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0);
+                (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0;
 
-        if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) {
+        if (forceRestrictedCapability
+                || (hasRestrictedCapabilities && !hasUnrestrictedCapabilities)) {
             removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
         }
     }
@@ -1131,7 +1143,11 @@
     }
 
     /**
-     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
+     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set.
+     * <p>
+     * Note that this method may break an invariant of having a particular capability in either
+     * wanted or unwanted lists but never in both.  Requests that have the same capability in
+     * both lists will never be satisfied.
      * @hide
      */
     public void combineCapabilities(NetworkCapabilities nc) {
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index e6ad89a..999771a 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -38,14 +38,18 @@
      * <table>
      * <tr><td><b>Detailed state</b></td><td><b>Coarse-grained state</b></td></tr>
      * <tr><td><code>IDLE</code></td><td><code>DISCONNECTED</code></td></tr>
-     * <tr><td><code>SCANNING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>SCANNING</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>CONNECTING</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>AUTHENTICATING</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>OBTAINING_IPADDR</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>VERIFYING_POOR_LINK</code></td><td><code>CONNECTING</code></td></tr>
+     * <tr><td><code>CAPTIVE_PORTAL_CHECK</code></td><td><code>CONNECTING</code></td></tr>
      * <tr><td><code>CONNECTED</code></td><td><code>CONNECTED</code></td></tr>
+     * <tr><td><code>SUSPENDED</code></td><td><code>SUSPENDED</code></td></tr>
      * <tr><td><code>DISCONNECTING</code></td><td><code>DISCONNECTING</code></td></tr>
      * <tr><td><code>DISCONNECTED</code></td><td><code>DISCONNECTED</code></td></tr>
-     * <tr><td><code>UNAVAILABLE</code></td><td><code>DISCONNECTED</code></td></tr>
      * <tr><td><code>FAILED</code></td><td><code>DISCONNECTED</code></td></tr>
+     * <tr><td><code>BLOCKED</code></td><td><code>DISCONNECTED</code></td></tr>
      * </table>
      */
     public enum State {
@@ -163,8 +167,17 @@
      * @return one of {@link ConnectivityManager#TYPE_MOBILE}, {@link
      * ConnectivityManager#TYPE_WIFI}, {@link ConnectivityManager#TYPE_WIMAX}, {@link
      * ConnectivityManager#TYPE_ETHERNET},  {@link ConnectivityManager#TYPE_BLUETOOTH}, or other
-     * types defined by {@link ConnectivityManager}
+     * types defined by {@link ConnectivityManager}.
+     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
+     *             multiple transports. Note that generally apps should not care about transport;
+     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+     *             apps concerned with meteredness or bandwidth should be looking at, as they
+     *             offer this information with much better accuracy.
      */
+    @Deprecated
     public int getType() {
         synchronized (this) {
             return mNetworkType;
@@ -172,8 +185,10 @@
     }
 
     /**
+     * @deprecated Use {@link NetworkCapabilities} instead
      * @hide
      */
+    @Deprecated
     public void setType(int type) {
         synchronized (this) {
             mNetworkType = type;
@@ -205,7 +220,16 @@
      * Return a human-readable name describe the type of the network,
      * for example "WIFI" or "MOBILE".
      * @return the name of the network type
+     * @deprecated Callers should switch to checking {@link NetworkCapabilities#hasTransport}
+     *             instead with one of the NetworkCapabilities#TRANSPORT_* constants :
+     *             {@link #getType} and {@link #getTypeName} cannot account for networks using
+     *             multiple transports. Note that generally apps should not care about transport;
+     *             {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED} and
+     *             {@link NetworkCapabilities#getLinkDownstreamBandwidthKbps} are calls that
+     *             apps concerned with meteredness or bandwidth should be looking at, as they
+     *             offer this information with much better accuracy.
      */
+    @Deprecated
     public String getTypeName() {
         synchronized (this) {
             return mTypeName;
@@ -230,7 +254,15 @@
      * that the network is fully usable.
      * @return {@code true} if network connectivity exists or is in the process
      * of being established, {@code false} otherwise.
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isConnectedOrConnecting() {
         synchronized (this) {
             return mState == State.CONNECTED || mState == State.CONNECTING;
@@ -259,8 +291,18 @@
      * data roaming has been disabled.</li>
      * <li>The device's radio is turned off, e.g., because airplane mode is enabled.</li>
      * </ul>
+     * Since Android L, this always returns {@code true}, because the system only
+     * returns info for available networks.
      * @return {@code true} if the network is available, {@code false} otherwise
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public boolean isAvailable() {
         synchronized (this) {
             return mIsAvailable;
@@ -270,9 +312,11 @@
     /**
      * Sets if the network is available, ie, if the connectivity is possible.
      * @param isAvailable the new availability value.
+     * @deprecated Use {@link NetworkCapabilities} instead
      *
      * @hide
      */
+    @Deprecated
     public void setIsAvailable(boolean isAvailable) {
         synchronized (this) {
             mIsAvailable = isAvailable;
@@ -285,7 +329,10 @@
      * network following a disconnect from another network.
      * @return {@code true} if this is a failover attempt, {@code false}
      * otherwise.
+     * @deprecated This field is not populated in recent Android releases,
+     *             and does not make a lot of sense in a multi-network world.
      */
+    @Deprecated
     public boolean isFailover() {
         synchronized (this) {
             return mIsFailover;
@@ -296,8 +343,10 @@
      * Set the failover boolean.
      * @param isFailover {@code true} to mark the current connection attempt
      * as a failover.
+     * @deprecated This hasn't been set in any recent Android release.
      * @hide
      */
+    @Deprecated
     public void setFailover(boolean isFailover) {
         synchronized (this) {
             mIsFailover = isFailover;
@@ -322,7 +371,10 @@
         }
     }
 
-    /** {@hide} */
+    /**
+     * @deprecated Use {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING} instead.
+     * {@hide}
+     */
     @VisibleForTesting
     @Deprecated
     public void setRoaming(boolean isRoaming) {
@@ -334,7 +386,15 @@
     /**
      * Reports the current coarse-grained state of the network.
      * @return the coarse-grained state
+     * @deprecated Apps should instead use the
+     *             {@link android.net.ConnectivityManager.NetworkCallback} API to
+     *             learn about connectivity changes.
+     *             {@link ConnectivityManager#registerDefaultNetworkCallback} and
+     *             {@link ConnectivityManager#registerNetworkCallback}. These will
+     *             give a more accurate picture of the connectivity state of
+     *             the device and let apps react more easily and quickly to changes.
      */
+    @Deprecated
     public State getState() {
         synchronized (this) {
             return mState;
@@ -358,8 +418,10 @@
      * if one was supplied. May be {@code null}.
      * @param extraInfo an optional {@code String} providing addditional network state
      * information passed up from the lower networking layers.
+     * @deprecated Use {@link NetworkCapabilities} instead.
      * @hide
      */
+    @Deprecated
     public void setDetailedState(DetailedState detailedState, String reason, String extraInfo) {
         synchronized (this) {
             this.mDetailedState = detailedState;
@@ -385,6 +447,8 @@
      * Report the reason an attempt to establish connectivity failed,
      * if one is available.
      * @return the reason for failure, or null if not available
+     * @deprecated This method does not have a consistent contract that could make it useful
+     *             to callers.
      */
     public String getReason() {
         synchronized (this) {
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 96826f8..1ee0ed7d 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -23,6 +23,7 @@
 import android.text.TextUtils;
 
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Defines a request for a network, made through {@link NetworkRequest.Builder} and used
@@ -204,6 +205,19 @@
         }
 
         /**
+         * Set the watched UIDs for this request. This will be reset and wiped out unless
+         * the calling app holds the CHANGE_NETWORK_STATE permission.
+         *
+         * @param uids The watched UIDs as a set of UidRanges, or null for everything.
+         * @return The builder to facilitate chaining.
+         * @hide
+         */
+        public Builder setUids(Set<UidRange> uids) {
+            mNetworkCapabilities.setUids(uids);
+            return this;
+        }
+
+        /**
          * Add a capability that must not exist in the requested network.
          * <p>
          * If the capability was previously added to the list of required capabilities (for
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 01b2b39..d6cbc88 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -31,6 +31,7 @@
 import java.io.PrintWriter;
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -64,6 +65,9 @@
     /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
     public static final int SET_DBG_VPN_OUT = 1002;
 
+    /** Include all interfaces when filtering */
+    public static final String[] INTERFACES_ALL = null;
+
     /** {@link #tag} value for total data across all tags. */
     // TODO: Rename TAG_NONE to TAG_ALL.
     public static final int TAG_NONE = 0;
@@ -94,6 +98,11 @@
     /** Denotes a request for stats at the interface and UID level. */
     public static final int STATS_PER_UID = 1;
 
+    private static final String CLATD_INTERFACE_PREFIX = "v4-";
+    // Delta between IPv4 header (20b) and IPv6 header (40b).
+    // Used for correct stats accounting on clatd interfaces.
+    private static final int IPV4V6_HEADER_DELTA = 20;
+
     // TODO: move fields to "mVariable" notation
 
     /**
@@ -359,23 +368,27 @@
             capacity = newLength;
         }
 
-        iface[size] = entry.iface;
-        uid[size] = entry.uid;
-        set[size] = entry.set;
-        tag[size] = entry.tag;
-        metered[size] = entry.metered;
-        roaming[size] = entry.roaming;
-        defaultNetwork[size] = entry.defaultNetwork;
-        rxBytes[size] = entry.rxBytes;
-        rxPackets[size] = entry.rxPackets;
-        txBytes[size] = entry.txBytes;
-        txPackets[size] = entry.txPackets;
-        operations[size] = entry.operations;
+        setValues(size, entry);
         size++;
 
         return this;
     }
 
+    private void setValues(int i, Entry entry) {
+        iface[i] = entry.iface;
+        uid[i] = entry.uid;
+        set[i] = entry.set;
+        tag[i] = entry.tag;
+        metered[i] = entry.metered;
+        roaming[i] = entry.roaming;
+        defaultNetwork[i] = entry.defaultNetwork;
+        rxBytes[i] = entry.rxBytes;
+        rxPackets[i] = entry.rxPackets;
+        txBytes[i] = entry.txBytes;
+        txPackets[i] = entry.txPackets;
+        operations[i] = entry.operations;
+    }
+
     /**
      * Return specific stats entry.
      */
@@ -745,6 +758,75 @@
     }
 
     /**
+     * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+     *
+     * <p>This mutates both base and stacked traffic stats, to account respectively for
+     * double-counted traffic and IPv4/IPv6 header size difference.
+     *
+     * <p>For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
+     * packet on the stacked interface, and once as translated to an IPv6 packet on the
+     * base interface. For correct stats accounting on the base interface, every 464xlat
+     * packet needs to be subtracted from the root UID on the base interface both for tx
+     * and rx traffic (http://b/12249687, http:/b/33681750).
+     *
+     * <p>This method will behave fine if {@code stackedIfaces} is an non-synchronized but add-only
+     * {@code ConcurrentHashMap}
+     * @param baseTraffic Traffic on the base interfaces. Will be mutated.
+     * @param stackedTraffic Stats with traffic stacked on top of our ifaces. Will also be mutated.
+     * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     */
+    public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+            NetworkStats stackedTraffic, Map<String, String> stackedIfaces) {
+        // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
+        // stackedIfaces may grow afterwards, but NetworkStats will just be resized automatically.
+        final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
+
+        // For recycling
+        Entry entry = null;
+        Entry adjust = new NetworkStats.Entry(IFACE_ALL, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
+
+        for (int i = 0; i < stackedTraffic.size; i++) {
+            entry = stackedTraffic.getValues(i, entry);
+            if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
+                continue;
+            }
+            final String baseIface = stackedIfaces.get(entry.iface);
+            if (baseIface == null) {
+                continue;
+            }
+            // Subtract any 464lat traffic seen for the root UID on the current base interface.
+            adjust.iface = baseIface;
+            adjust.rxBytes = -(entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
+            adjust.txBytes = -(entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
+            adjust.rxPackets = -entry.rxPackets;
+            adjust.txPackets = -entry.txPackets;
+            adjustments.combineValues(adjust);
+
+            // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
+            // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
+            // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
+            // difference for all packets (http://b/12249687, http:/b/33681750).
+            entry.rxBytes += entry.rxPackets * IPV4V6_HEADER_DELTA;
+            entry.txBytes += entry.txPackets * IPV4V6_HEADER_DELTA;
+            stackedTraffic.setValues(i, entry);
+        }
+
+        baseTraffic.combineAllValues(adjustments);
+    }
+
+    /**
+     * Calculate and apply adjustments to captured statistics for 464xlat traffic counted twice.
+     *
+     * <p>This mutates the object this method is called on. Equivalent to calling
+     * {@link #apply464xlatAdjustments(NetworkStats, NetworkStats, Map)} with {@code this} as
+     * base and stacked traffic.
+     * @param stackedIfaces Mapping ipv6if -> ipv4if interface where traffic is counted on both.
+     */
+    public void apply464xlatAdjustments(Map<String, String> stackedIfaces) {
+        apply464xlatAdjustments(this, this, stackedIfaces);
+    }
+
+    /**
      * Return total statistics grouped by {@link #iface}; doesn't mutate the
      * original structure.
      */
@@ -824,6 +906,39 @@
         return stats;
     }
 
+    /**
+     * Only keep entries that match all specified filters.
+     *
+     * <p>This mutates the original structure in place. After this method is called,
+     * size is the number of matching entries, and capacity is the previous capacity.
+     * @param limitUid UID to filter for, or {@link #UID_ALL}.
+     * @param limitIfaces Interfaces to filter for, or {@link #INTERFACES_ALL}.
+     * @param limitTag Tag to filter for, or {@link #TAG_ALL}.
+     */
+    public void filter(int limitUid, String[] limitIfaces, int limitTag) {
+        if (limitUid == UID_ALL && limitTag == TAG_ALL && limitIfaces == INTERFACES_ALL) {
+            return;
+        }
+
+        Entry entry = new Entry();
+        int nextOutputEntry = 0;
+        for (int i = 0; i < size; i++) {
+            entry = getValues(i, entry);
+            final boolean matches =
+                    (limitUid == UID_ALL || limitUid == entry.uid)
+                    && (limitTag == TAG_ALL || limitTag == entry.tag)
+                    && (limitIfaces == INTERFACES_ALL
+                            || ArrayUtils.contains(limitIfaces, entry.iface));
+
+            if (matches) {
+                setValues(nextOutputEntry, entry);
+                nextOutputEntry++;
+            }
+        }
+
+        size = nextOutputEntry;
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index b02d48d..c3f23a1 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -225,7 +225,7 @@
      * Indicates the Secure Element on which the transaction occurred.
      * eSE1...eSEn for Embedded Secure Elements, SIM1...SIMn for UICC, etc.
      */
-    public static final String EXTRA_SE_NAME = "android.nfc.extra.SE_NAME";
+    public static final String EXTRA_SECURE_ELEMENT_NAME = "android.nfc.extra.SECURE_ELEMENT_NAME";
 
     public static final int STATE_OFF = 1;
     public static final int STATE_TURNING_ON = 2;
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 336e1b4..5652d6d 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.BinderInternal;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
@@ -361,7 +362,9 @@
      * Add the calling thread to the IPC thread pool.  This function does
      * not return until the current process is exiting.
      */
-    public static final native void joinThreadPool();
+    public static final void joinThreadPool() {
+        BinderInternal.joinThreadPool();
+    }
 
     /**
      * Returns true if the specified interface is a proxy.
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a5e1934..31dbafa 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -268,10 +268,12 @@
     NetworkStats getNetworkStatsDetail();
 
     /**
-     * Return detailed network statistics for the requested UID,
+     * Return detailed network statistics for the requested UID and interfaces,
      * including interface and tag details.
+     * @param uid UID to obtain statistics for, or {@link NetworkStats#UID_ALL}.
+     * @param ifaces Interfaces to obtain statistics for, or {@link NetworkStats#INTERFACES_ALL}.
      */
-    NetworkStats getNetworkStatsUidDetail(int uid);
+    NetworkStats getNetworkStatsUidDetail(int uid, in String[] ifaces);
 
     /**
      * Return summary of network statistics all tethering interfaces.
@@ -340,7 +342,7 @@
      * Configure name servers, search paths, and resolver parameters for the given network.
      */
     void setDnsConfigurationForNetwork(int netId, in String[] servers, in String[] domains,
-            in int[] params, boolean useTls, String tlsHostname);
+            in int[] params, String tlsHostname, in String[] tlsServers);
 
     void setFirewallEnabled(boolean enabled);
     boolean isFirewallEnabled();
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 624e28a..0b98ecf 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -254,6 +254,7 @@
         } else if (record != null) {
             record.mEvents = 0;
             mFileDescriptorRecords.removeAt(index);
+            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
         }
     }
 
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 4f6d322..89168ae 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -189,7 +189,12 @@
             }
             ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
             for (int i=0; i<callbacks.size(); i++) {
-                callbacks.get(i).run();
+                try {
+                    callbacks.get(i).run();
+                } catch (Throwable t) {
+                    Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+                    // Ignore and try to go on.
+                }
             }
         }
     }
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index fa96dd3..0f4fc36 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -89,6 +89,8 @@
     public static final long TRACE_TAG_NETWORK = 1L << 21;
     /** @hide */
     public static final long TRACE_TAG_ADB = 1L << 22;
+    /** @hide */
+    public static final long TRACE_TAG_AIDL = 1L << 24;
 
     private static final long TRACE_TAG_NOT_READY = 1L << 63;
     private static final int MAX_SECTION_NAME_LEN = 127;
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index ecec448..5bf051d 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -4,6 +4,9 @@
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
@@ -356,6 +359,15 @@
         return result;
     }
 
+    /**
+     * Temporary dummy function.
+     * @hide
+     */
+    public ArrayList<WorkChain> getWorkChains() {
+        return null;
+    }
+
+
     private boolean removeUids(WorkSource other) {
         int N1 = mNum;
         final int[] uids1 = mUids;
@@ -664,6 +676,52 @@
         }
     }
 
+    /**
+     * Temporary dummy class for WorkChain. Will be removed.
+     * @hide
+     */
+    public static final class WorkChain implements Parcelable {
+        // @VisibleForTesting
+        public WorkChain() {
+        }
+
+        private WorkChain(Parcel in) {
+            in.readInt();
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public int[] getUids() {
+            return null;
+        }
+
+        /** @hide */
+        @VisibleForTesting
+        public String[] getTags() {
+            return null;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(0);
+        }
+
+        public static final Parcelable.Creator<WorkChain> CREATOR =
+                new Parcelable.Creator<WorkChain>() {
+                    public WorkChain createFromParcel(Parcel in) {
+                        return new WorkChain(in);
+                    }
+                    public WorkChain[] newArray(int size) {
+                        return new WorkChain[size];
+                    }
+                };
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 57418c8..939c190 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -32,6 +32,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 
@@ -158,6 +159,13 @@
     private final Object mLock = new Object();
 
     /**
+     * List of exemptions to the API blacklist. These are prefix matches on the runtime format
+     * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+     * list.
+     */
+    private List<String> mApiBlacklistExemptions = Collections.emptyList();
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -175,7 +183,7 @@
      * The process will continue running after this function returns.
      *
      * <p>If processes are not enabled, a new thread in the caller's
-     * process is created and main() of <var>processClass</var> called there.
+     * process is created and main() of <var>processclass</var> called there.
      *
      * <p>The niceName parameter, if not an empty string, is a custom name to
      * give to the process instead of using processClass.  This allows you to
@@ -454,6 +462,49 @@
     }
 
     /**
+     * Push hidden API blacklisting exemptions into the zygote process(es).
+     *
+     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+     * this call.
+     *
+     * @param exemptions List of hidden API exemption prefixes.
+     */
+    public void setApiBlacklistExemptions(List<String> exemptions) {
+        synchronized (mLock) {
+            mApiBlacklistExemptions = exemptions;
+            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+        if (state == null || state.isClosed()) {
+            return;
+        }
+        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
+            return;
+        }
+        try {
+            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+            state.writer.newLine();
+            state.writer.write("--set-api-blacklist-exemptions");
+            state.writer.newLine();
+            for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
+                state.writer.write(mApiBlacklistExemptions.get(i));
+                state.writer.newLine();
+            }
+            state.writer.flush();
+            int status = state.inputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", ioe);
+        }
+    }
+
+    /**
      * Tries to open socket to Zygote process if not already open. If
      * already open, does nothing.  May block and retry.  Requires that mLock be held.
      */
@@ -467,8 +518,8 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
         }
-
         if (primaryZygoteState.matches(abi)) {
             return primaryZygoteState;
         }
@@ -480,6 +531,7 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
         }
 
         if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 8aef012..67c6fb9 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
+import android.telecom.Log;
 
 /**
  * <p>
@@ -261,9 +262,16 @@
      */
     @WorkerThread
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
-        return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
+            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "isBlocked: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -297,9 +305,16 @@
      * @return {@code true} if the current user can block numbers.
      */
     public static boolean canCurrentUserBlockNumbers(Context context) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
-        return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
+            return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -368,8 +383,14 @@
          * the provider unless {@link #endBlockSuppression(Context)} is called.
          */
         public static void notifyEmergencyContact(Context context) {
-            context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            try {
+                context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "notifyEmergencyContact: provider not ready.");
+            }
         }
 
         /**
@@ -394,9 +415,16 @@
          */
         public static boolean shouldSystemBlockNumber(Context context, String phoneNumber,
                 Bundle extras) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
-            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
+                return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldSystemBlockNumber: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -416,9 +444,16 @@
          * @return {@code true} if should show emergency call notification. {@code false} otherwise.
          */
         public static boolean shouldShowEmergencyCallNotification(Context context) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
-            return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
+                return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -436,9 +471,16 @@
         public static boolean getEnhancedBlockSetting(Context context, String key) {
             Bundle extras = new Bundle();
             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
-            return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
+                return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "getEnhancedBlockSetting: provider not ready.");
+                return false;
+            }
         }
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ca3f5e9..2b5353d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3621,6 +3621,14 @@
         public static final Validator VIBRATE_WHEN_RINGING_VALIDATOR = sBooleanValidator;
 
         /**
+         * When {@code 1}, Telecom enhanced call blocking functionality is enabled.  When
+         * {@code 0}, enhanced call blocking functionality is disabled.
+         * @hide
+         */
+        public static final String DEBUG_ENABLE_ENHANCED_CALL_BLOCKING =
+                "debug.enable_enhanced_calling";
+
+        /**
          * Whether the audible DTMF tones are played by the dialer when dialing. The value is
          * boolean (1 or 0).
          */
@@ -8706,6 +8714,12 @@
         public static final String BLE_SCAN_LOW_LATENCY_INTERVAL_MS =
                 "ble_scan_low_latency_interval_ms";
 
+        /**
+         * The mode that BLE scanning clients will be moved to when in the background.
+         * @hide
+         */
+        public static final String BLE_SCAN_BACKGROUND_MODE = "ble_scan_background_mode";
+
        /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 65ce67f..5db3c1a 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Channel {
+public final class Channel implements java.nio.channels.Channel {
 
     private static final String TAG = "OMAPI.Channel";
     private Session mSession;
@@ -47,7 +47,8 @@
     private final SEService mService;
     private final Object mLock = new Object();
 
-    Channel(SEService service, Session session, ISecureElementChannel channel) {
+    Channel(@NonNull SEService service, @NonNull Session session,
+            @NonNull ISecureElementChannel channel) {
         if (service == null || session == null || channel == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -63,7 +64,7 @@
      * before closing the channel.
      */
     public void close() {
-        if (!isClosed()) {
+        if (isOpen()) {
             synchronized (mLock) {
                 try {
                     mChannel.close();
@@ -75,21 +76,21 @@
     }
 
     /**
-     * Tells if this channel is closed.
+     * Tells if this channel is open.
      *
-     * @return <code>true</code> if the channel is closed or in case of an error.
-     *         <code>false</code> otherwise.
+     * @return <code>false</code> if the channel is closed or in case of an error.
+     *         <code>true</code> otherwise.
      */
-    public boolean isClosed() {
+    public boolean isOpen() {
         if (!mService.isConnected()) {
             Log.e(TAG, "service not connected to system");
-            return true;
+            return false;
         }
         try {
-            return mChannel.isClosed();
+            return !mChannel.isClosed();
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in isClosed()");
-            return true;
+            return false;
         }
     }
 
@@ -158,7 +159,7 @@
      * @throws SecurityException if the command is filtered by the security policy.
      * @throws NullPointerException if command is NULL.
      */
-    public @NonNull byte[] transmit(byte[] command) throws IOException {
+    public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index e0c6e04..e9dd181 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -24,8 +24,4 @@
  * @hide
  */
 interface ISecureElementListener {
-  /**
-   * Called by the framework when the service is connected.
-   */
-  void serviceConnected();
 }
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 3dec976..80262f7 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -37,7 +37,7 @@
  *
  * @see <a href="http://globalplatform.org">GlobalPlatform Open Mobile API</a>
  */
-public class Reader {
+public final class Reader {
 
     private static final String TAG = "OMAPI.Reader";
     private final String mName;
@@ -46,7 +46,7 @@
     private final Object mLock = new Object();
 
 
-    Reader(SEService service, String name, ISecureElementReader reader) {
+    Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
         if (reader == null || service == null || name == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d59e86a..14727f0 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.concurrent.Executor;
 
 /**
  * The SEService realises the communication to available Secure Elements on the
@@ -40,7 +41,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class SEService {
+public final class SEService {
 
     /**
      * Error code used with ServiceSpecificException.
@@ -62,17 +63,38 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+    public interface OnConnectedListener {
+        /**
+         * Called by the framework when the service is connected.
+         */
+        void onConnected();
+    }
+
+    /**
+     * Listener object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private class SEListener extends ISecureElementListener.Stub {
+        public OnConnectedListener mListener = null;
+        public Executor mExecutor = null;
+
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        /**
-         * Called by the framework when the service is connected.
-         */
-        public void serviceConnected() {};
+        public void onConnected() {
+            if (mListener != null && mExecutor != null) {
+                mExecutor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        mListener.onConnected();
+                    }
+                });
+            }
+        }
     }
+    private SEListener mSEListener = new SEListener();
 
     private static final String TAG = "OMAPI.SEService";
 
@@ -95,34 +117,32 @@
     private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
 
     /**
-     * Listener object that allows the notification of the caller if this
-     * SEService could be bound to the backend.
-     */
-    private ISecureElementListener mSEListener;
-
-    /**
      * Establishes a new connection that can be used to connect to all the
      * Secure Elements available in the system. The connection process can be
      * quite long, so it happens in an asynchronous way. It is usable only if
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * serviceConnected() method called when the connection actually happen.
+     * onConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object. Can be <code>null</code>.
+     *            a OnConnectedListener object.
+     * @param executor
+     *            an Executor which will be used when invoking the callback.
      */
-    public SEService(Context context, SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull Executor executor,
+            @NonNull OnConnectedListener listener) {
 
-        if (context == null) {
-            throw new NullPointerException("context must not be null");
+        if (context == null || listener == null || executor == null) {
+            throw new NullPointerException("Arguments must not be null");
         }
 
         mContext = context;
-        mSEListener = listener;
+        mSEListener.mListener = listener;
+        mSEListener.mExecutor = executor;
 
         mConnection = new ServiceConnection() {
 
@@ -131,9 +151,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    try {
-                        mSEListener.serviceConnected();
-                    } catch (RemoteException ignore) { }
+                    mSEListener.onConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -164,12 +182,12 @@
     }
 
     /**
-     * Returns the list of available Secure Element readers.
+     * Returns an array of available Secure Element readers.
      * There must be no duplicated objects in the returned list.
      * All available readers shall be listed even if no card is inserted.
      *
-     * @return The readers list, as an array of Readers. If there are no
-     * readers the returned array is of length 0.
+     * @return An array of Readers. If there are no readers the returned array
+     * is of length 0.
      */
     public @NonNull Reader[] getReaders() {
         if (mSecureElementService == null) {
@@ -205,7 +223,8 @@
      * (including any binding to an underlying service).
      * As a result isConnected() will return false after shutdown() was called.
      * After this method call, the SEService object is not connected.
-     * It is recommended to call this method in the termination method of the calling application
+     * This method should be called when connection to the Secure Element is not needed
+     * or in the termination method of the calling application
      * (or part of this application) which is bound to this SEService.
      */
     public void shutdown() {
@@ -233,7 +252,7 @@
      *
      * @return String containing the OpenMobile API version (e.g. "3.0").
      */
-    public String getVersion() {
+    public @NonNull String getVersion() {
         return "3.2";
     }
 
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index 19a018e..d5f8c82 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -39,7 +39,7 @@
  *
  * @see <a href="http://simalliance.org">SIMalliance Open Mobile API  v3.0</a>
  */
-public class Session {
+public final class Session {
 
     private final Object mLock = new Object();
     private final SEService mService;
@@ -47,7 +47,8 @@
     private final ISecureElementSession mSession;
     private static final String TAG = "OMAPI.Session";
 
-    Session(SEService service, ISecureElementSession session, Reader reader) {
+    Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+            @NonNull Reader reader) {
         if (service == null || reader == null || session == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -195,7 +196,8 @@
      *             supported by the device
      * @return an instance of Channel if available or null.
      */
-    public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -244,7 +246,7 @@
      *             supported by the device
      * @return an instance of Channel if available or null.
      */
-    public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid) throws IOException {
         return openBasicChannel(aid, (byte) 0x00);
     }
 
@@ -300,13 +302,8 @@
      * @return an instance of Channel. Null if the Secure Element is unable to
      *         provide a new logical channel.
      */
-    public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
-
-        if ((mReader.getName().startsWith("SIM")) && (aid == null)) {
-            Log.e(TAG, "NULL AID not supported on " + mReader.getName());
-            return null;
-        }
-
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -358,7 +355,7 @@
      * @return an instance of Channel. Null if the Secure Element is unable to
      *         provide a new logical channel.
      */
-    public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid) throws IOException {
         return openLogicalChannel(aid, (byte) 0x00);
     }
 }
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
index 6965dc0..433dd3a 100644
--- a/core/java/android/util/StatsLog.java
+++ b/core/java/android/util/StatsLog.java
@@ -23,21 +23,37 @@
 public final class StatsLog {
     private static final String TAG = "StatsManager";
 
-    public static final int BLUETOOTH_ENABLED = 0;
+    public static final int BLUETOOTH_ENABLED_STATE_CHANGED = 0;
+    public static final int BLUETOOTH_ENABLED_STATE_CHANGED__STATE__UNKNOWN = 0;
+    public static final int BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED = 1;
+    public static final int BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED = 2;
 
     public static final int BLUETOOTH_CONNECTION_STATE_CHANGED = 1;
 
     public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED = 2;
     public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__UNKNOWN = 0;
-    public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__START = 1;
+    public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__PLAY = 1;
     public static final int BLUETOOTH_A2DP_AUDIO_STATE_CHANGED__STATE__STOP = 2;
 
+    public static final int BLE_SCAN_STATE_CHANGED = 2;
+    public static final int BLE_SCAN_STATE_CHANGED__STATE__OFF = 0;
+    public static final int BLE_SCAN_STATE_CHANGED__STATE__ON = 1;
+    public static final int BLE_SCAN_STATE_CHANGED__STATE__RESET = 2;
+
     private StatsLog() {}
 
     public static void write(int id, int field1) {}
 
-    public static void write(int id, int field1, int field2) {}
+    public static void write(int id, int field1, int field2, int field3) {}
 
     public static void write_non_chained(int id, int uid, String tag,
-            boolean field1, int field2, String field3) {}
+            int field1, int field2, String field3) {}
+
+    /** I am a dummy javadoc comment. */
+    public static void write(int code, int[] uid, String[] tag, int arg2,
+            boolean arg3, boolean arg4, boolean arg5) {};
+
+    /** I am a dummy javadoc comment. */
+    public static void write_non_chained(int code, int arg1, String arg2, int arg3,
+            boolean arg4, boolean arg5, boolean arg6) {};
 }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e576a0f..79f42df 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -397,4 +397,10 @@
      * Return the touch region for the current IME window, or an empty region if there is none.
      */
     Region getCurrentImeTouchRegion();
+
+    /**
+     * Requests that the WindowManager sends WindowManagerPolicy#ACTION_USER_ACTIVITY_NOTIFICATION
+     * on the next user activity.
+     */
+    void requestUserActivityNotification();
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index c4ffb4c..8e4f5b4 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -159,6 +159,12 @@
     public final static boolean WATCH_POINTER = false;
 
     /**
+     * Broadcast sent when a user activity is detected.
+     */
+    public final static String ACTION_USER_ACTIVITY_NOTIFICATION =
+            "android.intent.action.USER_ACTIVITY_NOTIFICATION";
+
+    /**
      * Sticky broadcast of the current HDMI plugged state.
      */
     public final static String ACTION_HDMI_PLUGGED = "android.intent.action.HDMI_PLUGGED";
@@ -1741,4 +1747,10 @@
      * @return true if ready; false otherwise.
      */
     boolean canDismissBootAnimation();
+
+    /**
+     * Requests that the WindowManager sends WindowManagerPolicy#ACTION_USER_ACTIVITY_NOTIFICATION
+     * on the next user activity.
+     */
+    public void requestUserActivityNotification();
 }
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index 902bd12..8561bf0 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -22,16 +22,14 @@
 import static android.net.NetworkStats.UID_ALL;
 import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
 
+import android.annotation.Nullable;
 import android.net.NetworkStats;
 import android.os.StrictMode;
 import android.os.SystemClock;
-import android.util.ArrayMap;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ProcFileReader;
-import com.google.android.collect.Lists;
 
 import libcore.io.IoUtils;
 
@@ -41,8 +39,10 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.net.ProtocolException;
-import java.util.ArrayList;
-import java.util.Objects;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Creates {@link NetworkStats} instances by parsing various {@code /proc/}
@@ -54,11 +54,6 @@
     private static final boolean USE_NATIVE_PARSING = true;
     private static final boolean SANITY_CHECK_NATIVE = false;
 
-    private static final String CLATD_INTERFACE_PREFIX = "v4-";
-    // Delta between IPv4 header (20b) and IPv6 header (40b).
-    // Used for correct stats accounting on clatd interfaces.
-    private static final int IPV4V6_HEADER_DELTA = 20;
-
     /** Path to {@code /proc/net/dev}. */
     private final File mStatsIfaceDev;
     /** Path to {@code /proc/net/xt_qtaguid/iface_stat_all}. */
@@ -70,18 +65,64 @@
 
     private boolean mUseBpfStats;
 
-    // TODO: to improve testability and avoid global state, do not use a static variable.
-    @GuardedBy("sStackedIfaces")
-    private static final ArrayMap<String, String> sStackedIfaces = new ArrayMap<>();
+    // TODO: only do adjustments in NetworkStatsService and remove this.
+    /**
+     * (Stacked interface) -> (base interface) association for all connected ifaces since boot.
+     *
+     * Because counters must never roll backwards, once a given interface is stacked on top of an
+     * underlying interface, the stacked interface can never be stacked on top of
+     * another interface. */
+    private static final ConcurrentHashMap<String, String> sStackedIfaces
+            = new ConcurrentHashMap<>();
 
     public static void noteStackedIface(String stackedIface, String baseIface) {
-        synchronized (sStackedIfaces) {
-            if (baseIface != null) {
-                sStackedIfaces.put(stackedIface, baseIface);
-            } else {
-                sStackedIfaces.remove(stackedIface);
+        if (stackedIface != null && baseIface != null) {
+            sStackedIfaces.put(stackedIface, baseIface);
+        }
+    }
+
+    /**
+     * Get a set of interfaces containing specified ifaces and stacked interfaces.
+     *
+     * <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces
+     * on which the specified ones are stacked. Stacked interfaces are those noted with
+     * {@link #noteStackedIface(String, String)}, but only interfaces noted before this method
+     * is called are guaranteed to be included.
+     */
+    public static String[] augmentWithStackedInterfaces(@Nullable String[] requiredIfaces) {
+        if (requiredIfaces == NetworkStats.INTERFACES_ALL) {
+            return null;
+        }
+
+        HashSet<String> relatedIfaces = new HashSet<>(Arrays.asList(requiredIfaces));
+        // ConcurrentHashMap's EntrySet iterators are "guaranteed to traverse
+        // elements as they existed upon construction exactly once, and may
+        // (but are not guaranteed to) reflect any modifications subsequent to construction".
+        // This is enough here.
+        for (Map.Entry<String, String> entry : sStackedIfaces.entrySet()) {
+            if (relatedIfaces.contains(entry.getKey())) {
+                relatedIfaces.add(entry.getValue());
+            } else if (relatedIfaces.contains(entry.getValue())) {
+                relatedIfaces.add(entry.getKey());
             }
         }
+
+        String[] outArray = new String[relatedIfaces.size()];
+        return relatedIfaces.toArray(outArray);
+    }
+
+    /**
+     * Applies 464xlat adjustments with ifaces noted with {@link #noteStackedIface(String, String)}.
+     * @see NetworkStats#apply464xlatAdjustments(NetworkStats, NetworkStats, Map)
+     */
+    public static void apply464xlatAdjustments(NetworkStats baseTraffic,
+            NetworkStats stackedTraffic) {
+        NetworkStats.apply464xlatAdjustments(baseTraffic, stackedTraffic, sStackedIfaces);
+    }
+
+    @VisibleForTesting
+    public static void clearStackedIfaces() {
+        sStackedIfaces.clear();
     }
 
     public NetworkStatsFactory() {
@@ -250,51 +291,10 @@
             NetworkStats lastStats) throws IOException {
         final NetworkStats stats =
               readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats);
-        final ArrayMap<String, String> stackedIfaces;
-        synchronized (sStackedIfaces) {
-            stackedIfaces = new ArrayMap<>(sStackedIfaces);
-        }
-        // Total 464xlat traffic to subtract from uid 0 on all base interfaces.
-        final NetworkStats adjustments = new NetworkStats(0, stackedIfaces.size());
 
-        NetworkStats.Entry entry = null; // For recycling
-
-        // For 464xlat traffic, xt_qtaguid sees every IPv4 packet twice, once as a native IPv4
-        // packet on the stacked interface, and once as translated to an IPv6 packet on the
-        // base interface. For correct stats accounting on the base interface, every 464xlat
-        // packet needs to be subtracted from the root UID on the base interface both for tx
-        // and rx traffic (http://b/12249687, http:/b/33681750).
-        for (int i = 0; i < stats.size(); i++) {
-            entry = stats.getValues(i, entry);
-            if (entry.iface == null || !entry.iface.startsWith(CLATD_INTERFACE_PREFIX)) {
-                continue;
-            }
-            final String baseIface = stackedIfaces.get(entry.iface);
-            if (baseIface == null) {
-                continue;
-            }
-
-            NetworkStats.Entry adjust =
-                    new NetworkStats.Entry(baseIface, 0, 0, 0, 0, 0, 0, 0L, 0L, 0L, 0L, 0L);
-            // Subtract any 464lat traffic seen for the root UID on the current base interface.
-            adjust.rxBytes -= (entry.rxBytes + entry.rxPackets * IPV4V6_HEADER_DELTA);
-            adjust.txBytes -= (entry.txBytes + entry.txPackets * IPV4V6_HEADER_DELTA);
-            adjust.rxPackets -= entry.rxPackets;
-            adjust.txPackets -= entry.txPackets;
-            adjustments.combineValues(adjust);
-
-            // For 464xlat traffic, xt_qtaguid only counts the bytes of the native IPv4 packet sent
-            // on the stacked interface with prefix "v4-" and drops the IPv6 header size after
-            // unwrapping. To account correctly for on-the-wire traffic, add the 20 additional bytes
-            // difference for all packets (http://b/12249687, http:/b/33681750).
-            entry.rxBytes = entry.rxPackets * IPV4V6_HEADER_DELTA;
-            entry.txBytes = entry.txPackets * IPV4V6_HEADER_DELTA;
-            entry.rxPackets = 0;
-            entry.txPackets = 0;
-            stats.combineValues(entry);
-        }
-
-        stats.combineAllValues(adjustments);
+        // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap.
+        // TODO: remove this and only apply adjustments in NetworkStatsService.
+        stats.apply464xlatAdjustments(sStackedIfaces);
 
         return stats;
     }
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index bb5a0ad..a9cd5c8 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -34,6 +34,7 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Objects;
 import java.util.TimeZone;
 import java.util.logging.LogManager;
 import org.apache.harmony.luni.internal.util.TimezoneGetter;
@@ -67,8 +68,12 @@
      * but apps can override that behavior.
      */
     private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
+        public volatile boolean mTriggered = false;
+
         @Override
         public void uncaughtException(Thread t, Throwable e) {
+            mTriggered = true;
+
             // Don't re-enter if KillApplicationHandler has already run
             if (mCrashing) return;
 
@@ -96,12 +101,33 @@
     /**
      * Handle application death from an uncaught exception.  The framework
      * catches these for the main threads, so this should only matter for
-     * threads created by applications.  Before this method runs,
-     * {@link LoggingHandler} will already have logged details.
+     * threads created by applications. Before this method runs, the given
+     * instance of {@link LoggingHandler} should already have logged details
+     * (and if not it is run first).
      */
     private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler {
+        private final LoggingHandler mLoggingHandler;
+
+        /**
+         * Create a new KillApplicationHandler that follows the given LoggingHandler.
+         * If {@link #uncaughtException(Thread, Throwable) uncaughtException} is called
+         * on the created instance without {@code loggingHandler} having been triggered,
+         * {@link LoggingHandler#uncaughtException(Thread, Throwable)
+         * loggingHandler.uncaughtException} will be called first.
+         *
+         * @param loggingHandler the {@link LoggingHandler} expected to have run before
+         *     this instance's {@link #uncaughtException(Thread, Throwable) uncaughtException}
+         *     is being called.
+         */
+        public KillApplicationHandler(LoggingHandler loggingHandler) {
+            this.mLoggingHandler = Objects.requireNonNull(loggingHandler);
+        }
+
+        @Override
         public void uncaughtException(Thread t, Throwable e) {
             try {
+                ensureLogging(t, e);
+
                 // Don't re-enter -- avoid infinite loops if crash-reporting crashes.
                 if (mCrashing) return;
                 mCrashing = true;
@@ -132,6 +158,33 @@
                 System.exit(10);
             }
         }
+
+        /**
+         * Ensures that the logging handler has been triggered.
+         *
+         * See b/73380984. This reinstates the pre-O behavior of
+         *
+         *   {@code thread.getUncaughtExceptionHandler().uncaughtException(thread, e);}
+         *
+         * logging the exception (in addition to killing the app). This behavior
+         * was never documented / guaranteed but helps in diagnostics of apps
+         * using the pattern.
+         *
+         * If this KillApplicationHandler is invoked the "regular" way (by
+         * {@link Thread#dispatchUncaughtException(Throwable)
+         * Thread.dispatchUncaughtException} in case of an uncaught exception)
+         * then the pre-handler (expected to be {@link #mLoggingHandler}) will already
+         * have run. Otherwise, we manually invoke it here.
+         */
+        private void ensureLogging(Thread t, Throwable e) {
+            if (!mLoggingHandler.mTriggered) {
+                try {
+                    mLoggingHandler.uncaughtException(t, e);
+                } catch (Throwable loggingThrowable) {
+                    // Ignored.
+                }
+            }
+        }
     }
 
     protected static final void commonInit() {
@@ -141,8 +194,9 @@
          * set handlers; these apply to all threads in the VM. Apps can replace
          * the default handler, but not the pre handler.
          */
-        Thread.setUncaughtExceptionPreHandler(new LoggingHandler());
-        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
+        LoggingHandler loggingHandler = new LoggingHandler();
+        Thread.setUncaughtExceptionPreHandler(loggingHandler);
+        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
 
         /*
          * Install a TimezoneGetter subclass for ZoneInfo.db
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 4901080..f0e7796 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -115,6 +115,14 @@
         command.append(' ');
         command.append(appProcess);
 
+        // Generate bare minimum of debug information to be able to backtrace through JITed code.
+        // We assume that if the invoke wrapper is used, backtraces are desirable:
+        //  * The wrap.sh script can only be used by debuggable apps, which would enable this flag
+        //    without the script anyway (the fork-zygote path).  So this makes the two consistent.
+        //  * The wrap.* property can only be used on userdebug builds and is likely to be used by
+        //    developers (e.g. enable debug-malloc), in which case backtraces are also useful.
+        command.append(" -Xcompiler-option --generate-mini-debug-info");
+
         command.append(" /system/bin --application");
         if (niceName != null) {
             command.append(" '--nice-name=").append(niceName).append("'");
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e23cbf8..0d10888 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -55,10 +55,21 @@
     public static final int DISABLE_VERIFIER = 1 << 9;
     /** Only use oat files located in /system. Otherwise use dex/jar/apk . */
     public static final int ONLY_USE_SYSTEM_OAT_FILES = 1 << 10;
-    /** Do enfore hidden API access restrictions. */
-    public static final int ENABLE_HIDDEN_API_CHECKS = 1 << 11;
     /** Force generation of native debugging information for backtraces. */
-    public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 12;
+    public static final int DEBUG_GENERATE_MINI_DEBUG_INFO = 1 << 11;
+    /**
+     * Hidden API access restrictions. This is a mask for bits representing the API enforcement
+     * policy, defined by {@code @ApplicationInfo.HiddenApiEnforcementPolicy}.
+     */
+    public static final int API_ENFORCEMENT_POLICY_MASK = (1 << 12) | (1 << 13);
+    /**
+     * Bit shift for use with {@link #API_ENFORCEMENT_POLICY_MASK}.
+     *
+     * (flags & API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT gives
+     * @ApplicationInfo.ApiEnforcementPolicy values.
+     */
+    public static final int API_ENFORCEMENT_POLICY_SHIFT =
+            Integer.numberOfTrailingZeros(API_ENFORCEMENT_POLICY_MASK);
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a32fb43..adc5508 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -47,6 +47,8 @@
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
+
 import libcore.io.IoUtils;
 
 /**
@@ -159,6 +161,11 @@
             return null;
         }
 
+        if (parsedArgs.apiBlacklistExemptions != null) {
+            handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+            return null;
+        }
+
         if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
             throw new ZygoteSecurityException("Client may not specify capabilities: " +
                     "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -278,6 +285,15 @@
         }
     }
 
+    private void handleApiBlacklistExemptions(String[] exemptions) {
+        try {
+            ZygoteInit.setApiBlacklistExemptions(exemptions);
+            mSocketOutStream.writeInt(0);
+        } catch (IOException ioe) {
+            throw new IllegalStateException("Error writing to command socket", ioe);
+        }
+    }
+
     protected void preload() {
         ZygoteInit.lazyPreload();
     }
@@ -424,6 +440,12 @@
         boolean startChildZygote;
 
         /**
+         * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
+         * or when they change, via --set-api-blacklist-exemptions.
+         */
+        String[] apiBlacklistExemptions;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -576,6 +598,11 @@
                     preloadDefault = true;
                 } else if (arg.equals("--start-child-zygote")) {
                     startChildZygote = true;
+                } else if (arg.equals("--set-api-blacklist-exemptions")) {
+                    // consume all remaining args; this is a stand-alone command, never included
+                    // with the regular fork command.
+                    apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+                    curArg = args.length;
                 } else {
                     break;
                 }
@@ -590,7 +617,7 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
-            } else if (!preloadDefault) {
+            } else if (!preloadDefault && apiBlacklistExemptions == null) {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
                 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a05454f..c8e7102 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -514,6 +514,10 @@
         /* should never reach here */
     }
 
+    public static void setApiBlacklistExemptions(String[] exemptions) {
+        VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
+    }
+
     /**
      * Creates a PathClassLoader for the given class path that is associated with a shared
      * namespace, i.e., this classloader can access platform-private native libraries. The
diff --git a/core/java/com/android/server/net/BaseNetdEventCallback.java b/core/java/com/android/server/net/BaseNetdEventCallback.java
index 3d3a3d0..fdba2f3 100644
--- a/core/java/com/android/server/net/BaseNetdEventCallback.java
+++ b/core/java/com/android/server/net/BaseNetdEventCallback.java
@@ -32,6 +32,12 @@
     }
 
     @Override
+    public void onPrivateDnsValidationEvent(int netId, String ipAddress,
+            String hostname, boolean validated) {
+        // default no-op
+    }
+
+    @Override
     public void onConnectEvent(String ipAddr, int port, long timestamp, int uid) {
         // default no-op
     }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3784d4d..7f6d19b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -20,6 +20,7 @@
 
 #include <android_runtime/AndroidRuntime.h>
 
+#include <android-base/macros.h>
 #include <android-base/properties.h>
 #include <binder/IBinder.h>
 #include <binder/IPCThreadState.h>
@@ -590,7 +591,6 @@
 {
     JavaVMInitArgs initArgs;
     char propBuf[PROPERTY_VALUE_MAX];
-    char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
     char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];
     char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
@@ -672,15 +672,7 @@
         executionMode = kEMJitCompiler;
     }
 
-    // If dalvik.vm.stack-trace-dir is set, it enables the "new" stack trace
-    // dump scheme and a new file is created for each stack dump. If it isn't set,
-    // the old scheme is enabled.
-    property_get("dalvik.vm.stack-trace-dir", propBuf, "");
-    if (strlen(propBuf) > 0) {
-        addOption("-Xusetombstonedtraces");
-    } else {
-        parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");
-    }
+    addOption("-Xusetombstonedtraces");
 
     strcpy(jniOptsBuf, "-Xjniopts:");
     if (parseRuntimeOption("dalvik.vm.jniopts", jniOptsBuf, "-Xjniopts:")) {
@@ -860,34 +852,18 @@
 
     // The runtime will compile a boot image, when necessary, not using installd. Thus, we need to
     // pass the instruction-set-features/variant as an image-compiler-option.
-    // TODO: Find a better way for the instruction-set.
-#if defined(__arm__)
-    constexpr const char* instruction_set = "arm";
-#elif defined(__aarch64__)
-    constexpr const char* instruction_set = "arm64";
-#elif defined(__mips__) && !defined(__LP64__)
-    constexpr const char* instruction_set = "mips";
-#elif defined(__mips__) && defined(__LP64__)
-    constexpr const char* instruction_set = "mips64";
-#elif defined(__i386__)
-    constexpr const char* instruction_set = "x86";
-#elif defined(__x86_64__)
-    constexpr const char* instruction_set = "x86_64";
-#else
-    constexpr const char* instruction_set = "unknown";
-#endif
     // Note: it is OK to reuse the buffer, as the values are exactly the same between
     //       * compiler-option, used for runtime compilation (DexClassLoader)
     //       * image-compiler-option, used for boot-image compilation on device
 
     // Copy the variant.
-    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);
+    sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", ABI_STRING);
     parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
                         "--instruction-set-variant=", "-Ximage-compiler-option");
     parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,
                         "--instruction-set-variant=", "-Xcompiler-option");
     // Copy the features.
-    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);
+    sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", ABI_STRING);
     parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
                         "--instruction-set-features=", "-Ximage-compiler-option");
     parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index a94cac0..9ec7517 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -124,6 +124,12 @@
         if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) {
             //ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks);
             env->CallStaticVoidMethod(sClazz, sCallChangeCallbacks);
+            // There should not be any exceptions. But we must guarantee
+            // there are none on return.
+            if (env->ExceptionCheck()) {
+                env->ExceptionClear();
+                LOG(ERROR) << "Exception pending after sysprop_change!";
+            }
         }
     }
 }
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3f95cf4..e5281ff 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -71,6 +71,9 @@
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
+#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
+                              append(StringPrintf(__VA_ARGS__))
+
 static pid_t gSystemServerPid = 0;
 
 static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
@@ -186,30 +189,32 @@
 
 // Calls POSIX setgroups() using the int[] object as an argument.
 // A NULL argument is tolerated.
-static void SetGids(JNIEnv* env, jintArray javaGids) {
+static bool SetGids(JNIEnv* env, jintArray javaGids, std::string* error_msg) {
   if (javaGids == NULL) {
-    return;
+    return true;
   }
 
   ScopedIntArrayRO gids(env, javaGids);
   if (gids.get() == NULL) {
-    RuntimeAbort(env, __LINE__, "Getting gids int array failed");
+    *error_msg = CREATE_ERROR("Getting gids int array failed");
+    return false;
   }
   int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
   if (rc == -1) {
-    std::ostringstream oss;
-    oss << "setgroups failed: " << strerror(errno) << ", gids.size=" << gids.size();
-    RuntimeAbort(env, __LINE__, oss.str().c_str());
+    *error_msg = CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size());
+    return false;
   }
+
+  return true;
 }
 
 // Sets the resource limits via setrlimit(2) for the values in the
 // two-dimensional array of integers that's passed in. The second dimension
 // contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
 // treated as an empty array.
-static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) {
+static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error_msg) {
   if (javaRlimits == NULL) {
-    return;
+    return true;
   }
 
   rlimit rlim;
@@ -219,7 +224,8 @@
     ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
     ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
     if (javaRlimit.size() != 3) {
-      RuntimeAbort(env, __LINE__, "rlimits array must have a second dimension of size 3");
+      *error_msg = CREATE_ERROR("rlimits array must have a second dimension of size 3");
+      return false;
     }
 
     rlim.rlim_cur = javaRlimit[1];
@@ -227,11 +233,13 @@
 
     int rc = setrlimit(javaRlimit[0], &rlim);
     if (rc == -1) {
-      ALOGE("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
+      *error_msg = CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
             rlim.rlim_max);
-      RuntimeAbort(env, __LINE__, "setrlimit failed");
+      return false;
     }
   }
+
+  return true;
 }
 
 // The debug malloc library needs to know whether it's the zygote or a child.
@@ -252,21 +260,23 @@
   }
 
   // Apply system or app filter based on uid.
-  if (getuid() >= AID_APP_START) {
+  if (uid >= AID_APP_START) {
     set_app_seccomp_filter();
   } else {
     set_system_seccomp_filter();
   }
 }
 
-static void EnableKeepCapabilities(JNIEnv* env) {
+static bool EnableKeepCapabilities(std::string* error_msg) {
   int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
   if (rc == -1) {
-    RuntimeAbort(env, __LINE__, "prctl(PR_SET_KEEPCAPS) failed");
+    *error_msg = CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
+    return false;
   }
+  return true;
 }
 
-static void DropCapabilitiesBoundingSet(JNIEnv* env) {
+static bool DropCapabilitiesBoundingSet(std::string* error_msg) {
   for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
     int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
     if (rc == -1) {
@@ -274,14 +284,15 @@
         ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
               "your kernel is compiled with file capabilities support");
       } else {
-        ALOGE("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
-        RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed");
+        *error_msg = CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
+        return false;
       }
     }
   }
+  return true;
 }
 
-static void SetInheritable(JNIEnv* env, uint64_t inheritable) {
+static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -289,21 +300,23 @@
 
   __user_cap_data_struct capdata[2];
   if (capget(&capheader, &capdata[0]) == -1) {
-    ALOGE("capget failed: %s", strerror(errno));
-    RuntimeAbort(env, __LINE__, "capget failed");
+    *error_msg = CREATE_ERROR("capget failed: %s", strerror(errno));
+    return false;
   }
 
   capdata[0].inheritable = inheritable;
   capdata[1].inheritable = inheritable >> 32;
 
   if (capset(&capheader, &capdata[0]) == -1) {
-    ALOGE("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
-    RuntimeAbort(env, __LINE__, "capset failed");
+    *error_msg = CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
+    return false;
   }
+
+  return true;
 }
 
-static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective,
-                            uint64_t inheritable) {
+static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
+                            std::string* error_msg) {
   __user_cap_header_struct capheader;
   memset(&capheader, 0, sizeof(capheader));
   capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -319,18 +332,20 @@
   capdata[1].inheritable = inheritable >> 32;
 
   if (capset(&capheader, &capdata[0]) == -1) {
-    ALOGE("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") failed: %s", permitted,
-          effective, inheritable, strerror(errno));
-    RuntimeAbort(env, __LINE__, "capset failed");
+    *error_msg = CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
+                              "failed: %s", permitted, effective, inheritable, strerror(errno));
+    return false;
   }
+  return true;
 }
 
-static void SetSchedulerPolicy(JNIEnv* env) {
+static bool SetSchedulerPolicy(std::string* error_msg) {
   errno = -set_sched_policy(0, SP_DEFAULT);
   if (errno != 0) {
-    ALOGE("set_sched_policy(0, SP_DEFAULT) failed");
-    RuntimeAbort(env, __LINE__, "set_sched_policy(0, SP_DEFAULT) failed");
+    *error_msg = CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno));
+    return false;
   }
+  return true;
 }
 
 static int UnmountTree(const char* path) {
@@ -364,7 +379,7 @@
 // Create a private mount namespace and bind mount appropriate emulated
 // storage for the given user.
 static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
-        bool force_mount_namespace) {
+        bool force_mount_namespace, std::string* error_msg) {
     // See storage config details at http://source.android.com/tech/storage/
 
     String8 storageSource;
@@ -381,7 +396,7 @@
 
     // Create a second private mount namespace for our process
     if (unshare(CLONE_NEWNS) == -1) {
-        ALOGW("Failed to unshare(): %s", strerror(errno));
+        *error_msg = CREATE_ERROR("Failed to unshare(): %s", strerror(errno));
         return false;
     }
 
@@ -392,7 +407,9 @@
 
     if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
             NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
-        ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno));
+        *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+                                  storageSource.string(),
+                                  strerror(errno));
         return false;
     }
 
@@ -400,11 +417,14 @@
     userid_t user_id = multiuser_get_user_id(uid);
     const String8 userSource(String8::format("/mnt/user/%d", user_id));
     if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+        *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
         return false;
     }
     if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
             NULL, MS_BIND, NULL)) == -1) {
-        ALOGW("Failed to mount %s to /storage/self: %s", userSource.string(), strerror(errno));
+        *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+                                  userSource.string(),
+                                  strerror(errno));
         return false;
     }
 
@@ -436,31 +456,32 @@
 // descriptor (if any) is closed via dup2(), replacing it with a valid
 // (open) descriptor to /dev/null.
 
-static void DetachDescriptors(JNIEnv* env, jintArray fdsToClose) {
+static bool DetachDescriptors(JNIEnv* env, jintArray fdsToClose, std::string* error_msg) {
   if (!fdsToClose) {
-    return;
+    return true;
   }
   jsize count = env->GetArrayLength(fdsToClose);
   ScopedIntArrayRO ar(env, fdsToClose);
   if (ar.get() == NULL) {
-      RuntimeAbort(env, __LINE__, "Bad fd array");
+    *error_msg = "Bad fd array";
+    return false;
   }
   jsize i;
   int devnull;
   for (i = 0; i < count; i++) {
     devnull = open("/dev/null", O_RDWR);
     if (devnull < 0) {
-      ALOGE("Failed to open /dev/null: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "Failed to open /dev/null");
-      continue;
+      *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
+      return false;
     }
     ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
     if (dup2(devnull, ar[i]) < 0) {
-      ALOGE("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
-      RuntimeAbort(env, __LINE__, "Failed dup2()");
+      *error_msg = StringPrintf("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
+      return false;
     }
     close(devnull);
   }
+  return true;
 }
 
 void SetThreadName(const char* thread_name) {
@@ -488,25 +509,30 @@
   if (errno != 0) {
     ALOGW("Unable to set the name of current thread to '%s': %s", buf, strerror(errno));
   }
+  // Update base::logging default tag.
+  android::base::SetDefaultTag(buf);
 }
 
 // The list of open zygote file descriptors.
 static FileDescriptorTable* gOpenFdTable = NULL;
 
-static void FillFileDescriptorVector(JNIEnv* env,
+static bool FillFileDescriptorVector(JNIEnv* env,
                                      jintArray java_fds,
-                                     std::vector<int>* fds) {
+                                     std::vector<int>* fds,
+                                     std::string* error_msg) {
   CHECK(fds != nullptr);
   if (java_fds != nullptr) {
     ScopedIntArrayRO ar(env, java_fds);
     if (ar.get() == nullptr) {
-      RuntimeAbort(env, __LINE__, "Bad fd array");
+      *error_msg = "Bad fd array";
+      return false;
     }
     fds->reserve(ar.size());
     for (size_t i = 0; i < ar.size(); ++i) {
       fds->push_back(ar[i]);
     }
   }
+  return true;
 }
 
 // Utility routine to fork zygote and specialize the child process.
@@ -524,32 +550,53 @@
   sigemptyset(&sigchld);
   sigaddset(&sigchld, SIGCHLD);
 
+  auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
+      __attribute__ ((noreturn)) {
+    const char* se_name_c_str = nullptr;
+    std::unique_ptr<ScopedUtfChars> se_name;
+    if (java_se_name != nullptr) {
+      se_name.reset(new ScopedUtfChars(env, java_se_name));
+      se_name_c_str = se_name->c_str();
+    }
+    if (se_name_c_str == nullptr && is_system_server) {
+      se_name_c_str = "system_server";
+    }
+    const std::string& error_msg = (se_name_c_str == nullptr)
+        ? msg
+        : StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
+    env->FatalError(error_msg.c_str());
+    __builtin_unreachable();
+  };
+
   // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
   // log, which would result in the logging FDs we close being reopened.
   // This would cause failures because the FDs are not whitelisted.
   //
   // Note that the zygote process is single threaded at this point.
   if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
-    ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
-    RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_BLOCK, { SIGCHLD }) failed.");
+    fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
   }
 
   // Close any logging related FDs before we start evaluating the list of
   // file descriptors.
   __android_log_close();
 
+  std::string error_msg;
+
   // If this is the first fork for this zygote, create the open FD table.
   // If it isn't, we just need to check whether the list of open files has
   // changed (and it shouldn't in the normal case).
   std::vector<int> fds_to_ignore;
-  FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore);
+  if (!FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore, &error_msg)) {
+    fail_fn(error_msg);
+  }
   if (gOpenFdTable == NULL) {
-    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore);
+    gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
     if (gOpenFdTable == NULL) {
-      RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table.");
+      fail_fn(error_msg);
     }
-  } else if (!gOpenFdTable->Restat(fds_to_ignore)) {
-    RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table.");
+  } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
+    fail_fn(error_msg);
   }
 
   pid_t pid = fork();
@@ -558,31 +605,33 @@
     PreApplicationInit();
 
     // Clean up any descriptors which must be closed immediately
-    DetachDescriptors(env, fdsToClose);
+    if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
+      fail_fn(error_msg);
+    }
 
     // Re-open all remaining open file descriptors so that they aren't shared
     // with the zygote across a fork.
-    if (!gOpenFdTable->ReopenOrDetach()) {
-      RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors.");
+    if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
+      fail_fn(error_msg);
     }
 
     if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
+      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
     }
 
-    // Must be called when the new process still has CAP_SYS_ADMIN.  The other alternative is to
-    // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
-    // b/71859146).
-    SetUpSeccompFilter(uid);
-
     // Keep capabilities across UID change, unless we're staying root.
     if (uid != 0) {
-      EnableKeepCapabilities(env);
+      if (!EnableKeepCapabilities(&error_msg)) {
+        fail_fn(error_msg);
+      }
     }
 
-    SetInheritable(env, permittedCapabilities);
-    DropCapabilitiesBoundingSet(env);
+    if (!SetInheritable(permittedCapabilities, &error_msg)) {
+      fail_fn(error_msg);
+    }
+    if (!DropCapabilitiesBoundingSet(&error_msg)) {
+      fail_fn(error_msg);
+    }
 
     bool use_native_bridge = !is_system_server && (instructionSet != NULL)
         && android::NativeBridgeAvailable();
@@ -599,8 +648,8 @@
       ALOGW("Native bridge will not be used because dataDir == NULL.");
     }
 
-    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge)) {
-      ALOGW("Failed to mount emulated storage: %s", strerror(errno));
+    if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+      ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
       if (errno == ENOTCONN || errno == EROFS) {
         // When device is actively encrypting, we get ENOTCONN here
         // since FUSE was mounted before the framework restarted.
@@ -608,7 +657,7 @@
         // FUSE hasn't been created yet by init.
         // In either case, continue without external storage.
       } else {
-        RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage");
+        fail_fn(error_msg);
       }
     }
 
@@ -623,9 +672,14 @@
         }
     }
 
-    SetGids(env, javaGids);
+    std::string error_msg;
+    if (!SetGids(env, javaGids, &error_msg)) {
+      fail_fn(error_msg);
+    }
 
-    SetRLimits(env, javaRlimits);
+    if (!SetRLimits(env, javaRlimits, &error_msg)) {
+      fail_fn(error_msg);
+    }
 
     if (use_native_bridge) {
       ScopedUtfChars isa_string(env, instructionSet);
@@ -635,14 +689,19 @@
 
     int rc = setresgid(gid, gid, gid);
     if (rc == -1) {
-      ALOGE("setresgid(%d) failed: %s", gid, strerror(errno));
-      RuntimeAbort(env, __LINE__, "setresgid failed");
+      fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
     }
 
+    // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+    // uid from 0, which clears capabilities.  The other alternative is to call
+    // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+    // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
+    // app process.
+    SetUpSeccompFilter(uid);
+
     rc = setresuid(uid, uid, uid);
     if (rc == -1) {
-      ALOGE("setresuid(%d) failed: %s", uid, strerror(errno));
-      RuntimeAbort(env, __LINE__, "setresuid failed");
+      fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
     }
 
     if (NeedsNoRandomizeWorkaround()) {
@@ -654,9 +713,14 @@
         }
     }
 
-    SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities);
+    if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
+                         &error_msg)) {
+      fail_fn(error_msg);
+    }
 
-    SetSchedulerPolicy(env);
+    if (!SetSchedulerPolicy(&error_msg)) {
+      fail_fn(error_msg);
+    }
 
     const char* se_info_c_str = NULL;
     ScopedUtfChars* se_info = NULL;
@@ -664,7 +728,7 @@
         se_info = new ScopedUtfChars(env, java_se_info);
         se_info_c_str = se_info->c_str();
         if (se_info_c_str == NULL) {
-          RuntimeAbort(env, __LINE__, "se_info_c_str == NULL");
+          fail_fn("se_info_c_str == NULL");
         }
     }
     const char* se_name_c_str = NULL;
@@ -673,22 +737,21 @@
         se_name = new ScopedUtfChars(env, java_se_name);
         se_name_c_str = se_name->c_str();
         if (se_name_c_str == NULL) {
-          RuntimeAbort(env, __LINE__, "se_name_c_str == NULL");
+          fail_fn("se_name_c_str == NULL");
         }
     }
     rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
     if (rc == -1) {
-      ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
-            is_system_server, se_info_c_str, se_name_c_str);
-      RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed");
+      fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
+            is_system_server, se_info_c_str, se_name_c_str));
     }
 
     // Make it easier to debug audit logs by setting the main thread's name to the
     // nice name rather than "app_process".
-    if (se_info_c_str == NULL && is_system_server) {
+    if (se_name_c_str == NULL && is_system_server) {
       se_name_c_str = "system_server";
     }
-    if (se_info_c_str != NULL) {
+    if (se_name_c_str != NULL) {
       SetThreadName(se_name_c_str);
     }
 
@@ -701,15 +764,14 @@
     env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
                               is_system_server, is_child_zygote, instructionSet);
     if (env->ExceptionCheck()) {
-      RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
+      fail_fn("Error calling post fork hooks.");
     }
   } else if (pid > 0) {
     // the parent process
 
     // We blocked SIGCHLD prior to a fork, we unblock it here.
     if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
-      ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno));
-      RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed.");
+      fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
     }
   }
   return pid;
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 1383bbd..b19848b 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -119,14 +119,57 @@
 
 FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
 
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+  // Create a FileDescriptorInfo for a given file descriptor. Returns
+  // |NULL| if an error occurred.
+  static FileDescriptorInfo* CreateFromFd(int fd, std::string* error_msg);
+
+  // Checks whether the file descriptor associated with this object
+  // refers to the same description.
+  bool Restat() const;
+
+  bool ReopenOrDetach(std::string* error_msg) const;
+
+  const int fd;
+  const struct stat stat;
+  const std::string file_path;
+  const int open_flags;
+  const int fd_flags;
+  const int fs_flags;
+  const off_t offset;
+  const bool is_sock;
+
+ private:
+  FileDescriptorInfo(int fd);
+
+  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+                     int fd_flags, int fs_flags, off_t offset);
+
+  // Returns the locally-bound name of the socket |fd|. Returns true
+  // iff. all of the following hold :
+  //
+  // - the socket's sa_family is AF_UNIX.
+  // - the length of the path is greater than zero (i.e, not an unnamed socket).
+  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+  //   address).
+  static bool GetSocketName(const int fd, std::string* result);
+
+  bool DetachSocket(std::string* error_msg) const;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
 // static
-FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) {
   struct stat f_stat;
   // This should never happen; the zygote should always have the right set
   // of permissions required to stat all its open files.
   if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
-    PLOG(ERROR) << "Unable to stat fd " << fd;
-    return NULL;
+    *error_msg = android::base::StringPrintf("Unable to stat %d", fd);
+    return nullptr;
   }
 
   const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
@@ -134,13 +177,15 @@
   if (S_ISSOCK(f_stat.st_mode)) {
     std::string socket_name;
     if (!GetSocketName(fd, &socket_name)) {
-      return NULL;
+      *error_msg = "Unable to get socket name";
+      return nullptr;
     }
 
     if (!whitelist->IsAllowed(socket_name)) {
-      LOG(ERROR) << "Socket name not whitelisted : " << socket_name
-                 << " (fd=" << fd << ")";
-      return NULL;
+      *error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
+                                               socket_name.c_str(),
+                                               fd);
+      return nullptr;
     }
 
     return new FileDescriptorInfo(fd);
@@ -157,19 +202,22 @@
   // with the child process across forks but those should have been closed
   // before we got to this point.
   if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
-    LOG(ERROR) << "Unsupported st_mode " << f_stat.st_mode;
-    return NULL;
+    *error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode);
+    return nullptr;
   }
 
   std::string file_path;
   const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
   if (!android::base::Readlink(fd_path, &file_path)) {
-    return NULL;
+    *error_msg = android::base::StringPrintf("Could not read fd link %s: %s",
+                                             fd_path.c_str(),
+                                             strerror(errno));
+    return nullptr;
   }
 
   if (!whitelist->IsAllowed(file_path)) {
-    LOG(ERROR) << "Not whitelisted : " << file_path;
-    return NULL;
+    *error_msg = std::string("Not whitelisted : ").append(file_path);
+    return nullptr;
   }
 
   // File descriptor flags : currently on FD_CLOEXEC. We can set these
@@ -177,8 +225,11 @@
   // there won't be any races.
   const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
   if (fd_flags == -1) {
-    PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFD)";
-    return NULL;
+    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
+                                             fd,
+                                             file_path.c_str(),
+                                             strerror(errno));
+    return nullptr;
   }
 
   // File status flags :
@@ -195,8 +246,11 @@
   //   their presence and pass them in to open().
   int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
   if (fs_flags == -1) {
-    PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFL)";
-    return NULL;
+    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
+                                             fd,
+                                             file_path.c_str(),
+                                             strerror(errno));
+    return nullptr;
   }
 
   // File offset : Ignore the offset for non seekable files.
@@ -221,9 +275,9 @@
   return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
 }
 
-bool FileDescriptorInfo::ReopenOrDetach() const {
+bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
   if (is_sock) {
-    return DetachSocket();
+    return DetachSocket(error_msg);
   }
 
   // NOTE: This might happen if the file was unlinked after being opened.
@@ -232,31 +286,49 @@
   const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
 
   if (new_fd == -1) {
-    PLOG(ERROR) << "Failed open(" << file_path << ", " << open_flags << ")";
+    *error_msg = android::base::StringPrintf("Failed open(%s, %i): %s",
+                                             file_path.c_str(),
+                                             open_flags,
+                                             strerror(errno));
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
     close(new_fd);
-    PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFD, " << fd_flags << ")";
+    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
+                                             new_fd,
+                                             fd_flags,
+                                             file_path.c_str(),
+                                             strerror(errno));
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
     close(new_fd);
-    PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFL, " << fs_flags << ")";
+    *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
+                                             new_fd,
+                                             fs_flags,
+                                             file_path.c_str(),
+                                             strerror(errno));
     return false;
   }
 
   if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
     close(new_fd);
-    PLOG(ERROR) << "Failed lseek64(" << new_fd << ", SEEK_SET)";
+    *error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
+                                             new_fd,
+                                             file_path.c_str(),
+                                             strerror(errno));
     return false;
   }
 
   if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
     close(new_fd);
-    PLOG(ERROR) << "Failed dup2(" << fd << ", " << new_fd << ")";
+    *error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s",
+                                             fd,
+                                             new_fd,
+                                             file_path.c_str(),
+                                             strerror(errno));
     return false;
   }
 
@@ -332,20 +404,22 @@
   return true;
 }
 
-bool FileDescriptorInfo::DetachSocket() const {
+bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const {
   const int dev_null_fd = open("/dev/null", O_RDWR);
   if (dev_null_fd < 0) {
-    PLOG(ERROR) << "Failed to open /dev/null";
+    *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
     return false;
   }
 
   if (dup2(dev_null_fd, fd) == -1) {
-    PLOG(ERROR) << "Failed dup2 on socket descriptor " << fd;
+    *error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
+                                             fd,
+                                             strerror(errno));
     return false;
   }
 
   if (close(dev_null_fd) == -1) {
-    PLOG(ERROR) << "Failed close(" << dev_null_fd << ")";
+    *error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno));
     return false;
   }
 
@@ -353,11 +427,12 @@
 }
 
 // static
-FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
+FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
+                                                 std::string* error_msg) {
   DIR* d = opendir(kFdPath);
-  if (d == NULL) {
-    PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
-    return NULL;
+  if (d == nullptr) {
+    *error_msg = std::string("Unable to open directory ").append(kFdPath);
+    return nullptr;
   }
   int dir_fd = dirfd(d);
   dirent* e;
@@ -373,7 +448,7 @@
       continue;
     }
 
-    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+    FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
     if (info == NULL) {
       if (closedir(d) == -1) {
         PLOG(ERROR) << "Unable to close directory";
@@ -384,19 +459,21 @@
   }
 
   if (closedir(d) == -1) {
-    PLOG(ERROR) << "Unable to close directory";
-    return NULL;
+    *error_msg = "Unable to close directory";
+    return nullptr;
   }
   return new FileDescriptorTable(open_fd_map);
 }
 
-bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) {
+bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) {
   std::set<int> open_fds;
 
   // First get the list of open descriptors.
   DIR* d = opendir(kFdPath);
   if (d == NULL) {
-    PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
+    *error_msg = android::base::StringPrintf("Unable to open directory %s: %s",
+                                             kFdPath,
+                                             strerror(errno));
     return false;
   }
 
@@ -416,21 +493,21 @@
   }
 
   if (closedir(d) == -1) {
-    PLOG(ERROR) << "Unable to close directory";
+    *error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno));
     return false;
   }
 
-  return RestatInternal(open_fds);
+  return RestatInternal(open_fds, error_msg);
 }
 
 // Reopens all file descriptors that are contained in the table. Returns true
 // if all descriptors were successfully re-opened or detached, and false if an
 // error occurred.
-bool FileDescriptorTable::ReopenOrDetach() {
+bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) {
   std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
   for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
     const FileDescriptorInfo* info = it->second;
-    if (info == NULL || !info->ReopenOrDetach()) {
+    if (info == NULL || !info->ReopenOrDetach(error_msg)) {
       return false;
     }
   }
@@ -443,7 +520,7 @@
     : open_fd_map_(map) {
 }
 
-bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
+bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) {
   bool error = false;
 
   // Iterate through the list of file descriptors we've already recorded
@@ -451,6 +528,8 @@
   //
   // (a) they continue to be open.
   // (b) they refer to the same file.
+  //
+  // We'll only store the last error message.
   std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
   while (it != open_fd_map_.end()) {
     std::set<int>::const_iterator element = open_fds.find(it->first);
@@ -471,7 +550,7 @@
         // The file descriptor refers to a different description. We must
         // update our entry in the table.
         delete it->second;
-        it->second = FileDescriptorInfo::CreateFromFd(*element);
+        it->second = FileDescriptorInfo::CreateFromFd(*element, error_msg);
         if (it->second == NULL) {
           // The descriptor no longer no longer refers to a whitelisted file.
           // We flag an error and remove it from the list of files we're
@@ -506,7 +585,7 @@
     std::set<int>::const_iterator it;
     for (it = open_fds.begin(); it != open_fds.end(); ++it) {
       const int fd = (*it);
-      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+      FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
       if (info == NULL) {
         // A newly opened file is not on the whitelist. Flag an error and
         // continue.
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index a39e387..a3570d7 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -28,6 +28,8 @@
 
 #include <android-base/macros.h>
 
+class FileDescriptorInfo;
+
 // Whitelist of open paths that the zygote is allowed to keep open.
 //
 // In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
@@ -66,49 +68,6 @@
   DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
 };
 
-// Keeps track of all relevant information (flags, offset etc.) of an
-// open zygote file descriptor.
-class FileDescriptorInfo {
- public:
-  // Create a FileDescriptorInfo for a given file descriptor. Returns
-  // |NULL| if an error occurred.
-  static FileDescriptorInfo* CreateFromFd(int fd);
-
-  // Checks whether the file descriptor associated with this object
-  // refers to the same description.
-  bool Restat() const;
-
-  bool ReopenOrDetach() const;
-
-  const int fd;
-  const struct stat stat;
-  const std::string file_path;
-  const int open_flags;
-  const int fd_flags;
-  const int fs_flags;
-  const off_t offset;
-  const bool is_sock;
-
- private:
-  FileDescriptorInfo(int fd);
-
-  FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
-                     int fd_flags, int fs_flags, off_t offset);
-
-  // Returns the locally-bound name of the socket |fd|. Returns true
-  // iff. all of the following hold :
-  //
-  // - the socket's sa_family is AF_UNIX.
-  // - the length of the path is greater than zero (i.e, not an unnamed socket).
-  // - the first byte of the path isn't zero (i.e, not a socket with an abstract
-  //   address).
-  static bool GetSocketName(const int fd, std::string* result);
-
-  bool DetachSocket() const;
-
-  DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
-};
-
 // A FileDescriptorTable is a collection of FileDescriptorInfo objects
 // keyed by their FDs.
 class FileDescriptorTable {
@@ -116,19 +75,20 @@
   // Creates a new FileDescriptorTable. This function scans
   // /proc/self/fd for the list of open file descriptors and collects
   // information about them. Returns NULL if an error occurs.
-  static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore);
+  static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
+                                     std::string* error_msg);
 
-  bool Restat(const std::vector<int>& fds_to_ignore);
+  bool Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg);
 
   // Reopens all file descriptors that are contained in the table. Returns true
   // if all descriptors were successfully re-opened or detached, and false if an
   // error occurred.
-  bool ReopenOrDetach();
+  bool ReopenOrDetach(std::string* error_msg);
 
  private:
   FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
 
-  bool RestatInternal(std::set<int>& open_fds);
+  bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
 
   static int ParseFd(dirent* e, int dir_fd);
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4ea2d10..20a5afe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -86,6 +86,7 @@
     <protected-broadcast android:name="android.intent.action.OVERLAY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.USER_ACTIVITY_NOTIFICATION" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -177,6 +178,8 @@
     <protected-broadcast
         android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
+        android:name="android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a49c6b1..3dd950e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2093,6 +2093,9 @@
     <!-- Whether UI for multi user should be shown -->
     <bool name="config_enableMultiUserUI">false</bool>
 
+    <!-- Whether the new Auto Selection Network UI should be shown -->
+    <bool name="config_enableNewAutoSelectNetworkUI">false</bool>
+
     <!-- If true, then we do not ask user for permission for apps to connect to USB devices.
          Do not set this to true for production devices. Doing so will cause you to fail CTS. -->
     <bool name="config_disableUsbPermissionDialogs">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dc14b23..9e38efe 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -138,25 +138,28 @@
     <string name="CLIRPermanent">You can\'t change the caller ID setting.</string>
 
     <!-- Notification title to tell the user that data service is blocked by access control. -->
-    <string name="RestrictedOnDataTitle">No data service</string>
+    <string name="RestrictedOnDataTitle">No mobile data service</string>
     <!-- Notification title to tell the user that emergency calling is blocked by access control. -->
-    <string name="RestrictedOnEmergencyTitle">No emergency calling</string>
+    <string name="RestrictedOnEmergencyTitle">Emergency calling unavailable</string>
     <!-- Notification title to tell the user that normal service is blocked by access control. -->
     <string name="RestrictedOnNormalTitle">No voice service</string>
     <!-- Notification title to tell the user that all emergency and normal voice services are blocked by access control. -->
-    <string name="RestrictedOnAllVoiceTitle">No voice/emergency service</string>
+    <string name="RestrictedOnAllVoiceTitle">No voice service or emergency calling</string>
 
     <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. -->
-    <string name="RestrictedStateContent">Temporarily not offered by the mobile network at your location</string>
+    <string name="RestrictedStateContent">Temporarily turned off by your carrier</string>
+
+    <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control when multiple SIMs are active. -->
+    <string name="RestrictedStateContentMsimTemplate">Temporarily turned off by your carrier for SIM <xliff:g id="simNumber" example="1">%d</xliff:g></string>
 
     <!-- Displayed to tell the user that they should switch their network preference. -->
-    <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
+    <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach mobile network</string>
     <!-- Displayed to tell the user that they should switch their network preference. -->
-    <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; Internet &gt; Mobile networks &gt; Preferred network type."</string>
+    <string name="NetworkPreferenceSwitchSummary">Try changing preferred network. Tap to change.</string>
     <!-- Displayed to tell the user that emergency calls might not be available. -->
-    <string name="EmergencyCallWarningTitle">Wi\u2011Fi calling is active</string>
+    <string name="EmergencyCallWarningTitle">Emergency calling unavailable</string>
     <!-- Displayed to tell the user that emergency calls might not be available. -->
-    <string name="EmergencyCallWarningSummary">Emergency calls require a mobile network.</string>
+    <string name="EmergencyCallWarningSummary">Can\u2019t make emergency calls over Wi\u2011Fi</string>
 
     <!-- Telephony notification channel name for a channel containing network alert notifications. -->
     <string name="notification_channel_network_alert">Alerts</string>
@@ -215,14 +218,14 @@
     <string name="roamingTextSearching">Searching for Service</string>
 
     <!-- Displayed when WFC registration fails -->
-    <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
+    <string name="wfcRegErrorTitle">Couldn\u2019t set up Wi\u2011Fi calling</string>
     <!-- WFC Operator Error Messages showed as alerts -->
     <string-array name="wfcOperatorErrorAlertMessages">
         <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
     </string-array>
     <!-- WFC Operator Error Messages showed as notifications -->
     <string-array name="wfcOperatorErrorNotificationMessages">
-        <item>Register with your carrier (Error code: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g>)</item>
+        <item>Issue registering Wi\u2011Fi calling with your carrier: <xliff:g id="code" example="REG09 - No 911 Address">%1$s</xliff:g></item>
     </string-array>
     <!-- Template for showing mobile network operator name while WFC is active -->
     <string-array name="wfcSpnFormats">
@@ -4437,14 +4440,14 @@
 
     <!-- Displayed when the USSD/SS request is modified by STK CC to a
     different request. This will be displayed in a toast. -->
-    <string name="stk_cc_ussd_to_dial">USSD request is modified to DIAL request.</string>
-    <string name="stk_cc_ussd_to_ss">USSD request is modified to SS request.</string>
-    <string name="stk_cc_ussd_to_ussd">USSD request is modified to new USSD request.</string>
-    <string name="stk_cc_ussd_to_dial_video">USSD request is modified to Video DIAL request.</string>
-    <string name="stk_cc_ss_to_dial">SS request is modified to DIAL request.</string>
-    <string name="stk_cc_ss_to_dial_video">SS request is modified to Video DIAL request.</string>
-    <string name="stk_cc_ss_to_ussd">SS request is modified to USSD request.</string>
-    <string name="stk_cc_ss_to_ss">SS request is modified to new SS request.</string>
+    <string name="stk_cc_ussd_to_dial">USSD request changed to regular call</string>
+    <string name="stk_cc_ussd_to_ss">USSD request changed to SS request</string>
+    <string name="stk_cc_ussd_to_ussd">Changed to new USSD request</string>
+    <string name="stk_cc_ussd_to_dial_video">USSD request changed to video call</string>
+    <string name="stk_cc_ss_to_dial">SS request changed to regular call</string>
+    <string name="stk_cc_ss_to_dial_video">SS request changed to video call</string>
+    <string name="stk_cc_ss_to_ussd">SS request changed to USSD request</string>
+    <string name="stk_cc_ss_to_ss">Changed to new SS request</string>
 
     <!-- Content description of the work profile icon in the notification. -->
     <string name="notification_work_profile_content_description">Work profile</string>
@@ -4682,11 +4685,17 @@
     <!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for others -->
     <string name="etws_primary_default_message_others"></string>
 
-    <!-- Title of notification when UE fails to register network with MM reject cause code. -->
-    <string name="mmcc_authentication_reject">SIM not allowed</string>
-    <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned</string>
-    <string name="mmcc_illegal_ms">SIM not allowed</string>
-    <string name="mmcc_illegal_me">Phone not allowed</string>
+    <!-- Title of notification when UE fails CS registration network with MM reject cause code from network. -->
+    <string name="mmcc_authentication_reject">SIM not allowed for voice</string>
+    <string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned for voice</string>
+    <string name="mmcc_illegal_ms">SIM not allowed for voice</string>
+    <string name="mmcc_illegal_me">Phone not allowed for voice</string>
+
+    <!-- Title of notification when UE fails to register network with MM reject cause code when multiple SIMs are active. -->
+    <string name="mmcc_authentication_reject_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+    <string name="mmcc_imsi_unknown_in_hlr_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not provisioned</string>
+    <string name="mmcc_illegal_ms_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
+    <string name="mmcc_illegal_me_msim_template">SIM <xliff:g id="simNumber" example="1">%d</xliff:g> not allowed</string>
 
     <!-- Popup window default title to be read by a screen reader-->
     <string name="popup_window_default_title">Popup Window</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ad5af95..e52f0f8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -311,6 +311,7 @@
   <java-symbol type="bool" name="config_forceDefaultOrientation" />
   <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
   <java-symbol type="bool" name="config_enableMultiUserUI"/>
+  <java-symbol type="bool" name="config_enableNewAutoSelectNetworkUI"/>
   <java-symbol type="bool" name="config_disableUsbPermissionDialogs"/>
   <java-symbol type="bool" name="config_hasRecents" />
   <java-symbol type="bool" name="config_windowShowCircularMask" />
@@ -546,6 +547,7 @@
   <java-symbol type="string" name="RestrictedOnEmergencyTitle" />
   <java-symbol type="string" name="RestrictedOnNormalTitle" />
   <java-symbol type="string" name="RestrictedStateContent" />
+  <java-symbol type="string" name="RestrictedStateContentMsimTemplate" />
   <java-symbol type="string" name="notification_channel_network_alert" />
   <java-symbol type="string" name="notification_channel_call_forward" />
   <java-symbol type="string" name="notification_channel_emergency_callback" />
@@ -1955,6 +1957,10 @@
   <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr" />
   <java-symbol type="string" name="mmcc_illegal_ms" />
   <java-symbol type="string" name="mmcc_illegal_me" />
+  <java-symbol type="string" name="mmcc_authentication_reject_msim_template" />
+  <java-symbol type="string" name="mmcc_imsi_unknown_in_hlr_msim_template" />
+  <java-symbol type="string" name="mmcc_illegal_ms_msim_template" />
+  <java-symbol type="string" name="mmcc_illegal_me_msim_template" />
   <java-symbol type="string" name="notification_listener_binding_label" />
   <java-symbol type="string" name="vr_listener_binding_label" />
   <java-symbol type="string" name="condition_provider_service_binding_label" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index f5b350b..8947bf0 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
     <!-- Arab Emirates -->
-    <shortcode country="ae" free="3214|1017" />
+    <shortcode country="ae" pattern="\\d{1,5}" free="3214|1017" />
 
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -80,7 +80,7 @@
     <shortcode country="cn" premium="1066.*" free="1065.*" />
 
     <!-- Colombia: 1-6 digits (not confirmed) -->
-    <shortcode country="co" pattern="\\d{1,6}" free="890350" />
+    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160" />
 
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
@@ -126,7 +126,7 @@
     <shortcode country="gr" pattern="\\d{5}" premium="54\\d{3}|19[0-5]\\d{2}" free="116\\d{3}|12115" />
 
     <!-- Croatia -->
-    <shortcode country="hr" free="13062" />
+    <shortcode country="hr" pattern="\\d{1,5}" free="13062" />
 
     <!-- Hungary: 4 or 10 digits starting with 1 or 0, plus EU:
          http://clients.txtnation.com/entries/209633-hungary-premium-sms-short-code-regulations -->
@@ -150,7 +150,7 @@
     <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503" standard="43\\d{3}" />
 
     <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
-    <shortcode country="jp" free="8083" />
+    <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
 
     <!-- Kenya: 5 digits, known premium codes listed -->
     <shortcode country="ke" pattern="\\d{5}" free="21725" />
@@ -165,10 +165,10 @@
     <shortcode country="kz" pattern="\\d{4}" premium="335[02]|4161|444[469]|77[2359]0|8444|919[3-5]|968[2-5]" />
 
     <!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006" />
+    <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991" />
 
     <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
-    <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399" />
+    <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
 
     <!-- Luxembourg: 5 digits, 6xxxx, plus EU:
          http://www.luxgsm.lu/assets/files/filepage/file_1253803400.pdf -->
@@ -199,10 +199,10 @@
     <shortcode country="pe" pattern="\\d{4,5}" free="9963" />
 
     <!-- Philippines -->
-    <shortcode country="ph" free="2147|5495|5496" />
+    <shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
 
     <!-- Pakistan -->
-    <shortcode country="pk" free="2057" />
+    <shortcode country="pk" pattern="\\d{1,5}" free="2057" />
 
     <!-- Poland: 4-5 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="pl" pattern="\\d{4,5}" premium="74240|79(?:10|866)|92525" free="116\\d{3}|8012|80921" />
@@ -224,7 +224,7 @@
     <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" free="6954|8501" standard="2037|2044"/>
 
     <!-- Saudi Arabia -->
-    <shortcode country="sa" free="8145" />
+    <shortcode country="sa" pattern="\\d{1,5}" free="8145" />
 
     <!-- Sweden: 5 digits (72xxx), plus EU: http://www.viatel.se/en/premium-sms/ -->
     <shortcode country="se" premium="72\\d{3}" free="116\\d{3}" />
@@ -240,13 +240,13 @@
     <shortcode country="sk" premium="\\d{4}" free="116\\d{3}|8000" />
 
     <!-- Thailand: 4186001 used by AIS_TH_DCB -->
-    <shortcode country="th" free="4186001" />
+    <shortcode country="th" pattern="\\d{1,5}" free="4186001" />
 
     <!-- Tajikistan: 4 digits, known premium codes listed -->
     <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
 
     <!-- Turkey -->
-    <shortcode country="tr" free="7529|5528|6493" />
+    <shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493" />
 
     <!-- Ukraine: 4 digits, known premium codes listed -->
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 757a70c..8a7c586 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -64,6 +64,7 @@
                     Settings.System.NOTIFICATION_LIGHT_PULSE, // candidate for backup?
                     Settings.System.NOTIFICATION_SOUND_CACHE, // internal cache
                     Settings.System.POINTER_LOCATION, // backup candidate?
+                    Settings.System.DEBUG_ENABLE_ENHANCED_CALL_BLOCKING, // used for testing only
                     Settings.System.RINGTONE_CACHE, // internal cache
                     Settings.System.SETUP_WIZARD_HAS_RUN, // Only used by SuW
                     Settings.System.SHOW_GTALK_SERVICE_STATUS, // candidate for backup?
@@ -117,6 +118,7 @@
                     Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
                     Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
+                    Settings.Global.BLE_SCAN_BACKGROUND_MODE,
                     Settings.Global.BLUETOOTH_A2DP_SINK_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SRC_PRIORITY_PREFIX,
                     Settings.Global.BLUETOOTH_A2DP_SUPPORTS_OPTIONAL_CODECS_PREFIX,
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 282b001..933e54e 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -16,6 +16,9 @@
 
 package android.os;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 import junit.framework.TestCase;
 
 import android.os.SystemProperties;
@@ -141,4 +144,48 @@
         } catch (NullPointerException npe) {
         }
     }
+
+    @SmallTest
+    public void testCallbacks() {
+        // Latches are not really necessary, but are easy to use.
+        final CountDownLatch wait1 = new CountDownLatch(1);
+        final CountDownLatch wait2 = new CountDownLatch(1);
+
+        Runnable r1 = new Runnable() {
+            boolean done = false;
+            @Override
+            public void run() {
+                if (done) {
+                    return;
+                }
+                done = true;
+
+                wait1.countDown();
+                throw new RuntimeException("test");
+            }
+        };
+
+        Runnable r2 = new Runnable() {
+            @Override
+            public void run() {
+                wait2.countDown();
+            }
+        };
+
+        SystemProperties.addChangeCallback(r1);
+        SystemProperties.addChangeCallback(r2);
+
+        SystemProperties.reportSyspropChanged();
+
+        try {
+            assertTrue(wait1.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("InterruptedException");
+        }
+        try {
+            assertTrue(wait2.await(5, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("InterruptedException");
+        }
+    }
 }
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 1d46d42..bacddf14 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -25,55 +25,67 @@
   <hidden-api-whitelisted-app package="android.car.input.service" />
   <hidden-api-whitelisted-app package="android.car.usb.handler" />
   <hidden-api-whitelisted-app package="android.ext.services" />
-  <hidden-api-whitelisted-app package="android.ext.shared" />
+  <hidden-api-whitelisted-app package="com.android.apps.tag" />
   <hidden-api-whitelisted-app package="com.android.backupconfirm" />
+  <hidden-api-whitelisted-app package="com.android.basicsmsreceiver" />
   <hidden-api-whitelisted-app package="com.android.bluetooth" />
   <hidden-api-whitelisted-app package="com.android.bluetoothdebug" />
   <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" />
+  <hidden-api-whitelisted-app package="com.android.bookmarkprovider" />
   <hidden-api-whitelisted-app package="com.android.calllogbackup" />
+  <hidden-api-whitelisted-app package="com.android.camera" />
   <hidden-api-whitelisted-app package="com.android.captiveportallogin" />
   <hidden-api-whitelisted-app package="com.android.car" />
+  <hidden-api-whitelisted-app package="com.android.car.dialer" />
   <hidden-api-whitelisted-app package="com.android.car.hvac" />
   <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" />
   <hidden-api-whitelisted-app package="com.android.car.media" />
   <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" />
+  <hidden-api-whitelisted-app package="com.android.car.messenger" />
+  <hidden-api-whitelisted-app package="com.android.car.overview" />
   <hidden-api-whitelisted-app package="com.android.car.radio" />
   <hidden-api-whitelisted-app package="com.android.car.settings" />
+  <hidden-api-whitelisted-app package="com.android.car.stream" />
   <hidden-api-whitelisted-app package="com.android.car.systemupdater" />
   <hidden-api-whitelisted-app package="com.android.car.trust" />
   <hidden-api-whitelisted-app package="com.android.carrierconfig" />
   <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" />
   <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" />
   <hidden-api-whitelisted-app package="com.android.certinstaller" />
+  <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
   <hidden-api-whitelisted-app package="com.android.customlocale2" />
   <hidden-api-whitelisted-app package="com.android.defcontainer" />
   <hidden-api-whitelisted-app package="com.android.documentsui" />
+  <hidden-api-whitelisted-app package="com.android.dreams.basic" />
   <hidden-api-whitelisted-app package="com.android.egg" />
-  <hidden-api-whitelisted-app package="com.android.email.policy" />
   <hidden-api-whitelisted-app package="com.android.emergency" />
   <hidden-api-whitelisted-app package="com.android.externalstorage" />
   <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" />
   <hidden-api-whitelisted-app package="com.android.gallery" />
   <hidden-api-whitelisted-app package="com.android.hotspot2" />
-  <hidden-api-whitelisted-app package="com.android.inputdevices" />
   <hidden-api-whitelisted-app package="com.android.keychain" />
   <hidden-api-whitelisted-app package="com.android.location.fused" />
   <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
   <hidden-api-whitelisted-app package="com.android.mms.service" />
   <hidden-api-whitelisted-app package="com.android.mtp" />
+  <hidden-api-whitelisted-app package="com.android.musicfx" />
   <hidden-api-whitelisted-app package="com.android.nfc" />
   <hidden-api-whitelisted-app package="com.android.osu" />
   <hidden-api-whitelisted-app package="com.android.packageinstaller" />
   <hidden-api-whitelisted-app package="com.android.pacprocessor" />
   <hidden-api-whitelisted-app package="com.android.phone" />
   <hidden-api-whitelisted-app package="com.android.pmc" />
+  <hidden-api-whitelisted-app package="com.android.printservice.recommendation" />
+  <hidden-api-whitelisted-app package="com.android.printspooler" />
   <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
+  <hidden-api-whitelisted-app package="com.android.providers.calendar" />
   <hidden-api-whitelisted-app package="com.android.providers.contacts" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" />
   <hidden-api-whitelisted-app package="com.android.providers.media" />
   <hidden-api-whitelisted-app package="com.android.providers.settings" />
   <hidden-api-whitelisted-app package="com.android.providers.telephony" />
+  <hidden-api-whitelisted-app package="com.android.providers.tv" />
   <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
   <hidden-api-whitelisted-app package="com.android.provision" />
   <hidden-api-whitelisted-app package="com.android.proxyhandler" />
@@ -85,10 +97,15 @@
   <hidden-api-whitelisted-app package="com.android.settings" />
   <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" />
   <hidden-api-whitelisted-app package="com.android.shell" />
+  <hidden-api-whitelisted-app package="com.android.smspush" />
+  <hidden-api-whitelisted-app package="com.android.spare_parts" />
+  <hidden-api-whitelisted-app package="com.android.statementservice" />
   <hidden-api-whitelisted-app package="com.android.stk" />
+  <hidden-api-whitelisted-app package="com.android.storagemanager" />
   <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" />
   <hidden-api-whitelisted-app package="com.android.systemui" />
-  <hidden-api-whitelisted-app package="com.android.systemui.theme.dark" />
+  <hidden-api-whitelisted-app package="com.android.systemui.plugins" />
+  <hidden-api-whitelisted-app package="com.android.terminal" />
   <hidden-api-whitelisted-app package="com.android.timezone.updater" />
   <hidden-api-whitelisted-app package="com.android.traceur" />
   <hidden-api-whitelisted-app package="com.android.tv.settings" />
@@ -97,5 +114,5 @@
   <hidden-api-whitelisted-app package="com.android.wallpaperbackup" />
   <hidden-api-whitelisted-app package="com.android.wallpapercropper" />
   <hidden-api-whitelisted-app package="com.googlecode.android_scripting" />
+  <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
 </config>
-
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index b767046..11360a1 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -62,7 +62,7 @@
         "uniform mediump vec4 roundRectInnerRectLTWH;\n"
         "uniform mediump float roundRectRadius;\n";
 const char* gVS_Header_Varyings_HasTexture =
-        "varying vec2 outTexCoords;\n";
+        "varying highp vec2 outTexCoords;\n";
 const char* gVS_Header_Varyings_HasColors =
         "varying vec4 outColors;\n";
 const char* gVS_Header_Varyings_HasVertexAlpha =
diff --git a/packages/CaptivePortalLogin/Android.mk b/packages/CaptivePortalLogin/Android.mk
index e6e0ad3..8a96b16 100644
--- a/packages/CaptivePortalLogin/Android.mk
+++ b/packages/CaptivePortalLogin/Android.mk
@@ -2,6 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 services.net
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
index 2324593..c292323 100644
--- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
+++ b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml
@@ -27,12 +27,17 @@
             android:layout_height="wrap_content" />
       </FrameLayout>
 
-      <WebView
-          android:id="@+id/webview"
-          android:layout_width="match_parent"
-          android:layout_height="match_parent"
-          android:layout_alignParentBottom="false"
-          android:layout_alignParentRight="false" />
+      <android.support.v4.widget.SwipeRefreshLayout
+            android:id="@+id/swipe_refresh"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        <WebView
+              android:id="@+id/webview"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:layout_alignParentBottom="false"
+              android:layout_alignParentRight="false" />
+      </android.support.v4.widget.SwipeRefreshLayout>
 
     </LinearLayout>
 </FrameLayout>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index e13aba7..2a1bbed 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -30,10 +30,12 @@
 import android.net.NetworkRequest;
 import android.net.Proxy;
 import android.net.Uri;
+import android.net.dns.ResolvUtil;
 import android.net.http.SslError;
 import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TypedValue;
@@ -41,6 +43,7 @@
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
+import android.webkit.CookieManager;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
@@ -88,6 +91,7 @@
     private ConnectivityManager mCm;
     private boolean mLaunchBrowser = false;
     private MyWebViewClient mWebViewClient;
+    private SwipeRefreshLayout mSwipeRefreshLayout;
     // Ensures that done() happens once exactly, handling concurrent callers with atomic operations.
     private final AtomicBoolean isDone = new AtomicBoolean(false);
 
@@ -115,6 +119,8 @@
 
         // Also initializes proxy system properties.
         mCm.bindProcessToNetwork(mNetwork);
+        mCm.setProcessDefaultNetworkForHostResolution(
+                ResolvUtil.getNetworkWithUseLocalNameserversFlag(mNetwork));
 
         // Proxy system properties must be initialized before setContentView is called because
         // setContentView initializes the WebView logic which in turn reads the system properties.
@@ -145,6 +151,7 @@
 
         final WebView webview = getWebview();
         webview.clearCache(true);
+        CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true);
         WebSettings webSettings = webview.getSettings();
         webSettings.setJavaScriptEnabled(true);
         webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
@@ -159,6 +166,13 @@
         // Start initial page load so WebView finishes loading proxy settings.
         // Actual load of mUrl is initiated by MyWebViewClient.
         webview.loadData("", "text/html", null);
+
+        mSwipeRefreshLayout = findViewById(R.id.swipe_refresh);
+        mSwipeRefreshLayout.setOnRefreshListener(() -> {
+                webview.reload();
+                mSwipeRefreshLayout.setRefreshing(true);
+            });
+
     }
 
     // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
@@ -393,6 +407,7 @@
         public void onPageFinished(WebView view, String url) {
             mPagesLoaded++;
             getProgressBar().setVisibility(View.INVISIBLE);
+            mSwipeRefreshLayout.setRefreshing(false);
             if (mPagesLoaded == 1) {
                 // Now that WebView has loaded at least one page we know it has read in the proxy
                 // settings.  Now prompt the WebView read the Network-specific proxy settings.
diff --git a/packages/CarrierDefaultApp/Android.mk b/packages/CarrierDefaultApp/Android.mk
index df88afd..5068b3b 100644
--- a/packages/CarrierDefaultApp/Android.mk
+++ b/packages/CarrierDefaultApp/Android.mk
@@ -9,6 +9,8 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_CERTIFICATE := platform
 
+LOCAL_STATIC_JAVA_LIBRARIES := services.net
+
 include $(BUILD_PACKAGE)
 
 # This finds and builds the test apk as well, so a single make does both.
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 95ec83d..7479d9a 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -32,6 +32,7 @@
 import android.net.Proxy;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.net.dns.ResolvUtil;
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
@@ -115,6 +116,8 @@
             requestNetworkForCaptivePortal();
         } else {
             mCm.bindProcessToNetwork(mNetwork);
+            mCm.setProcessDefaultNetworkForHostResolution(
+                    ResolvUtil.getNetworkWithUseLocalNameserversFlag(mNetwork));
             // Start initial page load so WebView finishes loading proxy settings.
             // Actual load of mUrl is initiated by MyWebViewClient.
             mWebView.loadData("", "text/html", null);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 428f0b8..61b88a5 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -456,10 +456,10 @@
     <string name="keep_screen_on">Stay awake</string>
     <!-- setting Checkbox summary whether to keep the screen on when plugged in  -->
     <string name="keep_screen_on_summary">Screen will never sleep while charging</string>
-    <!-- Setting Checkbox title whether to enable bluetooth HCI snoop log -->
+    <!-- Setting Checkbox title whether to enable Bluetooth HCI snoop log -->
     <string name="bt_hci_snoop_log">Enable Bluetooth HCI snoop log</string>
-    <!-- setting Checkbox summary whether to capture all bluetooth HCI packets in a file -->
-    <string name="bt_hci_snoop_log_summary">Capture all bluetooth HCI packets in a file</string>
+    <!-- setting Checkbox summary whether to capture all Bluetooth HCI packets in a file -->
+    <string name="bt_hci_snoop_log_summary">Capture all Bluetooth HCI packets in a file (Toggle Bluetooth after changing this setting)</string>
     <!-- setting Checkbox title whether to enable OEM unlock [CHAR_LIMIT=35] -->
     <string name="oem_unlock_enable">OEM unlocking</string>
     <!-- setting Checkbox summary whether to enable OEM unlock [CHAR_LIMIT=50] -->
@@ -501,30 +501,30 @@
     <!-- UI debug setting: Select Bluetooth AVRCP Version -->
     <string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Codec -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
     <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec -->
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Sample Rate Selection -->
     <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection -->
     <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Bits Per Sample</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Channel Mode Selection -->
     <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Channel Mode</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
+    <!-- UI debug setting: Trigger Bluetooth Audio LDAC Playback Quality Selection -->
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
     <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Trigger Bluetooth Audio LDAC Codec\u000ASelection: Playback Quality</string>
 
     <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
     <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 784c714..b74b2cd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -115,6 +116,8 @@
                    new ActiveDeviceChangedHandler());
         addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
                    new ActiveDeviceChangedHandler());
+        addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
+                   new ActiveDeviceChangedHandler());
 
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
@@ -434,6 +437,8 @@
                 bluetoothProfile = BluetoothProfile.A2DP;
             } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
                 bluetoothProfile = BluetoothProfile.HEADSET;
+            } else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) {
+                bluetoothProfile = BluetoothProfile.HEARING_AID;
             } else {
                 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
                 return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f6ec6a8..6c068ff 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -109,6 +109,7 @@
     // Active device state
     private boolean mIsActiveDeviceA2dp = false;
     private boolean mIsActiveDeviceHeadset = false;
+    private boolean mIsActiveDeviceHearingAid = false;
 
     /**
      * Describes the current device and profile for logging.
@@ -416,6 +417,36 @@
         }
     }
 
+    /**
+     * Set this device as active device
+     * @return true if at least one profile on this device is set to active, false otherwise
+     */
+    public boolean setActive() {
+        boolean result = false;
+        A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
+        if (a2dpProfile != null && isConnectedProfile(a2dpProfile)) {
+            if (a2dpProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: A2DP active device=" + this);
+                result = true;
+            }
+        }
+        HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
+        if ((headsetProfile != null) && isConnectedProfile(headsetProfile)) {
+            if (headsetProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: Headset active device=" + this);
+                result = true;
+            }
+        }
+        HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+        if ((hearingAidProfile != null) && isConnectedProfile(hearingAidProfile)) {
+            if (hearingAidProfile.setActiveDevice(getDevice())) {
+                Log.i(TAG, "OnPreferenceClickListener: Hearing Aid active device=" + this);
+                result = true;
+            }
+        }
+        return result;
+    }
+
     void refreshName() {
         fetchName();
         dispatchAttributesChanged();
@@ -478,6 +509,10 @@
             changed = (mIsActiveDeviceHeadset != isActive);
             mIsActiveDeviceHeadset = isActive;
             break;
+        case BluetoothProfile.HEARING_AID:
+            changed = (mIsActiveDeviceHearingAid != isActive);
+            mIsActiveDeviceHearingAid = isActive;
+            break;
         default:
             Log.w(TAG, "onActiveDeviceChanged: unknown profile " + bluetoothProfile +
                     " isActive " + isActive);
@@ -501,6 +536,8 @@
                 return mIsActiveDeviceA2dp;
             case BluetoothProfile.HEADSET:
                 return mIsActiveDeviceHeadset;
+            case BluetoothProfile.HEARING_AID:
+                return mIsActiveDeviceHearingAid;
             default:
                 Log.w(TAG, "getActiveDevice: unknown profile " + bluetoothProfile);
                 break;
@@ -592,6 +629,10 @@
         if (headsetProfile != null) {
             mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
         }
+        HearingAidProfile hearingAidProfile = mProfileManager.getHearingAidProfile();
+        if (hearingAidProfile != null) {
+            mIsActiveDeviceHearingAid = hearingAidProfile.isActiveDevice(mDevice);
+        }
     }
 
     /**
@@ -922,6 +963,7 @@
         boolean profileConnected = false;       // at least one profile is connected
         boolean a2dpNotConnected = false;       // A2DP is preferred but not connected
         boolean hfpNotConnected = false;    // HFP is preferred but not connected
+        boolean hearingAidNotConnected = false; // Hearing Aid is preferred but not connected
 
         for (LocalBluetoothProfile profile : getProfiles()) {
             int connectionStatus = getProfileConnectionState(profile);
@@ -943,6 +985,8 @@
                         } else if ((profile instanceof HeadsetProfile) ||
                                    (profile instanceof HfpClientProfile)) {
                             hfpNotConnected = true;
+                        } else if (profile instanceof  HearingAidProfile) {
+                            hearingAidNotConnected = true;
                         }
                     }
                     break;
@@ -975,6 +1019,10 @@
                 activeDeviceString = activeDeviceStringsArray[3]; // Active for Phone only
             }
         }
+        if (!hearingAidNotConnected && mIsActiveDeviceHearingAid) {
+            activeDeviceString = activeDeviceStringsArray[1];
+            return mContext.getString(R.string.bluetooth_connected, activeDeviceString);
+        }
 
         if (profileConnected) {
             if (a2dpNotConnected && hfpNotConnected) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index 8f9e4635..920500f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -134,6 +134,17 @@
         return mService.getConnectionState(device);
     }
 
+    public boolean setActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        mService.setActiveDevice(device);
+        return true;
+    }
+
+    public boolean isActiveDevice(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.isActiveDevice(device);
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index d6b2006..2f5eead 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -71,6 +71,8 @@
     @Mock
     private PanProfile mPanProfile;
     @Mock
+    private HearingAidProfile mHearingAidProfile;
+    @Mock
     private BluetoothDevice mDevice1;
     @Mock
     private BluetoothDevice mDevice2;
@@ -100,6 +102,7 @@
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
+        when(mHearingAidProfile.isProfileReady()).thenReturn(true);
         mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
     }
 
@@ -280,4 +283,63 @@
         assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
         assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
     }
+
+    /**
+     * Test to verify onActiveDeviceChanged() with A2DP and Hearing Aid.
+     */
+    @Test
+    public void testOnActiveDeviceChanged_withA2dpAndHearingAid() {
+        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice1);
+        assertThat(cachedDevice1).isNotNull();
+        CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mLocalAdapter,
+                mLocalProfileManager, mDevice2);
+        assertThat(cachedDevice2).isNotNull();
+
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+        // Connect device1 for A2DP and HFP and device2 for Hearing Aid
+        cachedDevice1.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice1.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+
+        // Verify that both devices are connected and none is Active
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+        // The first device is active for A2DP and HFP
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1, BluetoothProfile.HEADSET);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isTrue();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+
+        // The second device is active for Hearing Aid and the first device is not active
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.A2DP);
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice2, BluetoothProfile.HEARING_AID);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+
+        // No active device for Hearing Aid
+        mCachedDeviceManager.onActiveDeviceChanged(null, BluetoothProfile.HEARING_AID);
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice1.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.A2DP)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEADSET)).isFalse();
+        assertThat(cachedDevice2.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 2c91d5a..6593cbc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -61,6 +61,8 @@
     @Mock
     private PanProfile mPanProfile;
     @Mock
+    private HearingAidProfile mHearingAidProfile;
+    @Mock
     private BluetoothDevice mDevice;
     private CachedBluetoothDevice mCachedDevice;
     private Context mContext;
@@ -75,6 +77,7 @@
         when(mHfpProfile.isProfileReady()).thenReturn(true);
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
+        when(mHearingAidProfile.isProfileReady()).thenReturn(true);
         mCachedDevice = spy(
                 new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
         doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -209,6 +212,23 @@
     }
 
     @Test
+    public void testGetConnectionSummary_testSingleProfileActiveDeviceHearingAid() {
+        // Test without battery level
+        // Set Hearing Aid profile to be connected and test connection state summary
+        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected");
+
+        // Set device as Active for Hearing Aid and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
+        assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Connected, active");
+
+        // Set Hearing Aid profile to be disconnected and test connection state summary
+        mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
+        mCachedDevice.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.getConnectionSummary()).isNull();
+    }
+
+    @Test
     public void testGetConnectionSummary_testMultipleProfilesActiveDevice() {
         // Test without battery level
         // Set A2DP and HFP profiles to be connected and test connection state summary
@@ -299,4 +319,24 @@
         // Verify new alias is returned on getName
         assertThat(cachedBluetoothDevice.getName()).isEqualTo(DEVICE_ALIAS_NEW);
     }
+
+    @Test
+    public void testSetActive() {
+        when(mProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
+        when(mProfileManager.getHeadsetProfile()).thenReturn(mHfpProfile);
+        when(mA2dpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+        when(mHfpProfile.setActiveDevice(any(BluetoothDevice.class))).thenReturn(true);
+
+        assertThat(mCachedDevice.setActive()).isFalse();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+        assertThat(mCachedDevice.setActive()).isTrue();
+
+        mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(mCachedDevice.setActive()).isFalse();
+    }
 }
diff --git a/packages/StatementService/src/com/android/statementservice/DirectStatementService.java b/packages/StatementService/src/com/android/statementservice/DirectStatementService.java
index 449738e..659696e 100644
--- a/packages/StatementService/src/com/android/statementservice/DirectStatementService.java
+++ b/packages/StatementService/src/com/android/statementservice/DirectStatementService.java
@@ -155,17 +155,20 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        if (mThread != null) {
-            mThread.quit();
-        }
-
-        try {
-            if (mHttpResponseCache != null) {
-                mHttpResponseCache.delete();
+        final HttpResponseCache responseCache = mHttpResponseCache;
+        mHandler.post(new Runnable() {
+            public void run() {
+                try {
+                    if (responseCache != null) {
+                        responseCache.delete();
+                    }
+                } catch (IOException e) {
+                    Log.i(TAG, "HTTP(S) response cache deletion failed:" + e);
+                }
+                Looper.myLooper().quit();
             }
-        } catch (IOException e) {
-            Log.i(TAG, "HTTP(S) response cache deletion failed:" + e);
-        }
+        });
+        mHttpResponseCache = null;
     }
 
     @Override
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 8a1e0b9..bba9ed4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -133,6 +133,9 @@
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
 
+    <!-- Show indicator for Wifi on but not connected. -->
+    <bool name="config_showWifiIndicatorWhenEnabled">false</bool>
+
     <!-- Should "LTE"/"4G" be shown instead of "LTE+"/"4G+" when on NETWORK_TYPE_LTE_CA? -->
     <bool name="config_hideLtePlus">false</bool>
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 52b4c0a..9845c51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -174,6 +174,7 @@
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
+        if (mController == null) return;
         final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen();
         final boolean newValue = zen != ZEN_MODE_OFF;
         final boolean valueChanged = state.value != newValue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index b22ce18..0adb439 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -64,6 +64,7 @@
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
             .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+            .setUids(null)
             .build();
     private static final int NO_NETWORK = -1;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 2819624..36cfe57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -78,8 +78,10 @@
     @Override
     public void notifyListeners(SignalCallback callback) {
         // only show wifi in the cluster if connected or if wifi-only
+        boolean visibleWhenEnabled = mContext.getResources().getBoolean(
+                R.bool.config_showWifiIndicatorWhenEnabled);
         boolean wifiVisible = mCurrentState.enabled
-                && (mCurrentState.connected || !mHasMobileData);
+                && (mCurrentState.connected || !mHasMobileData || visibleWhenEnabled);
         String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
         boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
         String contentDescription = getStringIfExists(getContentDescription());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 7ca9d73..f76de5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -19,11 +19,15 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
@@ -32,6 +36,9 @@
 import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.UserManager;
 import android.security.IKeyChainService;
 import android.support.test.runner.AndroidJUnit4;
@@ -61,6 +68,7 @@
     private final UserManager mUserManager = mock(UserManager.class);
     private SecurityControllerImpl mSecurityController;
     private CountDownLatch mStateChangedLatch;
+    private ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
 
     // implementing SecurityControllerCallback
     @Override
@@ -72,7 +80,7 @@
     public void setUp() throws Exception {
         mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager);
         mContext.addMockSystemService(Context.USER_SERVICE, mUserManager);
-        mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mock(ConnectivityManager.class));
+        mContext.addMockSystemService(Context.CONNECTIVITY_SERVICE, mConnectivityManager);
 
         Intent intent = new Intent(IKeyChainService.class.getName());
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
@@ -176,4 +184,12 @@
         //assertTrue(mStateChangedLatch.await(31, TimeUnit.SECONDS));
         //assertFalse(mSecurityController.hasCACertInCurrentUser());
     }
+
+    @Test
+    public void testNetworkRequest() {
+        verify(mConnectivityManager, times(1)).registerNetworkCallback(argThat(
+                (NetworkRequest request) -> request.networkCapabilities.getUids() == null
+                        && request.networkCapabilities.getCapabilities().length == 0
+                ), any(NetworkCallback.class));
+    }
 }
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 029dc7b..6494a81d 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -60,6 +60,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
@@ -632,23 +633,14 @@
             if (DBG) {
                 Slog.d(TAG, "Binder is dead - unregister " + mPackageName);
             }
-            if (isBleAppPresent()) {
-                // Nothing to do, another app is here.
-                return;
-            }
-            if (DBG) {
-                Slog.d(TAG, "Disabling LE only mode after application crash");
-            }
-            try {
-                mBluetoothLock.readLock().lock();
-                if (mBluetooth != null && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
-                    mEnable = false;
-                    mBluetooth.onBrEdrDown();
+
+            for (Map.Entry<IBinder, ClientDeathRecipient> entry : mBleApps.entrySet()) {
+                IBinder token = entry.getKey();
+                ClientDeathRecipient deathRec = entry.getValue();
+                if (deathRec.equals(this)) {
+                    updateBleAppCount(token, false, mPackageName);
+                    break;
                 }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to call onBrEdrDown", e);
-            } finally {
-                mBluetoothLock.readLock().unlock();
             }
         }
 
@@ -2154,6 +2146,11 @@
             mActiveLogs.add(
                     new ActiveLog(reason, packageName, enable, System.currentTimeMillis()));
         }
+
+        int state = enable ? StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__ENABLED :
+                             StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED__STATE__DISABLED;
+        StatsLog.write_non_chained(StatsLog.BLUETOOTH_ENABLED_STATE_CHANGED,
+                Binder.getCallingUid(), null, state, reason, packageName);
     }
 
     private void addCrashLog() {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6c24e94..995f926 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -878,7 +878,12 @@
 
     private Tethering makeTethering() {
         // TODO: Move other elements into @Overridden getters.
-        final TetheringDependencies deps = new TetheringDependencies();
+        final TetheringDependencies deps = new TetheringDependencies() {
+            @Override
+            public boolean isTetheringSupported() {
+                return ConnectivityService.this.isTetheringSupported();
+            }
+        };
         return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
                 IoThread.get().getLooper(), new MockableSystemProperties(),
                 deps);
@@ -1356,6 +1361,12 @@
         }
     }
 
+    private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
+        if (!mPermissionMonitor.hasUseBackgroundNetworksPermission(Binder.getCallingUid())) {
+            nc.addCapability(NET_CAPABILITY_FOREGROUND);
+        }
+    }
+
     @Override
     public NetworkState[] getAllNetworkState() {
         // Require internal since we're handing out IMSI details
@@ -4365,15 +4376,13 @@
 
         NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         restrictRequestUidsForCaller(nc);
-        if (!ConnectivityManager.checkChangePermission(mContext)) {
-            // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
-            // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
-            // onLost and onAvailable callbacks when networks move in and out of the background.
-            // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
-            // can't request networks.
-            nc.addCapability(NET_CAPABILITY_FOREGROUND);
-        }
-        ensureValidNetworkSpecifier(networkCapabilities);
+        // Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
+        // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
+        // onLost and onAvailable callbacks when networks move in and out of the background.
+        // There is no need to do this for requests because an app without CHANGE_NETWORK_STATE
+        // can't request networks.
+        restrictBackgroundRequestForCaller(nc);
+        ensureValidNetworkSpecifier(nc);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
@@ -4531,17 +4540,17 @@
         return nai.network.netId;
     }
 
-    private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
+    private void handleRegisterNetworkAgent(NetworkAgentInfo nai) {
         if (VDBG) log("Got NetworkAgent Messenger");
-        mNetworkAgentInfos.put(na.messenger, na);
+        mNetworkAgentInfos.put(nai.messenger, nai);
         synchronized (mNetworkForNetId) {
-            mNetworkForNetId.put(na.network.netId, na);
+            mNetworkForNetId.put(nai.network.netId, nai);
         }
-        na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
-        NetworkInfo networkInfo = na.networkInfo;
-        na.networkInfo = null;
-        updateNetworkInfo(na, networkInfo);
-        updateUids(na, null, na.networkCapabilities);
+        nai.asyncChannel.connect(mContext, mTrackerHandler, nai.messenger);
+        NetworkInfo networkInfo = nai.networkInfo;
+        nai.networkInfo = null;
+        updateNetworkInfo(nai, networkInfo);
+        updateUids(nai, null, nai.networkCapabilities);
     }
 
     private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
@@ -5268,7 +5277,6 @@
                 for (LinkProperties stacked : newNetwork.linkProperties.getStackedLinks()) {
                     final String stackedIface = stacked.getInterfaceName();
                     bs.noteNetworkInterfaceType(stackedIface, type);
-                    NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
                 }
             } catch (RemoteException ignored) {
             }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 45a4dfb9..bde6bd8 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -36,6 +36,7 @@
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
 import android.net.IpSecUdpEncapResponse;
+import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
@@ -618,10 +619,8 @@
                                 spi,
                                 mConfig.getMarkValue(),
                                 mConfig.getMarkMask());
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId);
+            } catch (RemoteException | ServiceSpecificException e) {
+                Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -677,14 +676,14 @@
         @Override
         public void freeUnderlyingResources() {
             try {
-                mSrvConfig
-                        .getNetdInstance()
-                        .ipSecDeleteSecurityAssociation(
-                                mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId);
+                if (!mOwnedByTransform) {
+                    mSrvConfig
+                            .getNetdInstance()
+                            .ipSecDeleteSecurityAssociation(
+                                    mResourceId, mSourceAddress, mDestinationAddress, mSpi, 0, 0);
+                }
+            } catch (ServiceSpecificException | RemoteException e) {
+                Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
             }
 
             mSpi = IpSecManager.INVALID_SECURITY_PARAMETER_INDEX;
@@ -829,15 +828,13 @@
                                         0, direction, wildcardAddr, wildcardAddr, mark, 0xffffffff);
                     }
                 }
-            } catch (ServiceSpecificException e) {
-                // FIXME: get the error code and throw is at an IOException from Errno Exception
-            } catch (RemoteException e) {
+            } catch (ServiceSpecificException | RemoteException e) {
                 Log.e(
                         TAG,
                         "Failed to delete VTI with interface name: "
                                 + mInterfaceName
                                 + " and id: "
-                                + mResourceId);
+                                + mResourceId, e);
             }
 
             getResourceTracker().give();
@@ -934,7 +931,7 @@
             return mPort;
         }
 
-        public FileDescriptor getSocket() {
+        public FileDescriptor getFileDescriptor() {
             return mSocket;
         }
 
@@ -1068,7 +1065,10 @@
     public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
             String destinationAddress, int requestedSpi, IBinder binder) throws RemoteException {
         checkInetAddress(destinationAddress);
-        /* requestedSpi can be anything in the int range, so no check is needed. */
+        // RFC 4303 Section 2.1 - 0=local, 1-255=reserved.
+        if (requestedSpi > 0 && requestedSpi < 256) {
+            throw new IllegalArgumentException("ESP SPI must not be in the range of 0-255.");
+        }
         checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
@@ -1319,7 +1319,9 @@
      * from multiple local IP addresses over the same tunnel.
      */
     @Override
-    public synchronized void addAddressToTunnelInterface(int tunnelResourceId, String localAddr) {
+    public synchronized void addAddressToTunnelInterface(
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1327,8 +1329,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Add address to TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceAddAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1337,7 +1352,8 @@
      */
     @Override
     public synchronized void removeAddressFromTunnelInterface(
-            int tunnelResourceId, String localAddr) {
+            int tunnelResourceId, LinkAddress localAddr) {
+        enforceNetworkStackPermission();
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
 
         // Get tunnelInterface record; if no such interface is found, will throw
@@ -1345,8 +1361,21 @@
         TunnelInterfaceRecord tunnelInterfaceInfo =
                 userRecord.mTunnelInterfaceRecords.getResourceOrThrow(tunnelResourceId);
 
-        // TODO: Add calls to netd:
-        //       Remove address from TunnelInterface
+        try {
+            // We can assume general validity of the IP address, since we get them as a
+            // LinkAddress, which does some validation.
+            mSrvConfig
+                    .getNetdInstance()
+                    .interfaceDelAddress(
+                            tunnelInterfaceInfo.mInterfaceName,
+                            localAddr.getAddress().getHostAddress(),
+                            localAddr.getPrefixLength());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
+            throw new IllegalArgumentException(e);
+        }
     }
 
     /**
@@ -1467,6 +1496,13 @@
         IpSecAlgorithm crypt = c.getEncryption();
         IpSecAlgorithm authCrypt = c.getAuthenticatedEncryption();
 
+        String cryptName;
+        if (crypt == null) {
+            cryptName = (authCrypt == null) ? IpSecAlgorithm.CRYPT_NULL : "";
+        } else {
+            cryptName = crypt.getName();
+        }
+
         mSrvConfig
                 .getNetdInstance()
                 .ipSecAddSecurityAssociation(
@@ -1481,7 +1517,7 @@
                         (auth != null) ? auth.getName() : "",
                         (auth != null) ? auth.getKey() : new byte[] {},
                         (auth != null) ? auth.getTruncationLengthBits() : 0,
-                        (crypt != null) ? crypt.getName() : "",
+                        cryptName,
                         (crypt != null) ? crypt.getKey() : new byte[] {},
                         (crypt != null) ? crypt.getTruncationLengthBits() : 0,
                         (authCrypt != null) ? authCrypt.getName() : "",
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 88ae224..5d719ad 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1867,10 +1867,10 @@
     }
 
     @Override
-    public NetworkStats getNetworkStatsUidDetail(int uid) {
+    public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null);
+            return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL, null);
         } catch (IOException e) {
             throw new IllegalStateException(e);
         }
@@ -1942,13 +1942,13 @@
 
     @Override
     public void setDnsConfigurationForNetwork(int netId, String[] servers, String[] domains,
-                    int[] params, boolean useTls, String tlsHostname) {
+                    int[] params, String tlsHostname, String[] tlsServers) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
         final String[] tlsFingerprints = new String[0];
         try {
             mNetdService.setResolverConfiguration(
-                    netId, servers, domains, params, useTls, tlsHostname, tlsFingerprints);
+                    netId, servers, domains, params, tlsHostname, tlsServers, tlsFingerprints);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 3927ebd..539c001 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -65,7 +65,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.NoSuchElementException;
 
@@ -90,6 +89,8 @@
     private static final boolean VDBG = false; // STOPSHIP if true
 
     private static class Record {
+        Context context;
+
         String callingPackage;
 
         IBinder binder;
@@ -108,8 +109,6 @@
 
         int phoneId = SubscriptionManager.INVALID_PHONE_INDEX;
 
-        boolean canReadPhoneState;
-
         boolean matchPhoneStateListenerEvent(int events) {
             return (callback != null) && ((events & this.events) != 0);
         }
@@ -118,6 +117,15 @@
             return (onSubscriptionsChangedListenerCallback != null);
         }
 
+        boolean canReadPhoneState() {
+            try {
+                return TelephonyPermissions.checkReadPhoneState(
+                        context, subId, callerPid, callerUid, callingPackage, "listen");
+            } catch (SecurityException e) {
+                return false;
+            }
+        }
+
         @Override
         public String toString() {
             return "{callingPackage=" + callingPackage + " binder=" + binder
@@ -125,8 +133,7 @@
                     + " onSubscriptionsChangedListenererCallback="
                                             + onSubscriptionsChangedListenerCallback
                     + " callerUid=" + callerUid + " subId=" + subId + " phoneId=" + phoneId
-                    + " events=" + Integer.toHexString(events)
-                    + " canReadPhoneState=" + canReadPhoneState + "}";
+                    + " events=" + Integer.toHexString(events) + "}";
         }
     }
 
@@ -164,14 +171,9 @@
 
     private int[] mDataActivity;
 
+    // Connection state of default APN type data (i.e. internet) of phones
     private int[] mDataConnectionState;
 
-    private ArrayList<String>[] mConnectedApns;
-
-    private LinkProperties[] mDataConnectionLinkProperties;
-
-    private NetworkCapabilities[] mDataConnectionNetworkCapabilities;
-
     private Bundle[] mCellLocation;
 
     private int[] mDataConnectionNetworkType;
@@ -212,11 +214,6 @@
                 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR |
                 PhoneStateListener.LISTEN_VOLTE_STATE;
 
-    static final int CHECK_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_CALL_STATE |
-                PhoneStateListener.LISTEN_DATA_ACTIVITY |
-                PhoneStateListener.LISTEN_DATA_CONNECTION_STATE;
-
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
                 PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE;
@@ -323,9 +320,8 @@
         mBatteryStats = BatteryStatsService.getService();
 
         int numPhones = TelephonyManager.getDefault().getPhoneCount();
-        if (DBG) log("TelephonyRegistor: ctor numPhones=" + numPhones);
+        if (DBG) log("TelephonyRegistry: ctor numPhones=" + numPhones);
         mNumPhones = numPhones;
-        mConnectedApns = new ArrayList[numPhones];
         mCallState = new int[numPhones];
         mDataActivity = new int[numPhones];
         mDataConnectionState = new int[numPhones];
@@ -339,8 +335,6 @@
         mMessageWaiting = new boolean[numPhones];
         mCallForwarding = new boolean[numPhones];
         mCellLocation = new Bundle[numPhones];
-        mDataConnectionLinkProperties = new LinkProperties[numPhones];
-        mDataConnectionNetworkCapabilities = new NetworkCapabilities[numPhones];
         mCellInfo = new ArrayList<List<CellInfo>>();
         mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
         for (int i = 0; i < numPhones; i++) {
@@ -358,7 +352,6 @@
             mCellLocation[i] = new Bundle();
             mCellInfo.add(i, null);
             mPhysicalChannelConfigs.add(i, null);
-            mConnectedApns[i] = new ArrayList<String>();
         }
 
         // Note that location can be null for non-phone builds like
@@ -386,20 +379,13 @@
     public void addOnSubscriptionsChangedListener(String callingPackage,
             IOnSubscriptionsChangedListener callback) {
         int callerUserId = UserHandle.getCallingUserId();
-        mContext.getSystemService(AppOpsManager.class)
-                .checkPackage(Binder.getCallingUid(), callingPackage);
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen oscl: E pkg=" + callingPackage + " myUserId=" + UserHandle.myUserId()
                 + " callerUserId="  + callerUserId + " callback=" + callback
                 + " callback.asBinder=" + callback.asBinder());
         }
 
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                mContext, callingPackage, "addOnSubscriptionsChangedListener")) {
-            return;
-        }
-
-
         synchronized (mRecords) {
             // register
             IBinder b = callback.asBinder();
@@ -409,12 +395,12 @@
                 return;
             }
 
+            r.context = mContext;
             r.onSubscriptionsChangedListenerCallback = callback;
             r.callingPackage = callingPackage;
             r.callerUid = Binder.getCallingUid();
             r.callerPid = Binder.getCallingPid();
             r.events = 0;
-            r.canReadPhoneState = true; // permission has been enforced above
             if (DBG) {
                 log("listen oscl:  Register r=" + r);
             }
@@ -483,8 +469,7 @@
     private void listen(String callingPackage, IPhoneStateListener callback, int events,
             boolean notifyNow, int subId) {
         int callerUserId = UserHandle.getCallingUserId();
-        mContext.getSystemService(AppOpsManager.class)
-                .checkPackage(Binder.getCallingUid(), callingPackage);
+        mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         if (VDBG) {
             log("listen: E pkg=" + callingPackage + " events=0x" + Integer.toHexString(events)
                 + " notifyNow=" + notifyNow + " subId=" + subId + " myUserId="
@@ -495,7 +480,7 @@
             // Checks permission and throws SecurityException for disallowed operations. For pre-M
             // apps whose runtime permission has been revoked, we return immediately to skip sending
             // events to the app without crashing it.
-            if (!checkListenerPermission(events, callingPackage, "listen")) {
+            if (!checkListenerPermission(events, subId, callingPackage, "listen")) {
                 return;
             }
 
@@ -509,14 +494,11 @@
                     return;
                 }
 
+                r.context = mContext;
                 r.callback = callback;
                 r.callingPackage = callingPackage;
                 r.callerUid = Binder.getCallingUid();
                 r.callerPid = Binder.getCallingPid();
-                boolean isPhoneStateEvent = (events & (CHECK_PHONE_STATE_PERMISSION_MASK
-                        | ENFORCE_PHONE_STATE_PERMISSION_MASK)) != 0;
-                r.canReadPhoneState =
-                        isPhoneStateEvent && canReadPhoneState(callingPackage, "listen");
                 // Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
                 // force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
                 if (!SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -684,18 +666,9 @@
         }
     }
 
-    private boolean canReadPhoneState(String callingPackage, String message) {
-        try {
-            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                    mContext, callingPackage, message);
-        } catch (SecurityException e) {
-            return false;
-        }
-    }
-
     private String getCallIncomingNumber(Record record, int phoneId) {
-        // Hide the number if record's process has no READ_PHONE_STATE permission
-        return record.canReadPhoneState ? mCallIncomingNumber[phoneId] : "";
+        // Hide the number if record's process can't currently read phone state.
+        return record.canReadPhoneState() ? mCallIncomingNumber[phoneId] : "";
     }
 
     private Record add(IBinder binder) {
@@ -770,7 +743,7 @@
                 if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_CALL_STATE) &&
                         (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)) {
                     try {
-                        String incomingNumberOrEmpty = r.canReadPhoneState ? incomingNumber : "";
+                        String incomingNumberOrEmpty = r.canReadPhoneState() ? incomingNumber : "";
                         r.callback.onCallStateChanged(state, incomingNumberOrEmpty);
                     } catch (RemoteException ex) {
                         mRemoveList.add(r.binder);
@@ -1216,36 +1189,12 @@
         int phoneId = SubscriptionManager.getPhoneId(subId);
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
-                boolean modified = false;
-                if (state == TelephonyManager.DATA_CONNECTED) {
-                    if (!mConnectedApns[phoneId].contains(apnType)) {
-                        mConnectedApns[phoneId].add(apnType);
-                        if (mDataConnectionState[phoneId] != state) {
-                            mDataConnectionState[phoneId] = state;
-                            modified = true;
-                        }
-                    }
-                } else {
-                    if (mConnectedApns[phoneId].remove(apnType)) {
-                        if (mConnectedApns[phoneId].isEmpty()) {
-                            mDataConnectionState[phoneId] = state;
-                            modified = true;
-                        } else {
-                            // leave mDataConnectionState as is and
-                            // send out the new status for the APN in question.
-                        }
-                    }
-                }
-                mDataConnectionLinkProperties[phoneId] = linkProperties;
-                mDataConnectionNetworkCapabilities[phoneId] = networkCapabilities;
-                if (mDataConnectionNetworkType[phoneId] != networkType) {
-                    mDataConnectionNetworkType[phoneId] = networkType;
-                    // need to tell registered listeners about the new network type
-                    modified = true;
-                }
-                if (modified) {
-                    String str = "onDataConnectionStateChanged(" + mDataConnectionState[phoneId]
-                            + ", " + mDataConnectionNetworkType[phoneId] + ")";
+                // We only call the callback when the change is for default APN type.
+                if (PhoneConstants.APN_TYPE_DEFAULT.equals(apnType)
+                        && (mDataConnectionState[phoneId] != state
+                        || mDataConnectionNetworkType[phoneId] != networkType)) {
+                    String str = "onDataConnectionStateChanged(" + state
+                            + ", " + networkType + ")";
                     log(str);
                     mLocalLog.log(str);
                     for (Record r : mRecords) {
@@ -1256,15 +1205,16 @@
                                 if (DBG) {
                                     log("Notify data connection state changed on sub: " + subId);
                                 }
-                                r.callback.onDataConnectionStateChanged(
-                                        mDataConnectionState[phoneId],
-                                        mDataConnectionNetworkType[phoneId]);
+                                r.callback.onDataConnectionStateChanged(state, networkType);
                             } catch (RemoteException ex) {
                                 mRemoveList.add(r.binder);
                             }
                         }
                     }
                     handleRemoveListLocked();
+
+                    mDataConnectionState[phoneId] = state;
+                    mDataConnectionNetworkType[phoneId] = networkType;
                 }
                 mPreciseDataConnectionState = new PreciseDataConnectionState(state, networkType,
                         apnType, apn, reason, linkProperties, "");
@@ -1500,14 +1450,10 @@
                 pw.println("mCallForwarding=" + mCallForwarding[i]);
                 pw.println("mDataActivity=" + mDataActivity[i]);
                 pw.println("mDataConnectionState=" + mDataConnectionState[i]);
-                pw.println("mDataConnectionLinkProperties=" + mDataConnectionLinkProperties[i]);
-                pw.println("mDataConnectionNetworkCapabilities=" +
-                        mDataConnectionNetworkCapabilities[i]);
                 pw.println("mCellLocation=" + mCellLocation[i]);
                 pw.println("mCellInfo=" + mCellInfo.get(i));
                 pw.decreaseIndent();
             }
-            pw.println("mConnectedApns=" + Arrays.toString(mConnectedApns));
             pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState);
             pw.println("mPreciseCallState=" + mPreciseCallState);
             pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
@@ -1724,7 +1670,8 @@
                 == PackageManager.PERMISSION_GRANTED;
     }
 
-    private boolean checkListenerPermission(int events, String callingPackage, String message) {
+    private boolean checkListenerPermission(
+            int events, int subId, String callingPackage, String message) {
         if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
@@ -1736,7 +1683,7 @@
 
         if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
-                    mContext, callingPackage, message)) {
+                    mContext, subId, callingPackage, message)) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 53285e6..155febd 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -587,13 +587,8 @@
     }
 
     private File dumpKernelStackTraces() {
-        String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
-        if (tracesPath == null || tracesPath.length() == 0) {
-            return null;
-        }
-
-        native_dumpKernelStacks(tracesPath);
-        return new File(tracesPath);
+        native_dumpKernelStacks("/data/anr");
+        return new File("/data/anr");
     }
 
     private native void native_dumpKernelStacks(String tracesPath);
@@ -615,14 +610,6 @@
                 return null;
             }
 
-            // Don't run the FD monitor on builds that have a global ANR trace file. We're using
-            // the ANR trace directory as a quick hack in order to get these traces in bugreports
-            // and we wouldn't want to overwrite something important.
-            final String dumpDirStr = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
-            if (dumpDirStr.isEmpty()) {
-                return null;
-            }
-
             final StructRlimit rlimit;
             try {
                 rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
@@ -639,7 +626,7 @@
             // We do this to avoid having to enumerate the contents of /proc/self/fd in order to
             // count the number of descriptors open in the process.
             final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
-            return new OpenFdMonitor(new File(dumpDirStr), fdThreshold);
+            return new OpenFdMonitor(new File("/data/anr"), fdThreshold);
         }
 
         OpenFdMonitor(File dumpDir, File fdThreshold) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6c60b74..7cfc8af 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -257,6 +257,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
@@ -2695,13 +2696,15 @@
     }
 
     /**
-     * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the
+     * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the
      * latest value via a content observer.
      */
     static class HiddenApiBlacklist extends ContentObserver {
 
         private final Context mContext;
         private boolean mBlacklistDisabled;
+        private String mExemptionsStr;
+        private List<String> mExemptions = Collections.emptyList();
 
         public HiddenApiBlacklist(Handler handler, Context context) {
             super(handler);
@@ -2717,8 +2720,22 @@
         }
 
         private void update() {
-            mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(),
-                    Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+            String exemptions = Settings.Global.getString(mContext.getContentResolver(),
+                    Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
+            if (!TextUtils.equals(exemptions, mExemptionsStr)) {
+                mExemptionsStr = exemptions;
+                if ("*".equals(exemptions)) {
+                    mBlacklistDisabled = true;
+                    mExemptions = Collections.emptyList();
+                } else {
+                    mBlacklistDisabled = false;
+                    mExemptions = TextUtils.isEmpty(exemptions)
+                            ? Collections.emptyList()
+                            : Arrays.asList(exemptions.split(","));
+                }
+                zygoteProcess.setApiBlacklistExemptions(mExemptions);
+            }
+
         }
 
         boolean isDisabled() {
@@ -3944,12 +3961,14 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
-            if (!app.info.isAllowedToUseHiddenApi() &&
-                    !disableHiddenApiChecks &&
-                    !mHiddenApiBlacklist.isDisabled()) {
-                // This app is not allowed to use undocumented and private APIs, or blacklisting is
-                // enabled. Set up its runtime with the appropriate flag.
-                runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
+            if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
+                @HiddenApiEnforcementPolicy int policy =
+                        app.info.getHiddenApiEnforcementPolicy();
+                int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
+                if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
+                    throw new IllegalStateException("Invalid API policy: " + policy);
+                }
+                runtimeFlags |= policyBits;
             }
 
             String invokeWith = null;
@@ -5591,57 +5610,20 @@
             }
         }
 
-        boolean useTombstonedForJavaTraces = false;
-        File tracesFile;
+        final File tracesDir = new File("/data/anr");
+        // Each set of ANR traces is written to a separate file and dumpstate will process
+        // all such files and add them to a captured bug report if they're recent enough.
+        maybePruneOldTraces(tracesDir);
 
-        final String tracesDirProp = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
-        if (tracesDirProp.isEmpty()) {
-            // When dalvik.vm.stack-trace-dir is not set, we are using the "old" trace
-            // dumping scheme. All traces are written to a global trace file (usually
-            // "/data/anr/traces.txt") so the code below must take care to unlink and recreate
-            // the file if requested.
-            //
-            // This mode of operation will be removed in the near future.
-
-
-            String globalTracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
-            if (globalTracesPath.isEmpty()) {
-                Slog.w(TAG, "dumpStackTraces: no trace path configured");
-                return null;
-            }
-
-            tracesFile = new File(globalTracesPath);
-            try {
-                if (clearTraces && tracesFile.exists()) {
-                    tracesFile.delete();
-                }
-
-                tracesFile.createNewFile();
-                FileUtils.setPermissions(globalTracesPath, 0666, -1, -1); // -rw-rw-rw-
-            } catch (IOException e) {
-                Slog.w(TAG, "Unable to prepare ANR traces file: " + tracesFile, e);
-                return null;
-            }
-        } else {
-            File tracesDir = new File(tracesDirProp);
-            // When dalvik.vm.stack-trace-dir is set, we use the "new" trace dumping scheme.
-            // Each set of ANR traces is written to a separate file and dumpstate will process
-            // all such files and add them to a captured bug report if they're recent enough.
-            maybePruneOldTraces(tracesDir);
-
-            // NOTE: We should consider creating the file in native code atomically once we've
-            // gotten rid of the old scheme of dumping and lot of the code that deals with paths
-            // can be removed.
-            tracesFile = createAnrDumpFile(tracesDir);
-            if (tracesFile == null) {
-                return null;
-            }
-
-            useTombstonedForJavaTraces = true;
+        // NOTE: We should consider creating the file in native code atomically once we've
+        // gotten rid of the old scheme of dumping and lot of the code that deals with paths
+        // can be removed.
+        File tracesFile = createAnrDumpFile(tracesDir);
+        if (tracesFile == null) {
+            return null;
         }
 
-        dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids,
-                useTombstonedForJavaTraces);
+        dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
         return tracesFile;
     }
 
@@ -5678,83 +5660,22 @@
      * since it's the system_server that creates trace files for most ANRs.
      */
     private static void maybePruneOldTraces(File tracesDir) {
+        final File[] files = tracesDir.listFiles();
+        if (files == null) return;
+
+        final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
         final long now = System.currentTimeMillis();
-        final File[] traceFiles = tracesDir.listFiles();
-
-        if (traceFiles != null) {
-            for (File file : traceFiles) {
-                if ((now - file.lastModified()) > DAY_IN_MILLIS)  {
-                    if (!file.delete()) {
-                        Slog.w(TAG, "Unable to prune stale trace file: " + file);
-                    }
+        Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+        for (int i = 0; i < files.length; ++i) {
+            if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+                if (!files[i].delete()) {
+                    Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
                 }
             }
         }
     }
 
     /**
-     * Legacy code, do not use. Existing users will be deleted.
-     *
-     * @deprecated
-     */
-    @Deprecated
-    public static class DumpStackFileObserver extends FileObserver {
-        // Keep in sync with frameworks/native/cmds/dumpstate/utils.cpp
-        private static final int TRACE_DUMP_TIMEOUT_MS = 10000; // 10 seconds
-
-        private final String mTracesPath;
-        private boolean mClosed;
-
-        public DumpStackFileObserver(String tracesPath) {
-            super(tracesPath, FileObserver.CLOSE_WRITE);
-            mTracesPath = tracesPath;
-        }
-
-        @Override
-        public synchronized void onEvent(int event, String path) {
-            mClosed = true;
-            notify();
-        }
-
-        public long dumpWithTimeout(int pid, long timeout) {
-            sendSignal(pid, SIGNAL_QUIT);
-            final long start = SystemClock.elapsedRealtime();
-
-            final long waitTime = Math.min(timeout, TRACE_DUMP_TIMEOUT_MS);
-            synchronized (this) {
-                try {
-                    wait(waitTime); // Wait for traces file to be closed.
-                } catch (InterruptedException e) {
-                    Slog.wtf(TAG, e);
-                }
-            }
-
-            // This avoids a corner case of passing a negative time to the native
-            // trace in case we've already hit the overall timeout.
-            final long timeWaited = SystemClock.elapsedRealtime() - start;
-            if (timeWaited >= timeout) {
-                return timeWaited;
-            }
-
-            if (!mClosed) {
-                Slog.w(TAG, "Didn't see close of " + mTracesPath + " for pid " + pid +
-                       ". Attempting native stack collection.");
-
-                final long nativeDumpTimeoutMs = Math.min(
-                        NATIVE_DUMP_TIMEOUT_MS, timeout - timeWaited);
-
-                Debug.dumpNativeBacktraceToFileTimeout(pid, mTracesPath,
-                        (int) (nativeDumpTimeoutMs / 1000));
-            }
-
-            final long end = SystemClock.elapsedRealtime();
-            mClosed = false;
-
-            return (end - start);
-        }
-    }
-
-    /**
      * Dump java traces for process {@code pid} to the specified file. If java trace dumping
      * fails, a native backtrace is attempted. Note that the timeout {@code timeoutMs} only applies
      * to the java section of the trace, a further {@code NATIVE_DUMP_TIMEOUT_MS} might be spent
@@ -5772,106 +5693,78 @@
     }
 
     private static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
-            ArrayList<Integer> nativePids, ArrayList<Integer> extraPids,
-            boolean useTombstonedForJavaTraces) {
+            ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
 
         // We don't need any sort of inotify based monitoring when we're dumping traces via
         // tombstoned. Data is piped to an "intercept" FD installed in tombstoned so we're in full
         // control of all writes to the file in question.
-        final DumpStackFileObserver observer;
-        if (useTombstonedForJavaTraces) {
-            observer = null;
-        } else {
-            // Use a FileObserver to detect when traces finish writing.
-            // The order of traces is considered important to maintain for legibility.
-            observer = new DumpStackFileObserver(tracesFile);
-        }
 
         // We must complete all stack dumps within 20 seconds.
         long remainingTime = 20 * 1000;
-        try {
-            if (observer != null) {
-                observer.startWatching();
+
+        // First collect all of the stacks of the most important pids.
+        if (firstPids != null) {
+            int num = firstPids.size();
+            for (int i = 0; i < num; i++) {
+                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid " + firstPids.get(i));
+                final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
+                                                                remainingTime);
+
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+                           "); deadline exceeded.");
+                    return;
+                }
+
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
+                }
             }
+        }
 
-            // First collect all of the stacks of the most important pids.
-            if (firstPids != null) {
-                int num = firstPids.size();
-                for (int i = 0; i < num; i++) {
-                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for pid "
-                            + firstPids.get(i));
-                    final long timeTaken;
-                    if (useTombstonedForJavaTraces) {
-                        timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile, remainingTime);
-                    } else {
-                        timeTaken = observer.dumpWithTimeout(firstPids.get(i), remainingTime);
-                    }
+        // Next collect the stacks of the native pids
+        if (nativePids != null) {
+            for (int pid : nativePids) {
+                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
+                final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
 
-                    remainingTime -= timeTaken;
-                    if (remainingTime <= 0) {
-                        Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
+                final long start = SystemClock.elapsedRealtime();
+                Debug.dumpNativeBacktraceToFileTimeout(
+                        pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
+                final long timeTaken = SystemClock.elapsedRealtime() - start;
+
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
+                        "); deadline exceeded.");
+                    return;
+                }
+
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
+                }
+            }
+        }
+
+        // Lastly, dump stacks for all extra PIDs from the CPU tracker.
+        if (extraPids != null) {
+            for (int pid : extraPids) {
+                if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
+
+                final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
+
+                remainingTime -= timeTaken;
+                if (remainingTime <= 0) {
+                    Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
                             "); deadline exceeded.");
-                        return;
-                    }
-
-                    if (DEBUG_ANR) {
-                        Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
-                    }
+                    return;
                 }
-            }
 
-            // Next collect the stacks of the native pids
-            if (nativePids != null) {
-                for (int pid : nativePids) {
-                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid);
-                    final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
-
-                    final long start = SystemClock.elapsedRealtime();
-                    Debug.dumpNativeBacktraceToFileTimeout(
-                            pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
-                    final long timeTaken = SystemClock.elapsedRealtime() - start;
-
-                    remainingTime -= timeTaken;
-                    if (remainingTime <= 0) {
-                        Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
-                            "); deadline exceeded.");
-                        return;
-                    }
-
-                    if (DEBUG_ANR) {
-                        Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
-                    }
+                if (DEBUG_ANR) {
+                    Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
                 }
             }
-
-            // Lastly, dump stacks for all extra PIDs from the CPU tracker.
-            if (extraPids != null) {
-                for (int pid : extraPids) {
-                    if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + pid);
-
-                    final long timeTaken;
-                    if (useTombstonedForJavaTraces) {
-                        timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
-                    } else {
-                        timeTaken = observer.dumpWithTimeout(pid, remainingTime);
-                    }
-
-                    remainingTime -= timeTaken;
-                    if (remainingTime <= 0) {
-                        Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
-                                "); deadline exceeded.");
-                        return;
-                    }
-
-                    if (DEBUG_ANR) {
-                        Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
-                    }
-                }
-            }
-        } finally {
-            if (observer != null) {
-                observer.stopWatching();
-            }
         }
     }
 
@@ -5918,7 +5811,7 @@
             if (app != null) {
                 ArrayList<Integer> firstPids = new ArrayList<Integer>();
                 firstPids.add(app.pid);
-                dumpStackTraces(tracesPath, firstPids, null, null, true /* useTombstoned */);
+                dumpStackTraces(tracesPath, firstPids, null, null);
             }
 
             File lastTracesFile = null;
@@ -7145,6 +7038,9 @@
 
             if (profilerInfo != null && profilerInfo.profileFd != null) {
                 profilerInfo.profileFd = profilerInfo.profileFd.dup();
+                if (TextUtils.equals(mProfileApp, processName) && mProfilerInfo != null) {
+                    clearProfilerLocked();
+                }
             }
 
             // We deprecated Build.SERIAL and it is not accessible to
@@ -7211,7 +7107,10 @@
                         mCoreSettingsObserver.getCoreSettingsLocked(),
                         buildSerial);
             }
-
+            if (profilerInfo != null) {
+                profilerInfo.closeFd();
+                profilerInfo = null;
+            }
             checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
             updateLruProcessLocked(app, false, null);
             checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
@@ -23603,6 +23502,14 @@
                     } catch (IOException e) {
                     }
                     mProfilerInfo.profileFd = null;
+
+                    if (proc.pid == MY_PID) {
+                        // When profiling the system server itself, avoid closing the file
+                        // descriptor, as profilerControl will not create a copy.
+                        // Note: it is also not correct to just set profileFd to null, as the
+                        //       whole ProfilerInfo instance is passed down!
+                        profilerInfo = null;
+                    }
                 } else {
                     stopProfilerLocked(proc, profileType);
                     if (profilerInfo != null && profilerInfo.profileFd != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 9e37c78..5573cd9 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -684,6 +684,14 @@
         int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1);
         if (maxCallVolume != -1) {
             MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = maxCallVolume;
+        }
+
+        int defaultCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_default", -1);
+        if (defaultCallVolume != -1 &&
+                defaultCallVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] &&
+                defaultCallVolume >= MIN_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]) {
+            AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = defaultCallVolume;
+        } else {
             AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] =
                     (maxCallVolume * 3) / 4;
         }
@@ -695,7 +703,8 @@
 
         int defaultMusicVolume = SystemProperties.getInt("ro.config.media_vol_default", -1);
         if (defaultMusicVolume != -1 &&
-                defaultMusicVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]) {
+                defaultMusicVolume <= MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] &&
+                defaultMusicVolume >= MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]) {
             AudioSystem.DEFAULT_STREAM_VOLUME[AudioSystem.STREAM_MUSIC] = defaultMusicVolume;
         } else {
             if (isPlatformTelevision()) {
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 557828a..5579849 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -34,22 +34,19 @@
 import android.net.Network;
 import android.net.NetworkUtils;
 import android.net.Uri;
+import android.net.dns.ResolvUtil;
 import android.os.Binder;
 import android.os.INetworkManagementService;
 import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.system.GaiException;
-import android.system.OsConstants;
-import android.system.StructAddrinfo;
 import android.text.TextUtils;
 import android.util.Slog;
 
 import com.android.server.connectivity.MockableSystemProperties;
 
-import libcore.io.Libcore;
-
 import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
@@ -126,28 +123,19 @@
     }
 
     public static PrivateDnsConfig tryBlockingResolveOf(Network network, String name) {
-        final StructAddrinfo hints = new StructAddrinfo();
-        // Unnecessary, but expressly no AI_ADDRCONFIG.
-        hints.ai_flags = 0;
-        // Fetch all IP addresses at once to minimize re-resolution.
-        hints.ai_family = OsConstants.AF_UNSPEC;
-        hints.ai_socktype = OsConstants.SOCK_DGRAM;
-
         try {
-            final InetAddress[] ips = Libcore.os.android_getaddrinfo(name, hints, network.netId);
-            if (ips != null && ips.length > 0) {
-                return new PrivateDnsConfig(name, ips);
-            }
-        } catch (GaiException ignored) {}
-
-        return null;
+            final InetAddress[] ips = ResolvUtil.blockingResolveAllLocally(network, name);
+            return new PrivateDnsConfig(name, ips);
+        } catch (UnknownHostException uhe) {
+            return new PrivateDnsConfig(name, null);
+        }
     }
 
     public static Uri[] getPrivateDnsSettingsUris() {
-        final Uri[] uris = new Uri[2];
-        uris[0] = Settings.Global.getUriFor(PRIVATE_DNS_MODE);
-        uris[1] = Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER);
-        return uris;
+        return new Uri[]{
+            Settings.Global.getUriFor(PRIVATE_DNS_MODE),
+            Settings.Global.getUriFor(PRIVATE_DNS_SPECIFIER),
+        };
     }
 
     private final Context mContext;
@@ -192,35 +180,38 @@
 
     public void setDnsConfigurationForNetwork(
             int netId, LinkProperties lp, boolean isDefaultNetwork) {
+        final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
+        final String[] domainStrs = getDomainStrings(lp.getDomains());
+
+        updateParametersSettings();
+        final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
+
         // We only use the PrivateDnsConfig data pushed to this class instance
         // from ConnectivityService because it works in coordination with
         // NetworkMonitor to decide which networks need validation and runs the
         // blocking calls to resolve Private DNS strict mode hostnames.
         //
-        // At this time we do attempt to enable Private DNS on non-Internet
+        // At this time we do not attempt to enable Private DNS on non-Internet
         // networks like IMS.
         final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
 
         final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
         final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
         final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
-
-        final String[] serverStrs = NetworkUtils.makeStrings(
-                strictMode ? Arrays.stream(privateDnsCfg.ips)
-                                   .filter((ip) -> lp.isReachable(ip))
-                                   .collect(Collectors.toList())
-                           : lp.getDnsServers());
-        final String[] domainStrs = getDomainStrings(lp.getDomains());
-
-        updateParametersSettings();
-        final int[] params = { mSampleValidity, mSuccessThreshold, mMinSamples, mMaxSamples };
+        final String[] tlsServers =
+                strictMode ? NetworkUtils.makeStrings(
+                        Arrays.stream(privateDnsCfg.ips)
+                              .filter((ip) -> lp.isReachable(ip))
+                              .collect(Collectors.toList()))
+                : useTls ? assignedServers  // Opportunistic
+                : new String[0];            // Off
 
         Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
-                netId, Arrays.toString(serverStrs), Arrays.toString(domainStrs),
-                Arrays.toString(params), useTls, tlsHostname));
+                netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
+                Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
         try {
             mNMS.setDnsConfigurationForNetwork(
-                    netId, serverStrs, domainStrs, params, useTls, tlsHostname);
+                    netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
         } catch (Exception e) {
             Slog.e(TAG, "Error setting DNS configuration: " + e);
             return;
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index e786bed..2d50dd5 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -101,9 +101,12 @@
 
 
     /**
-     * There are only 2 possible callbacks.
+     * There are only 3 possible callbacks.
      *
-     * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY].
+     * mNetdEventCallbackList[CALLBACK_CALLER_CONNECTIVITY_SERVICE]
+     * Callback registered/unregistered by ConnectivityService.
+     *
+     * mNetdEventCallbackList[CALLBACK_CALLER_DEVICE_POLICY]
      * Callback registered/unregistered when logging is being enabled/disabled in DPM
      * by the device owner. It's DevicePolicyManager's responsibility to ensure that.
      *
@@ -112,6 +115,7 @@
      */
     @GuardedBy("this")
     private static final int[] ALLOWED_CALLBACK_TYPES = {
+        INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
         INetdEventCallback.CALLBACK_CALLER_DEVICE_POLICY,
         INetdEventCallback.CALLBACK_CALLER_NETWORK_WATCHLIST
     };
@@ -211,6 +215,19 @@
     @Override
     // Called concurrently by multiple binder threads.
     // This method must not block or perform long-running operations.
+    public synchronized void onPrivateDnsValidationEvent(int netId,
+            String ipAddress, String hostname, boolean validated)
+            throws RemoteException {
+        for (INetdEventCallback callback : mNetdEventCallbackList) {
+            if (callback != null) {
+                callback.onPrivateDnsValidationEvent(netId, ipAddress, hostname, validated);
+            }
+        }
+    }
+
+    @Override
+    // Called concurrently by multiple binder threads.
+    // This method must not block or perform long-running operations.
     public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr,
             int port, int uid) throws RemoteException {
         long timestamp = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index e084ff8..d578e95 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.CHANGE_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
 import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
@@ -27,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,6 +41,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -150,7 +154,14 @@
         update(mUsers, mApps, true);
     }
 
-    private boolean hasPermission(PackageInfo app, String permission) {
+    @VisibleForTesting
+    boolean isPreinstalledSystemApp(PackageInfo app) {
+        int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
+        return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
+    }
+
+    @VisibleForTesting
+    boolean hasPermission(PackageInfo app, String permission) {
         if (app.requestedPermissions != null) {
             for (String p : app.requestedPermissions) {
                 if (permission.equals(p)) {
@@ -166,14 +177,40 @@
     }
 
     private boolean hasRestrictedNetworkPermission(PackageInfo app) {
-        int flags = app.applicationInfo != null ? app.applicationInfo.flags : 0;
-        if ((flags & FLAG_SYSTEM) != 0 || (flags & FLAG_UPDATED_SYSTEM_APP) != 0) {
-            return true;
-        }
+        if (isPreinstalledSystemApp(app)) return true;
         return hasPermission(app, CONNECTIVITY_INTERNAL)
                 || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
     }
 
+    private boolean hasUseBackgroundNetworksPermission(PackageInfo app) {
+        // This function defines what it means to hold the permission to use
+        // background networks.
+        return hasPermission(app, CHANGE_NETWORK_STATE)
+                || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS)
+                || hasPermission(app, CONNECTIVITY_INTERNAL)
+                || hasPermission(app, NETWORK_STACK)
+                // TODO : remove this check (b/31479477). Not all preinstalled apps should
+                // have access to background networks, they should just request the appropriate
+                // permission for their use case from the list above.
+                || isPreinstalledSystemApp(app);
+    }
+
+    public boolean hasUseBackgroundNetworksPermission(int uid) {
+        final String[] names = mPackageManager.getPackagesForUid(uid);
+        if (null == names || names.length == 0) return false;
+        try {
+            // Only using the first package name. There may be multiple names if multiple
+            // apps share the same UID, but in that case they also share permissions so
+            // querying with any of the names will return the same results.
+            final PackageInfo app = mPackageManager.getPackageInfo(names[0], GET_PERMISSIONS);
+            return hasUseBackgroundNetworksPermission(app);
+        } catch (NameNotFoundException e) {
+            // App not found.
+            loge("NameNotFoundException " + names[0], e);
+            return false;
+        }
+    }
+
     private int[] toIntArray(List<Integer> list) {
         int[] array = new int[list.size()];
         for (int i = 0; i < list.size(); i++) {
@@ -308,4 +345,8 @@
     private static void loge(String s) {
         Log.e(TAG, s);
     }
+
+    private static void loge(String s, Throwable e) {
+        Log.e(TAG, s, e);
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index cffa834..e520d0c 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -19,6 +19,27 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_ERRORED_TETHER;
+import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+import static android.net.ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_INVALID;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
@@ -45,7 +66,6 @@
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.IpPrefix;
@@ -56,6 +76,7 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.net.util.VersionedBroadcastListener;
@@ -98,6 +119,7 @@
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
 import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
 import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -146,7 +168,7 @@
             stateMachine = sm;
             // Assume all state machines start out available and with no errors.
             lastState = IControlsTethering.STATE_AVAILABLE;
-            lastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+            lastError = TETHER_ERROR_NO_ERROR;
         }
 
         public boolean isCurrentlyServing() {
@@ -181,9 +203,10 @@
     private final VersionedBroadcastListener mCarrierConfigChange;
     // TODO: Delete SimChangeListener; it's obsolete.
     private final SimChangeListener mSimChange;
+    private final TetheringDependencies mDeps;
 
     private volatile TetheringConfiguration mConfig;
-    private String mCurrentUpstreamIface;
+    private InterfaceSet mCurrentUpstreamIfaceSet;
     private Notification.Builder mTetheredNotificationBuilder;
     private int mLastNotificationId;
 
@@ -202,21 +225,22 @@
         mPolicyManager = policyManager;
         mLooper = looper;
         mSystemProperties = systemProperties;
+        mDeps = deps;
 
         mPublicSync = new Object();
 
         mTetherStates = new ArrayMap<>();
 
-        mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
+        mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
         final Handler smHandler = mTetherMasterSM.getHandler();
         mOffloadController = new OffloadController(smHandler,
-                deps.getOffloadHardwareInterface(smHandler, mLog),
+                mDeps.getOffloadHardwareInterface(smHandler, mLog),
                 mContext.getContentResolver(), mNMService,
                 mLog);
-        mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
-                mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+        mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
+                TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
 
         IntentFilter filter = new IntentFilter();
@@ -239,7 +263,7 @@
         mStateReceiver = new StateReceiver();
         filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_STATE);
-        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(CONNECTIVITY_ACTION);
         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
@@ -261,12 +285,6 @@
         updateConfiguration();
     }
 
-    // We can't do this once in the Tethering() constructor and cache the value, because the
-    // CONNECTIVITY_SERVICE is registered only after the Tethering() constructor has completed.
-    private ConnectivityManager getConnectivityManager() {
-        return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-    }
-
     private WifiManager getWifiManager() {
         return (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
     }
@@ -292,7 +310,7 @@
             if (up) {
                 maybeTrackNewInterfaceLocked(iface);
             } else {
-                if (ifaceNameToType(iface) == ConnectivityManager.TETHERING_BLUETOOTH) {
+                if (ifaceNameToType(iface) == TETHERING_BLUETOOTH) {
                     stopTrackingInterfaceLocked(iface);
                 } else {
                     // Ignore usb0 down after enabling RNDIS.
@@ -314,13 +332,13 @@
         final TetheringConfiguration cfg = mConfig;
 
         if (cfg.isWifi(iface)) {
-            return ConnectivityManager.TETHERING_WIFI;
+            return TETHERING_WIFI;
         } else if (cfg.isUsb(iface)) {
-            return ConnectivityManager.TETHERING_USB;
+            return TETHERING_USB;
         } else if (cfg.isBluetooth(iface)) {
-            return ConnectivityManager.TETHERING_BLUETOOTH;
+            return TETHERING_BLUETOOTH;
         }
-        return ConnectivityManager.TETHERING_INVALID;
+        return TETHERING_INVALID;
     }
 
     @Override
@@ -422,26 +440,26 @@
         boolean isProvisioningRequired = enable && isTetherProvisioningRequired();
         int result;
         switch (type) {
-            case ConnectivityManager.TETHERING_WIFI:
+            case TETHERING_WIFI:
                 result = setWifiTethering(enable);
-                if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
                     scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
-            case ConnectivityManager.TETHERING_USB:
+            case TETHERING_USB:
                 result = setUsbTethering(enable);
-                if (isProvisioningRequired && result == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) {
                     scheduleProvisioningRechecks(type);
                 }
                 sendTetherResult(receiver, result);
                 break;
-            case ConnectivityManager.TETHERING_BLUETOOTH:
+            case TETHERING_BLUETOOTH:
                 setBluetoothTethering(enable, receiver);
                 break;
             default:
                 Log.w(TAG, "Invalid tether type.");
-                sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE);
+                sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
         }
     }
 
@@ -452,7 +470,7 @@
     }
 
     private int setWifiTethering(final boolean enable) {
-        int rval = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+        int rval = TETHER_ERROR_MASTER_ERROR;
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mPublicSync) {
@@ -460,7 +478,7 @@
                 final WifiManager mgr = getWifiManager();
                 if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
                     (!enable && mgr.stopSoftAp())) {
-                    rval = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+                    rval = TETHER_ERROR_NO_ERROR;
                 }
             }
         } finally {
@@ -474,7 +492,7 @@
         if (adapter == null || !adapter.isEnabled()) {
             Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " +
                     (adapter == null));
-            sendTetherResult(receiver, ConnectivityManager.TETHER_ERROR_SERVICE_UNAVAIL);
+            sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL);
             return;
         }
 
@@ -487,12 +505,12 @@
                 ((BluetoothPan) proxy).setBluetoothTethering(enable);
                 // TODO: Enabling bluetooth tethering can fail asynchronously here.
                 // We should figure out a way to bubble up that failure instead of sending success.
-                int result = ((BluetoothPan) proxy).isTetheringOn() == enable ?
-                        ConnectivityManager.TETHER_ERROR_NO_ERROR :
-                        ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
+                final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
+                        ? TETHER_ERROR_NO_ERROR
+                        : TETHER_ERROR_MASTER_ERROR;
                 sendTetherResult(receiver, result);
                 if (enable && isTetherProvisioningRequired()) {
-                    scheduleProvisioningRechecks(ConnectivityManager.TETHERING_BLUETOOTH);
+                    scheduleProvisioningRechecks(TETHERING_BLUETOOTH);
                 }
                 adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
             }
@@ -506,8 +524,8 @@
 
     private void sendUiTetherProvisionIntent(int type, ResultReceiver receiver) {
         Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
-        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -530,7 +548,7 @@
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
                 // If provisioning is successful, enable tethering, otherwise just send the error.
-                if (resultCode == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                if (resultCode == TETHER_ERROR_NO_ERROR) {
                     enableTetheringInternal(type, true, receiver);
                 } else {
                     sendTetherResult(receiver, resultCode);
@@ -550,8 +568,8 @@
 
     private void scheduleProvisioningRechecks(int type) {
         Intent intent = new Intent();
-        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(ConnectivityManager.EXTRA_SET_ALARM, true);
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_SET_ALARM, true);
         intent.setComponent(TETHER_SERVICE);
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -568,9 +586,9 @@
 
     private void sendSilentTetherProvisionIntent(int type, ResultReceiver receiver) {
         Intent intent = new Intent();
-        intent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, type);
-        intent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
-        intent.putExtra(ConnectivityManager.EXTRA_PROVISION_CALLBACK, receiver);
+        intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+        intent.putExtra(EXTRA_RUN_PROVISION, true);
+        intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
         intent.setComponent(TETHER_SERVICE);
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -581,9 +599,9 @@
     }
 
     private void cancelTetherProvisioningRechecks(int type) {
-        if (getConnectivityManager().isTetheringSupported()) {
+        if (mDeps.isTetheringSupported()) {
             Intent intent = new Intent();
-            intent.putExtra(ConnectivityManager.EXTRA_REM_TETHER_TYPE, type);
+            intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
             intent.setComponent(TETHER_SERVICE);
             final long ident = Binder.clearCallingIdentity();
             try {
@@ -598,8 +616,8 @@
     // TODO: De-duplicate with above code, where possible.
     private void startProvisionIntent(int tetherType) {
         final Intent startProvIntent = new Intent();
-        startProvIntent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
-        startProvIntent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+        startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
+        startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
         startProvIntent.setComponent(TETHER_SERVICE);
         mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
     }
@@ -614,13 +632,13 @@
             TetherState tetherState = mTetherStates.get(iface);
             if (tetherState == null) {
                 Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
-                return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+                return TETHER_ERROR_UNKNOWN_IFACE;
             }
             // Ignore the error status of the interface.  If the interface is available,
             // the errors are referring to past tethering attempts anyway.
             if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
                 Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
-                return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+                return TETHER_ERROR_UNAVAIL_IFACE;
             }
             // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's
             // queue but not yet processed, this will be a no-op and it will not
@@ -629,7 +647,7 @@
             // TODO: reexamine the threading and messaging model.
             tetherState.stateMachine.sendMessage(
                     TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState);
-            return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+            return TETHER_ERROR_NO_ERROR;
         }
     }
 
@@ -639,22 +657,22 @@
             TetherState tetherState = mTetherStates.get(iface);
             if (tetherState == null) {
                 Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
-                return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+                return TETHER_ERROR_UNKNOWN_IFACE;
             }
             if (!tetherState.isCurrentlyServing()) {
                 Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
-                return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
+                return TETHER_ERROR_UNAVAIL_IFACE;
             }
             tetherState.stateMachine.sendMessage(
                     TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
-            return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+            return TETHER_ERROR_NO_ERROR;
         }
     }
 
     public void untetherAll() {
-        stopTethering(ConnectivityManager.TETHERING_WIFI);
-        stopTethering(ConnectivityManager.TETHERING_USB);
-        stopTethering(ConnectivityManager.TETHERING_BLUETOOTH);
+        stopTethering(TETHERING_WIFI);
+        stopTethering(TETHERING_USB);
+        stopTethering(TETHERING_BLUETOOTH);
     }
 
     public int getLastTetherError(String iface) {
@@ -663,7 +681,7 @@
             if (tetherState == null) {
                 Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface +
                         ", ignoring");
-                return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
+                return TETHER_ERROR_UNKNOWN_IFACE;
             }
             return tetherState.lastError;
         }
@@ -671,7 +689,7 @@
 
     // TODO: Figure out how to update for local hotspot mode interfaces.
     private void sendTetherStateChangedBroadcast() {
-        if (!getConnectivityManager().isTetheringSupported()) return;
+        if (!mDeps.isTetheringSupported()) return;
 
         final ArrayList<String> availableList = new ArrayList<>();
         final ArrayList<String> tetherList = new ArrayList<>();
@@ -688,7 +706,7 @@
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
                 String iface = mTetherStates.keyAt(i);
-                if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
                     erroredList.add(iface);
                 } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
                     availableList.add(iface);
@@ -706,13 +724,13 @@
                 }
             }
         }
-        final Intent bcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        final Intent bcast = new Intent(ACTION_TETHER_STATE_CHANGED);
         bcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
                 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER, availableList);
-        bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
-        bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, tetherList);
-        bcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER, erroredList);
+        bcast.putStringArrayListExtra(EXTRA_AVAILABLE_TETHER, availableList);
+        bcast.putStringArrayListExtra(EXTRA_ACTIVE_LOCAL_ONLY, localOnlyList);
+        bcast.putStringArrayListExtra(EXTRA_ACTIVE_TETHER, tetherList);
+        bcast.putStringArrayListExtra(EXTRA_ERRORED_TETHER, erroredList);
         mContext.sendStickyBroadcastAsUser(bcast, UserHandle.ALL);
         if (DBG) {
             Log.d(TAG, String.format(
@@ -835,7 +853,7 @@
 
             if (action.equals(UsbManager.ACTION_USB_STATE)) {
                 handleUsbAction(intent);
-            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+            } else if (action.equals(CONNECTIVITY_ACTION)) {
                 handleConnectivityAction(intent);
             } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
                 handleWifiApAction(intent);
@@ -846,8 +864,8 @@
         }
 
         private void handleConnectivityAction(Intent intent) {
-            final NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
-                    ConnectivityManager.EXTRA_NETWORK_INFO);
+            final NetworkInfo networkInfo =
+                    (NetworkInfo) intent.getParcelableExtra(EXTRA_NETWORK_INFO);
             if (networkInfo == null ||
                     networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
                 return;
@@ -883,14 +901,10 @@
             synchronized (Tethering.this.mPublicSync) {
                 if (!usbConnected && mRndisEnabled) {
                     // Turn off tethering if it was enabled and there is a disconnect.
-                    tetherMatchingInterfaces(
-                            IControlsTethering.STATE_AVAILABLE,
-                            ConnectivityManager.TETHERING_USB);
+                    tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB);
                 } else if (usbConfigured && rndisEnabled) {
                     // Tether if rndis is enabled and usb is configured.
-                    tetherMatchingInterfaces(
-                            IControlsTethering.STATE_TETHERED,
-                            ConnectivityManager.TETHERING_USB);
+                    tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB);
                 }
                 mRndisEnabled = usbConfigured && rndisEnabled;
             }
@@ -971,7 +985,7 @@
 
         for (int i = 0; i < mTetherStates.size(); i++) {
             TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine;
-            if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+            if (tism.interfaceType() == TETHERING_WIFI) {
                 tism.unwanted();
                 return;
             }
@@ -999,7 +1013,7 @@
         }
 
         if (!TextUtils.isEmpty(ifname)) {
-            maybeTrackNewInterfaceLocked(ifname, ConnectivityManager.TETHERING_WIFI);
+            maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI);
             changeInterfaceState(ifname, ipServingMode);
         } else {
             mLog.e(String.format(
@@ -1058,7 +1072,7 @@
                 Log.wtf(TAG, "Unknown interface state: " + requestedState);
                 return;
         }
-        if (result != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+        if (result != TETHER_ERROR_NO_ERROR) {
             Log.e(TAG, "unable start or stop tethering on iface " + ifname);
             return;
         }
@@ -1099,7 +1113,7 @@
         synchronized (mPublicSync) {
             usbManager.setCurrentFunction(enable ? UsbManager.USB_FUNCTION_RNDIS : null, false);
         }
-        return ConnectivityManager.TETHER_ERROR_NO_ERROR;
+        return TETHER_ERROR_NO_ERROR;
     }
 
     // TODO review API - figure out how to delete these entirely.
@@ -1138,7 +1152,7 @@
         synchronized (mPublicSync) {
             for (int i = 0; i < mTetherStates.size(); i++) {
                 TetherState tetherState = mTetherStates.valueAt(i);
-                if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+                if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
                     list.add(mTetherStates.keyAt(i));
                 }
             }
@@ -1159,12 +1173,11 @@
     }
 
     // Needed because the canonical source of upstream truth is just the
-    // upstream interface name, |mCurrentUpstreamIface|.  This is ripe for
-    // future simplification, once the upstream Network is canonical.
+    // upstream interface set, |mCurrentUpstreamIfaceSet|.
     private boolean pertainsToCurrentUpstream(NetworkState ns) {
-        if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
+        if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
             for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
-                if (mCurrentUpstreamIface.equals(ifname)) {
+                if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
                     return true;
                 }
             }
@@ -1185,7 +1198,7 @@
                 }
                 String iface = mTetherStates.keyAt(i);
                 int interfaceType = ifaceNameToType(iface);
-                if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+                if (interfaceType != TETHERING_INVALID) {
                     tethered.add(interfaceType);
                 }
             }
@@ -1240,7 +1253,7 @@
 
         private static final int UPSTREAM_SETTLE_TIME_MS     = 10000;
 
-        TetherMasterSM(String name, Looper looper) {
+        TetherMasterSM(String name, Looper looper, TetheringDependencies deps) {
             super(name, looper);
 
             mInitialState = new InitialState();
@@ -1260,7 +1273,7 @@
             addState(mSetDnsForwardersErrorState);
 
             mNotifyList = new ArrayList<>();
-            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+            mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
             mOffload = new OffloadWrapper();
 
             setInitialState(mInitialState);
@@ -1359,31 +1372,27 @@
         }
 
         protected void setUpstreamNetwork(NetworkState ns) {
-            String iface = null;
+            InterfaceSet ifaces = null;
             if (ns != null) {
                 // Find the interface with the default IPv4 route. It may be the
                 // interface described by linkProperties, or one of the interfaces
                 // stacked on top of it.
                 mLog.i("Looking for default routes on: " + ns.linkProperties);
-                final String iface4 = getIPv4DefaultRouteInterface(ns);
-                final String iface6 = getIPv6DefaultRouteInterface(ns);
-                mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
-
-                iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
+                ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
+                mLog.i("Found upstream interface(s): " + ifaces);
             }
 
-            if (iface != null) {
+            if (ifaces != null) {
                 setDnsForwarders(ns.network, ns.linkProperties);
             }
-            notifyDownstreamsOfNewUpstreamIface(iface);
+            notifyDownstreamsOfNewUpstreamIface(ifaces);
             if (ns != null && pertainsToCurrentUpstream(ns)) {
                 // If we already have NetworkState for this network examine
                 // it immediately, because there likely will be no second
                 // EVENT_ON_AVAILABLE (it was already received).
                 handleNewUpstreamNetworkState(ns);
-            } else if (mCurrentUpstreamIface == null) {
-                // There are no available upstream networks, or none that
-                // have an IPv4 default route (current metric for success).
+            } else if (mCurrentUpstreamIfaceSet == null) {
+                // There are no available upstream networks.
                 handleNewUpstreamNetworkState(null);
             }
         }
@@ -1410,12 +1419,10 @@
             }
         }
 
-        protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
-            mLog.log("Notifying downstreams of upstream=" + ifaceName);
-            mCurrentUpstreamIface = ifaceName;
+        protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
+            mCurrentUpstreamIfaceSet = ifaces;
             for (TetherInterfaceStateMachine sm : mNotifyList) {
-                sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                        ifaceName);
+                sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
             }
         }
 
@@ -1441,7 +1448,7 @@
             }
 
             // If this is a Wi-Fi interface, notify WifiManager of the active serving state.
-            if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+            if (who.interfaceType() == TETHERING_WIFI) {
                 final WifiManager mgr = getWifiManager();
                 final String iface = who.interfaceName();
                 switch (mode) {
@@ -1465,8 +1472,8 @@
             mForwardedDownstreams.remove(who);
 
             // If this is a Wi-Fi interface, tell WifiManager of any errors.
-            if (who.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
-                if (who.lastError() != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+            if (who.interfaceType() == TETHERING_WIFI) {
+                if (who.lastError() != TETHER_ERROR_NO_ERROR) {
                     getWifiManager().updateInterfaceIpState(
                             who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
                 }
@@ -1487,7 +1494,7 @@
                 // For example, after CONNECTIVITY_ACTION listening is removed, here
                 // is where we could observe a Wi-Fi network becoming available and
                 // passing validation.
-                if (mCurrentUpstreamIface == null) {
+                if (mCurrentUpstreamIfaceSet == null) {
                     // If we have no upstream interface, try to run through upstream
                     // selection again.  If, for example, IPv4 connectivity has shown up
                     // after IPv6 (e.g., 464xlat became available) we want the chance to
@@ -1511,8 +1518,7 @@
                     handleNewUpstreamNetworkState(ns);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
-                    setDnsForwarders(ns.network, ns.linkProperties);
-                    handleNewUpstreamNetworkState(ns);
+                    chooseUpstreamType(false);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LOST:
                     // TODO: Re-evaluate possible upstreams. Currently upstream
@@ -1585,7 +1591,7 @@
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
                         who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                                mCurrentUpstreamIface);
+                                mCurrentUpstreamIfaceSet);
                         // If there has been a change and an upstream is now
                         // desired, kick off the selection process.
                         final boolean previousUpstreamWanted = updateUpstreamWanted();
@@ -1672,7 +1678,7 @@
                         who.sendMessage(mErrorNotification);
                         break;
                     case CMD_CLEAR_ERROR:
-                        mErrorNotification = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+                        mErrorNotification = TETHER_ERROR_NO_ERROR;
                         transitionTo(mInitialState);
                         break;
                     default:
@@ -1863,7 +1869,7 @@
                 pw.println(" - lastError = " + tetherState.lastError);
             }
             pw.println("Upstream wanted: " + upstreamWanted());
-            pw.println("Current upstream interface: " + mCurrentUpstreamIface);
+            pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
             pw.decreaseIndent();
         }
 
@@ -1934,7 +1940,7 @@
         // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
         // Thus we give a chance for TetherMasterSM to recover to InitialState
         // by sending CMD_CLEAR_ERROR
-        if (error == ConnectivityManager.TETHER_ERROR_MASTER_ERROR) {
+        if (error == TETHER_ERROR_MASTER_ERROR) {
             mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
         }
         int which;
@@ -1978,7 +1984,7 @@
     private void maybeTrackNewInterfaceLocked(final String iface) {
         // If we don't care about this type of interface, ignore.
         final int interfaceType = ifaceNameToType(iface);
-        if (interfaceType == ConnectivityManager.TETHERING_INVALID) {
+        if (interfaceType == TETHERING_INVALID) {
             mLog.log(iface + " is not a tetherable iface, ignoring");
             return;
         }
@@ -1996,7 +2002,7 @@
         final TetherState tetherState = new TetherState(
                 new TetherInterfaceStateMachine(
                     iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
-                    makeControlCallback(iface)));
+                    makeControlCallback(iface), mDeps));
         mTetherStates.put(iface, tetherState);
         tetherState.stateMachine.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c9bdcf1..2fda08e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,6 +151,13 @@
                 .multiply(BigInteger.valueOf(howManyPercentIsMost))
                 .divide(BigInteger.valueOf(100));
     }
+    // How many routes to evaluate before bailing and declaring this Vpn should provide
+    // the INTERNET capability. This is necessary because computing the adress space is
+    // O(n²) and this is running in the system service, so a limit is needed to alleviate
+    // the risk of attack.
+    // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
+    // is actually O(n²)+O(n²).
+    private static final int MAX_ROUTES_TO_EVALUATE = 150;
 
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
@@ -862,10 +869,12 @@
      */
     @VisibleForTesting
     static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+        final List<RouteInfo> routes = lp.getAllRoutes();
+        if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
         final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
         TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
         TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
-        for (final RouteInfo route : lp.getAllRoutes()) {
+        for (final RouteInfo route : routes) {
             IpPrefix destination = route.getDestination();
             if (destination.isIPv4()) {
                 ipv4Prefixes.add(destination);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 518f6c1..ba67c94 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -30,10 +30,8 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Random;
 
@@ -119,7 +117,7 @@
         if (VDBG) {
             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
         }
-        if (!canTetherIPv6(ns, mLog)) {
+        if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
             stopIPv6TetheringOnAllInterfaces();
             setUpstreamNetworkState(null);
             return;
@@ -208,70 +206,6 @@
         return null;
     }
 
-    private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
-        // Broadly speaking:
-        //
-        //     [1] does the upstream have an IPv6 default route?
-        //
-        // and
-        //
-        //     [2] does the upstream have one or more global IPv6 /64s
-        //         dedicated to this device?
-        //
-        // In lieu of Prefix Delegation and other evaluation of whether a
-        // prefix may or may not be dedicated to this device, for now just
-        // check whether the upstream is TRANSPORT_CELLULAR. This works
-        // because "[t]he 3GPP network allocates each default bearer a unique
-        // /64 prefix", per RFC 6459, Section 5.2.
-
-        final boolean canTether =
-                (ns != null) && (ns.network != null) &&
-                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
-                // At least one upstream DNS server:
-                ns.linkProperties.isProvisioned() &&
-                // Minimal amount of IPv6 provisioning:
-                ns.linkProperties.hasIPv6DefaultRoute() &&
-                ns.linkProperties.hasGlobalIPv6Address() &&
-                // Temporary approximation of "dedicated prefix":
-                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
-        // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
-        // tethering with 464xlat involved). TODO: Rectify this shortcoming,
-        // likely by calling NetworkManagementService#startInterfaceForwarding()
-        // for all upstream interfaces.
-        RouteInfo v4default = null;
-        RouteInfo v6default = null;
-        if (canTether) {
-            for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
-                if (r.isIPv4Default()) {
-                    v4default = r;
-                } else if (r.isIPv6Default()) {
-                    v6default = r;
-                }
-
-                if (v4default != null && v6default != null) {
-                    break;
-                }
-            }
-        }
-
-        final boolean supportedConfiguration =
-                (v4default != null) && (v6default != null) &&
-                (v4default.getInterface() != null) &&
-                v4default.getInterface().equals(v6default.getInterface());
-
-        final boolean outcome = canTether && supportedConfiguration;
-
-        if (ns == null) {
-            sharedLog.log("No available upstream.");
-        } else {
-            sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
-                    (outcome ? "available" : "not available"), toDebugString(ns)));
-        }
-
-        return outcome;
-    }
-
     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
         final LinkProperties v6only = new LinkProperties();
         if (lp == null) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..5ed14a0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -31,7 +31,7 @@
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
+import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -49,12 +49,12 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Random;
+import java.util.Set;
 
 /**
  * Provides the interface to IP-layer serving functionality for a given network
@@ -117,9 +117,11 @@
     private final int mInterfaceType;
     private final LinkProperties mLinkProperties;
 
+    private final TetheringDependencies mDeps;
+
     private int mLastError;
     private int mServingMode;
-    private String mMyUpstreamIfaceName;  // may change over time
+    private InterfaceSet mUpstreamIfaceSet;  // may change over time
     private InterfaceParams mInterfaceParams;
     // TODO: De-duplicate this with mLinkProperties above. Currently, these link
     // properties are those selected by the IPv6TetheringCoordinator and relayed
@@ -134,18 +136,19 @@
     public TetherInterfaceStateMachine(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
             INetworkManagementService nMService, INetworkStatsService statsService,
-            IControlsTethering tetherController) {
+            IControlsTethering tetherController,
+            TetheringDependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNMService = nMService;
-        // TODO: This should be passed in for testability.
-        mNetd = NetdService.getInstance();
+        mNetd = deps.getNetdService();
         mStatsService = statsService;
         mTetherController = tetherController;
         mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
+        mDeps = deps;
         resetLinkProperties();
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
         mServingMode = IControlsTethering.STATE_AVAILABLE;
@@ -246,16 +249,14 @@
     }
 
     private boolean startIPv6() {
-        // TODO: Refactor for better testability.  This is one of the things
-        // that prohibits unittesting IPv6 tethering setup.
-        mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+        mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
         if (mInterfaceParams == null) {
             mLog.e("Failed to find InterfaceParams");
             stopIPv6();
             return false;
         }
 
-        mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
+        mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
         if (!mRaDaemon.start()) {
             stopIPv6();
             return false;
@@ -621,10 +622,10 @@
         }
 
         private void cleanupUpstream() {
-            if (mMyUpstreamIfaceName == null) return;
+            if (mUpstreamIfaceSet == null) return;
 
-            cleanupUpstreamInterface(mMyUpstreamIfaceName);
-            mMyUpstreamIfaceName = null;
+            for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
+            mUpstreamIfaceSet = null;
         }
 
         private void cleanupUpstreamInterface(String upstreamIface) {
@@ -660,34 +661,66 @@
                     mLog.e("CMD_TETHER_REQUESTED while already tethering.");
                     break;
                 case CMD_TETHER_CONNECTION_CHANGED:
-                    String newUpstreamIfaceName = (String)(message.obj);
-                    if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
-                            (mMyUpstreamIfaceName != null &&
-                            mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+                    final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
+                    if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
                         if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
                         break;
                     }
-                    cleanupUpstream();
-                    if (newUpstreamIfaceName != null) {
+
+                    if (newUpstreamIfaceSet == null) {
+                        cleanupUpstream();
+                        break;
+                    }
+
+                    for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
+                        cleanupUpstreamInterface(removed);
+                    }
+
+                    final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
+                    // This makes the call to cleanupUpstream() in the error
+                    // path for any interface neatly cleanup all the interfaces.
+                    mUpstreamIfaceSet = newUpstreamIfaceSet;
+
+                    for (String ifname : added) {
                         try {
-                            mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
-                            mNMService.startInterfaceForwarding(mIfaceName,
-                                    newUpstreamIfaceName);
+                            mNMService.enableNat(mIfaceName, ifname);
+                            mNMService.startInterfaceForwarding(mIfaceName, ifname);
                         } catch (Exception e) {
                             mLog.e("Exception enabling NAT: " + e);
-                            cleanupUpstreamInterface(newUpstreamIfaceName);
+                            cleanupUpstream();
                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
                             transitionTo(mInitialState);
                             return true;
                         }
                     }
-                    mMyUpstreamIfaceName = newUpstreamIfaceName;
                     break;
                 default:
                     return false;
             }
             return true;
         }
+
+        private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
+            if (mUpstreamIfaceSet == null && newIfaces == null) return true;
+            if (mUpstreamIfaceSet != null && newIfaces != null) {
+                return mUpstreamIfaceSet.equals(newIfaces);
+            }
+            return false;
+        }
+
+        private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
+            if (mUpstreamIfaceSet == null) return new HashSet<>();
+
+            final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
+            removed.removeAll(newIfaces.ifnames);
+            return removed;
+        }
+
+        private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
+            final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
+            if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
+            return added;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index b8174b6..0ac7a36 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,9 +16,18 @@
 
 package com.android.server.connectivity.tethering;
 
+import android.content.Context;
+import android.net.INetd;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetdService;
 import android.os.Handler;
 import android.net.util.SharedLog;
 
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+
 
 /**
  * Capture tethering dependencies, for injection.
@@ -29,4 +38,30 @@
     public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
         return new OffloadHardwareInterface(h, log);
     }
+
+    public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
+            SharedLog log, int what) {
+        return new UpstreamNetworkMonitor(ctx, target, log, what);
+    }
+
+    public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+            ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+        return new IPv6TetheringCoordinator(notifyList, log);
+    }
+
+    public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+        return new RouterAdvertisementDaemon(ifParams);
+    }
+
+    public InterfaceParams getInterfaceParams(String ifName) {
+        return InterfaceParams.getByName(ifName);
+    }
+
+    public INetd getNetdService() {
+        return NetdService.getInstance();
+    }
+
+    public boolean isTetheringSupported() {
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
new file mode 100644
index 0000000..6c7ff91
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+/**
+ * @hide
+ */
+public final class TetheringInterfaceUtils {
+    /**
+     * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
+     * @return null if there is no usable interface, or a set of at least one interface otherwise.
+     */
+    public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
+        if (ns == null) {
+            return null;
+        }
+
+        final LinkProperties lp = ns.linkProperties;
+        final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
+        final String if6 = getIPv6Interface(ns);
+
+        return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
+    }
+
+    /**
+     * Get the upstream interface for IPv6 tethering.
+     * @return null if there is no usable interface, or the interface name otherwise.
+     */
+    public static @Nullable String getIPv6Interface(NetworkState ns) {
+        // Broadly speaking:
+        //
+        //     [1] does the upstream have an IPv6 default route?
+        //
+        // and
+        //
+        //     [2] does the upstream have one or more global IPv6 /64s
+        //         dedicated to this device?
+        //
+        // In lieu of Prefix Delegation and other evaluation of whether a
+        // prefix may or may not be dedicated to this device, for now just
+        // check whether the upstream is TRANSPORT_CELLULAR. This works
+        // because "[t]he 3GPP network allocates each default bearer a unique
+        // /64 prefix", per RFC 6459, Section 5.2.
+        final boolean canTether =
+                (ns != null) && (ns.network != null) &&
+                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+                // At least one upstream DNS server:
+                ns.linkProperties.hasIPv6DnsServer() &&
+                // Minimal amount of IPv6 provisioning:
+                ns.linkProperties.hasGlobalIPv6Address() &&
+                // Temporary approximation of "dedicated prefix":
+                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        return canTether
+                ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
+                : null;
+    }
+
+    private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
+        final RouteInfo ri = (lp != null)
+                ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
+                : null;
+        return (ri != null) ? ri.getInterface() : null;
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 8298127..da40692 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -27,6 +27,7 @@
 import static android.net.ConnectivityManager.isNetworkTypeMobile;
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.INTERFACES_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.ROAMING_ALL;
 import static android.net.NetworkStats.SET_ALL;
@@ -34,6 +35,7 @@
 import static android.net.NetworkStats.SET_FOREGROUND;
 import static android.net.NetworkStats.STATS_PER_IFACE;
 import static android.net.NetworkStats.STATS_PER_UID;
+import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.NetworkStatsHistory.FIELD_ALL;
@@ -128,6 +130,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.net.VpnInfo;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -726,7 +729,8 @@
         final long token = Binder.clearCallingIdentity();
         final NetworkStats networkLayer;
         try {
-            networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid);
+            networkLayer = mNetworkManager.getNetworkStatsUidDetail(uid,
+                    NetworkStats.INTERFACES_ALL);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -748,6 +752,18 @@
     }
 
     @Override
+    public NetworkStats getDetailedUidStats(String[] requiredIfaces) {
+        try {
+            final String[] ifacesToQuery =
+                    NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces);
+            return getNetworkStatsUidDetail(ifacesToQuery);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error compiling UID stats", e);
+            return new NetworkStats(0L, 0);
+        }
+    }
+
+    @Override
     public String[] getMobileIfaces() {
         return mMobileIfaces;
     }
@@ -1109,6 +1125,8 @@
                         if (isMobile) {
                             mobileIfaces.add(stackedIface);
                         }
+
+                        NetworkStatsFactory.noteStackedIface(stackedIface, baseIface);
                     }
                 }
             }
@@ -1130,7 +1148,7 @@
     private void recordSnapshotLocked(long currentTime) throws RemoteException {
         // snapshot and record current counters; read UID stats first to
         // avoid over counting dev stats.
-        final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
+        final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
         final NetworkStats xtSnapshot = getNetworkStatsXt();
         final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
 
@@ -1464,12 +1482,21 @@
      * Return snapshot of current UID statistics, including any
      * {@link TrafficStats#UID_TETHERING}, video calling data usage, and {@link #mUidOperations}
      * values.
+     *
+     * @param ifaces A list of interfaces the stats should be restricted to, or
+     *               {@link NetworkStats#INTERFACES_ALL}.
      */
-    private NetworkStats getNetworkStatsUidDetail() throws RemoteException {
-        final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL);
+    private NetworkStats getNetworkStatsUidDetail(String[] ifaces)
+            throws RemoteException {
+
+        // TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here.
+        final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL,
+                ifaces);
 
         // fold tethering stats and operations into uid snapshot
         final NetworkStats tetherSnapshot = getNetworkStatsTethering(STATS_PER_UID);
+        tetherSnapshot.filter(UID_ALL, ifaces, TAG_ALL);
+        NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, tetherSnapshot);
         uidSnapshot.combineAllValues(tetherSnapshot);
 
         final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
@@ -1478,8 +1505,11 @@
         // fold video calling data usage stats into uid snapshot
         final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
         if (vtStats != null) {
+            vtStats.filter(UID_ALL, ifaces, TAG_ALL);
+            NetworkStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats);
             uidSnapshot.combineAllValues(vtStats);
         }
+
         uidSnapshot.combineAllValues(mUidOperations);
 
         return uidSnapshot;
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 3814ef3..46f39f2 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -463,10 +463,17 @@
 
         if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
             mAbortPostBootUpdate.set(true);
+
+            // Do not reschedule.
+            // TODO: We should reschedule if we didn't process all apps, yet.
+            return false;
         } else {
             mAbortIdleOptimization.set(true);
+
+            // Reschedule the run.
+            // TODO: Should this be dependent on the stop reason?
+            return true;
         }
-        return false;
     }
 
     private void notifyPinService(ArraySet<String> updatedPackages) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 168b10b..73f4274 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -40,7 +40,7 @@
 
     /* ***************************************************************************
      * IMPORTANT: These values are passed to native code. Keep them in sync with
-     * frameworks/native/cmds/installd/installd.h
+     * frameworks/native/cmds/installd/installd_constants.h
      * **************************************************************************/
     /** Application should be visible to everyone */
     public static final int DEXOPT_PUBLIC         = 1 << 1;
@@ -64,6 +64,8 @@
     public static final int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
     /** Indicates that dexopt should convert to CompactDex. */
     public static final int DEXOPT_GENERATE_COMPACT_DEX = 1 << 11;
+    /** Indicates that dexopt should generate an app image */
+    public static final int DEXOPT_GENERATE_APP_IMAGE = 1 << 12;
 
     // NOTE: keep in sync with installd
     public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index faaa3ba..88caf32 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -267,7 +267,7 @@
                 final StringBuilder builder = new StringBuilder();
 
                 // The current version.
-                builder.append("8 ");
+                builder.append("9 ");
 
                 builder.append("dexopt");
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 5ea778b..f9e11f9 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -47,6 +47,8 @@
 
 import dalvik.system.DexFile;
 
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_NONE;
+
 import static com.android.server.pm.Installer.DEXOPT_BOOTCOMPLETE;
 import static com.android.server.pm.Installer.DEXOPT_DEBUGGABLE;
 import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
@@ -58,6 +60,7 @@
 import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
 import static com.android.server.pm.Installer.DEXOPT_ENABLE_HIDDEN_API_CHECKS;
 import static com.android.server.pm.Installer.DEXOPT_GENERATE_COMPACT_DEX;
+import static com.android.server.pm.Installer.DEXOPT_GENERATE_APP_IMAGE;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 
@@ -452,13 +455,15 @@
             pw.increaseIndent();
 
             for (String isa : dexCodeInstructionSets) {
-                String status = null;
                 try {
-                    status = DexFile.getDexFileStatus(path, isa);
+                    String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
+                    String compilationStatus = status[0];
+                    String compilationReason = status[1];
+                    pw.println(isa + ": [status=" + compilationStatus
+                            +"] reason=[" + compilationReason + "]");
                 } catch (IOException ioe) {
-                     status = "[Exception]: " + ioe.getMessage();
+                    pw.println(isa + ": [Exception]: " + ioe.getMessage());
                 }
-                pw.println(isa + ": " + status);
             }
 
             if (useInfo.isUsedByOtherApps(path)) {
@@ -519,6 +524,10 @@
         return getDexFlags(pkg.applicationInfo, compilerFilter, options);
     }
 
+    private boolean isAppImageEnabled() {
+        return SystemProperties.get("dalvik.vm.appimageformat", "").length() > 0;
+    }
+
     private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
         int flags = info.flags;
         boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
@@ -532,7 +541,10 @@
         int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
         // Some apps are executed with restrictions on hidden API usage. If this app is one
         // of them, pass a flag to dexopt to enable the same restrictions during compilation.
-        int hiddenApiFlag = info.isAllowedToUseHiddenApi() ? 0 : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
+        // TODO we should pass the actual flag value to dexopt, rather than assuming blacklist
+        int hiddenApiFlag = info.getHiddenApiEnforcementPolicy() == HIDDEN_API_ENFORCEMENT_NONE
+                ? 0
+                : DEXOPT_ENABLE_HIDDEN_API_CHECKS;
         // Avoid generating CompactDex for modes that are latency critical.
         final int compilationReason = options.getCompilationReason();
         boolean generateCompactDex = true;
@@ -542,6 +554,14 @@
             case PackageManagerService.REASON_INSTALL:
                  generateCompactDex = false;
         }
+        // Use app images only if it is enabled and we are compiling
+        // profile-guided (so the app image doesn't conservatively contain all classes).
+        // If the app didn't request for the splits to be loaded in isolation or if it does not
+        // declare inter-split dependencies, then all the splits will be loaded in the base
+        // apk class loader (in the order of their definition, otherwise disable app images
+        // because they are unsupported for multiple class loaders. b/7269679
+        boolean generateAppImage = isProfileGuidedFilter && (info.splitDependencies == null ||
+                !info.requestsIsolatedSplitLoading()) && isAppImageEnabled();
         int dexFlags =
                 (isPublic ? DEXOPT_PUBLIC : 0)
                 | (debuggable ? DEXOPT_DEBUGGABLE : 0)
@@ -549,6 +569,7 @@
                 | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
                 | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0)
                 | (generateCompactDex ? DEXOPT_GENERATE_COMPACT_DEX : 0)
+                | (generateAppImage ? DEXOPT_GENERATE_APP_IMAGE : 0)
                 | hiddenApiFlag;
         return adjustDexoptFlags(dexFlags);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d1abd63..a54cf1a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3375,6 +3375,13 @@
         // "/data/system/package_cache/1"
         File cacheDir = FileUtils.createDir(cacheBaseDir, PACKAGE_PARSER_CACHE_VERSION);
 
+        if (cacheDir == null) {
+            // Something went wrong. Attempt to delete everything and return.
+            Slog.wtf(TAG, "Cache directory cannot be created - wiping base dir " + cacheBaseDir);
+            FileUtils.deleteContentsAndDir(cacheBaseDir);
+            return null;
+        }
+
         // The following is a workaround to aid development on non-numbered userdebug
         // builds or cases where "adb sync" is used on userdebug builds. If we detect that
         // the system partition is newer.
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index d219476..0971058 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -392,6 +392,8 @@
                 KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR);
     }
 
+    private static final int USER_ACTIVITY_NOTIFICATION_DELAY = 200;
+
     /** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
     static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
 
@@ -677,6 +679,7 @@
 
     private boolean mPendingKeyguardOccluded;
     private boolean mKeyguardOccludedChanged;
+    private boolean mNotifyUserActivity;
 
     boolean mShowingDream;
     private boolean mLastShowingDream;
@@ -722,6 +725,9 @@
     // Behavior of Back button while in-call and screen on
     int mIncallBackBehavior;
 
+    // Whether system navigation keys are enabled
+    boolean mSystemNavigationKeysEnabled;
+
     Display mDisplay;
 
     private int mDisplayRotation;
@@ -831,6 +837,7 @@
     private static final int MSG_DISPATCH_BACK_KEY_TO_AUTOFILL = 24;
     private static final int MSG_SYSTEM_KEY_PRESS = 25;
     private static final int MSG_HANDLE_ALL_APPS = 26;
+    private static final int MSG_NOTIFY_USER_ACTIVITY = 27;
 
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS = 0;
     private static final int MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION = 1;
@@ -926,6 +933,13 @@
                 case MSG_HANDLE_ALL_APPS:
                     launchAllAppsAction();
                     break;
+                case MSG_NOTIFY_USER_ACTIVITY:
+                    removeMessages(MSG_NOTIFY_USER_ACTIVITY);
+                    Intent intent = new Intent(ACTION_USER_ACTIVITY_NOTIFICATION);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
+                            android.Manifest.permission.USER_ACTIVITY);
+                    break;
             }
         }
     }
@@ -978,6 +992,9 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.POLICY_CONTROL), false, this,
                     UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED), false, this,
+                    UserHandle.USER_ALL);
             updateSettings();
         }
 
@@ -2309,6 +2326,9 @@
                     Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
                     Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR_DEFAULT,
                     UserHandle.USER_CURRENT);
+            mSystemNavigationKeysEnabled = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
+                    0, UserHandle.USER_CURRENT) == 1;
 
             // Configure wake gesture.
             boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -6298,7 +6318,7 @@
         if (event.getAction() == KeyEvent.ACTION_UP) {
             if (!mAccessibilityManager.isEnabled()
                     || !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
-                if (areSystemNavigationKeysEnabled()) {
+                if (mSystemNavigationKeysEnabled) {
                     sendSystemKeyToStatusBarAsync(event.getKeyCode());
                 }
             }
@@ -7466,6 +7486,13 @@
         mHandler.sendEmptyMessage(MSG_HIDE_BOOT_MESSAGE);
     }
 
+    @Override
+    public void requestUserActivityNotification() {
+        if (!mNotifyUserActivity && !mHandler.hasMessages(MSG_NOTIFY_USER_ACTIVITY)) {
+            mNotifyUserActivity = true;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public void userActivity() {
@@ -7487,6 +7514,12 @@
                 mHandler.postDelayed(mScreenLockTimeout, mLockScreenTimeout);
             }
         }
+
+        if (mAwake && mNotifyUserActivity) {
+            mHandler.sendEmptyMessageDelayed(MSG_NOTIFY_USER_ACTIVITY,
+                    USER_ACTIVITY_NOTIFICATION_DELAY);
+            mNotifyUserActivity = false;
+        }
     }
 
     class ScreenLockTimeout implements Runnable {
@@ -7783,11 +7816,6 @@
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
     }
 
-    private boolean areSystemNavigationKeysEnabled() {
-        return Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
-    }
-
     @Override
     public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always) {
         if (!mVibrator.hasVibrator()) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 4698d72..4303352 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -301,6 +301,9 @@
     }
 
     private void drawSizeMismatchSnapshot(GraphicBuffer buffer) {
+        if (!mSurface.isValid()) {
+            throw new IllegalStateException("mSurface does not hold a valid surface.");
+        }
         final SurfaceSession session = new SurfaceSession(mSurface);
 
         // Keep a reference to it such that it doesn't get destroyed when finalized.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0abc847..671b00b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7301,6 +7301,15 @@
         mPolicy.registerShortcutKey(shortcutCode, shortcutKeyReceiver);
     }
 
+    @Override
+    public void requestUserActivityNotification() {
+        if (!checkCallingPermission(android.Manifest.permission.USER_ACTIVITY,
+                "requestUserActivityNotification()")) {
+            throw new SecurityException("Requires USER_ACTIVITY permission");
+        }
+        mPolicy.requestUserActivityNotification();
+    }
+
     void markForSeamlessRotation(WindowState w, boolean seamlesslyRotated) {
         if (seamlesslyRotated == w.mSeamlesslyRotated) {
             return;
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 7d9736e..92a09d3 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -16,21 +16,21 @@
 
 package android.net.apf;
 
+import static android.net.util.NetworkConstants.*;
 import static android.system.OsConstants.*;
-
 import static com.android.internal.util.BitUtils.bytesToBEInt;
 import static com.android.internal.util.BitUtils.getUint16;
 import static com.android.internal.util.BitUtils.getUint32;
 import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.uint16;
 import static com.android.internal.util.BitUtils.uint32;
-import static com.android.internal.util.BitUtils.uint8;
 
-import android.os.SystemClock;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
-import android.net.apf.ApfGenerator;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
 import android.net.ip.IpClient;
@@ -39,31 +39,29 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
 import android.net.util.InterfaceParams;
+import android.os.PowerManager;
+import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
-
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.lang.Thread;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
 import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
-
 import libcore.io.IoBridge;
 
 /**
@@ -215,10 +213,6 @@
             { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
 
     private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
-    private static final int ICMP6_ROUTER_SOLICITATION = 133;
-    private static final int ICMP6_ROUTER_ADVERTISEMENT = 134;
-    private static final int ICMP6_NEIGHBOR_SOLICITATION = 135;
-    private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136;
 
     // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
     private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
@@ -258,9 +252,26 @@
     private long mUniqueCounter;
     @GuardedBy("this")
     private boolean mMulticastFilter;
+    @GuardedBy("this")
+    private boolean mInDozeMode;
     private final boolean mDrop802_3Frames;
     private final int[] mEthTypeBlackList;
 
+    // Detects doze mode state transitions.
+    private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) {
+                PowerManager powerManager =
+                        (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+                final boolean deviceIdle = powerManager.isDeviceIdleMode();
+                setDozeMode(deviceIdle);
+            }
+        }
+    };
+    private final Context mContext;
+
     // Our IPv4 address, if we have just one, otherwise null.
     @GuardedBy("this")
     private byte[] mIPv4Address;
@@ -269,13 +280,14 @@
     private int mIPv4PrefixLength;
 
     @VisibleForTesting
-    ApfFilter(ApfConfiguration config, InterfaceParams ifParams,
+    ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
             IpClient.Callback ipClientCallback, IpConnectivityLog log) {
         mApfCapabilities = config.apfCapabilities;
         mIpClientCallback = ipClientCallback;
         mInterfaceParams = ifParams;
         mMulticastFilter = config.multicastFilter;
         mDrop802_3Frames = config.ieee802_3Filter;
+        mContext = context;
 
         // Now fill the black list from the passed array
         mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
@@ -284,6 +296,10 @@
 
         // TODO: ApfFilter should not generate programs until IpClient sends provisioning success.
         maybeStartFilter();
+
+        // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter.
+        mContext.registerReceiver(mDeviceIdleReceiver,
+                new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED));
     }
 
     private void log(String s) {
@@ -522,7 +538,7 @@
             // to our packet socket. b/29586253
             if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 ||
                     getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 ||
-                    getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMP6_ROUTER_ADVERTISEMENT) {
+                    getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) {
                 throw new InvalidRaException("Not an ICMP6 router advertisement");
             }
 
@@ -889,10 +905,11 @@
     private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException {
         // Here's a basic summary of what the IPv6 filter program does:
         //
-        // if it's not ICMPv6:
-        //   if it's multicast and we're dropping multicast:
-        //     drop
-        //   pass
+        // if we're dropping multicast
+        //   if it's not IPCMv6 or it's ICMPv6 but we're in doze mode:
+        //     if it's multicast:
+        //       drop
+        //     pass
         // if it's ICMPv6 RS to any:
         //   drop
         // if it's ICMPv6 NA to ff02::1:
@@ -902,28 +919,44 @@
 
         // Drop multicast if the multicast filter is enabled.
         if (mMulticastFilter) {
-            // Don't touch ICMPv6 multicast here, we deal with it in more detail later.
-            String skipIpv6MulticastFilterLabel = "skipIPv6MulticastFilter";
-            gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIpv6MulticastFilterLabel);
+            final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter";
+            final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast";
 
-            // Drop all other packets sent to ff00::/8.
+            // While in doze mode, drop ICMPv6 multicast pings, let the others pass.
+            // While awake, let all ICMPv6 multicasts through.
+            if (mInDozeMode) {
+                // Not ICMPv6? -> Proceed to multicast filtering
+                gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel);
+
+                // ICMPv6 but not ECHO? -> Skip the multicast filter.
+                // (ICMPv6 ECHO requests will go through the multicast filter below).
+                gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
+                gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel);
+            } else {
+                gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel);
+            }
+
+            // Drop all other packets sent to ff00::/8 (multicast prefix).
+            gen.defineLabel(dropAllIPv6MulticastsLabel);
             gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
             gen.addJumpIfR0Equals(0xff, gen.DROP_LABEL);
-            // Not multicast and not ICMPv6. Pass.
+            // Not multicast. Pass.
             gen.addJump(gen.PASS_LABEL);
-            gen.defineLabel(skipIpv6MulticastFilterLabel);
+            gen.defineLabel(skipIPv6MulticastFilterLabel);
         } else {
             // If not ICMPv6, pass.
             gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, gen.PASS_LABEL);
         }
 
+        // If we got this far, the packet is ICMPv6.  Drop some specific types.
+
         // Add unsolicited multicast neighbor announcements filter
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
         // Drop all router solicitations (b/32833400)
-        gen.addJumpIfR0Equals(ICMP6_ROUTER_SOLICITATION, gen.DROP_LABEL);
+        gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, gen.DROP_LABEL);
         // If not neighbor announcements, skip filter.
-        gen.addJumpIfR0NotEquals(ICMP6_NEIGHBOR_ANNOUNCEMENT, skipUnsolicitedMulticastNALabel);
+        gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
         // If to ff02::1, drop.
         // TODO: Drop only if they don't contain the address of on-link neighbours.
         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
@@ -1167,9 +1200,9 @@
      * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
      * filtering using APF programs.
      */
-    public static ApfFilter maybeCreate(ApfConfiguration config,
+    public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
             InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
-        if (config == null || ifParams == null) return null;
+        if (context == null || config == null || ifParams == null) return null;
         ApfCapabilities apfCapabilities =  config.apfCapabilities;
         if (apfCapabilities == null) return null;
         if (apfCapabilities.apfVersionSupported == 0) return null;
@@ -1186,7 +1219,8 @@
             Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
             return null;
         }
-        return new ApfFilter(config, ifParams, ipClientCallback, new IpConnectivityLog());
+
+        return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog());
     }
 
     public synchronized void shutdown() {
@@ -1196,12 +1230,11 @@
             mReceiveThread = null;
         }
         mRas.clear();
+        mContext.unregisterReceiver(mDeviceIdleReceiver);
     }
 
     public synchronized void setMulticastFilter(boolean isEnabled) {
-        if (mMulticastFilter == isEnabled) {
-            return;
-        }
+        if (mMulticastFilter == isEnabled) return;
         mMulticastFilter = isEnabled;
         if (!isEnabled) {
             mNumProgramUpdatesAllowingMulticast++;
@@ -1209,6 +1242,13 @@
         installNewProgramLocked();
     }
 
+    @VisibleForTesting
+    public synchronized void setDozeMode(boolean isEnabled) {
+        if (mInDozeMode == isEnabled) return;
+        mInDozeMode = isEnabled;
+        installNewProgramLocked();
+    }
+
     /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
     private static LinkAddress findIPv4LinkAddress(LinkProperties lp) {
         LinkAddress ipv4Address = null;
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index d41fbce..ca8f727 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -367,7 +367,7 @@
      */
     public boolean setApfVersion(int version) {
         // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
-        return version == 2;
+        return version >= 2;
     }
 
     private void addInstruction(Instruction instruction) {
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
new file mode 100644
index 0000000..97d20f4
--- /dev/null
+++ b/services/net/java/android/net/dns/ResolvUtil.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dns;
+
+import android.net.Network;
+import android.net.NetworkUtils;
+import android.system.GaiException;
+import android.system.OsConstants;
+import android.system.StructAddrinfo;
+
+import libcore.io.Libcore;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+
+/**
+ * DNS resolution utility class.
+ *
+ * @hide
+ */
+public class ResolvUtil {
+    // Non-portable DNS resolution flag.
+    private static final long NETID_USE_LOCAL_NAMESERVERS = 0x80000000L;
+
+    private ResolvUtil() {}
+
+    public static InetAddress[] blockingResolveAllLocally(Network network, String name)
+            throws UnknownHostException {
+        final StructAddrinfo hints = new StructAddrinfo();
+        // Unnecessary, but expressly no AI_ADDRCONFIG.
+        hints.ai_flags = 0;
+        // Fetch all IP addresses at once to minimize re-resolution.
+        hints.ai_family = OsConstants.AF_UNSPEC;
+        hints.ai_socktype = OsConstants.SOCK_DGRAM;
+
+        final Network networkForResolv = getNetworkWithUseLocalNameserversFlag(network);
+
+        try {
+            return Libcore.os.android_getaddrinfo(name, hints, (int) networkForResolv.netId);
+        } catch (GaiException gai) {
+            gai.rethrowAsUnknownHostException(name + ": TLS-bypass resolution failed");
+            return null;  // keep compiler quiet
+        }
+    }
+
+    public static Network getNetworkWithUseLocalNameserversFlag(Network network) {
+        final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
+        return new Network((int) netidForResolv);
+    }
+}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 9863370..a184b22 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -1490,7 +1490,7 @@
                     mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
             apfConfig.ethTypeBlackList =
                     mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
-            mApfFilter = ApfFilter.maybeCreate(apfConfig, mInterfaceParams, mCallback);
+            mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback);
             // TODO: investigate the effects of any multicast filtering racing/interfering with the
             // rest of this IP configuration startup.
             if (mApfFilter == null) {
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java
new file mode 100644
index 0000000..9f26fa1
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceSet.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+/**
+ * @hide
+ */
+public class InterfaceSet {
+    public final Set<String> ifnames;
+
+    public InterfaceSet(String... names) {
+        final Set<String> nameSet = new HashSet<>();
+        for (String name : names) {
+            if (name != null) nameSet.add(name);
+        }
+        ifnames = Collections.unmodifiableSet(nameSet);
+    }
+
+    @Override
+    public String toString() {
+        final StringJoiner sj = new StringJoiner(",", "[", "]");
+        for (String ifname : ifnames) sj.add(ifname);
+        return sj.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj != null
+                && obj instanceof InterfaceSet
+                && ifnames.equals(((InterfaceSet)obj).ifnames);
+    }
+}
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 984c9f8..53fd01f 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -136,6 +136,8 @@
      *     - https://tools.ietf.org/html/rfc4861
      */
     public static final int ICMPV6_HEADER_MIN_LEN = 4;
+    public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
+    public static final int ICMPV6_ECHO_REPLY_TYPE = 129;
     public static final int ICMPV6_ROUTER_SOLICITATION    = 133;
     public static final int ICMPV6_ROUTER_ADVERTISEMENT   = 134;
     public static final int ICMPV6_NEIGHBOR_SOLICITATION  = 135;
@@ -147,7 +149,6 @@
     public static final int ICMPV6_ND_OPTION_TLLA = 2;
     public static final int ICMPV6_ND_OPTION_MTU  = 5;
 
-    public static final int ICMPV6_ECHO_REQUEST_TYPE = 128;
 
     /**
      * UDP constants.
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index eca27ee..108d2ea 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -670,4 +670,8 @@
     public boolean canDismissBootAnimation() {
         return true;
     }
+
+    @Override
+    public void requestUserActivityNotification() {
+    }
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 73243d2..1c0e260 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -881,7 +881,7 @@
          */
         @IntDef(prefix = { "HANDOVER_" },
                 value = {HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_NOT_SUPPORTED,
-                HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERG_CALL,
+                HANDOVER_FAILURE_USER_REJECTED, HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL,
                 HANDOVER_FAILURE_UNKNOWN})
         @Retention(RetentionPolicy.SOURCE)
         public @interface HandoverFailureErrors {}
@@ -939,7 +939,7 @@
          * For more information on call handovers, see
          * {@link #handoverTo(PhoneAccountHandle, int, Bundle)}.
          */
-        public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 4;
+        public static final int HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL = 4;
 
         /**
          * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when a handover
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 9a53d8c..ca444d4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -2598,7 +2598,6 @@
     }
 
     /**
-     *
      * Request audio routing to a specific bluetooth device. Calling this method may result in
      * the device routing audio to a different bluetooth device than the one specified if the
      * bluetooth stack is unable to route audio to the requested device.
@@ -2609,13 +2608,13 @@
      * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
      * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
      * <p>
-     * See also {@link InCallService#requestBluetoothAudio(String)}
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * See also {@link InCallService#requestBluetoothAudio(BluetoothDevice)}
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         for (Listener l : mListeners) {
-            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
+            l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH,
+                    bluetoothDevice.getAddress());
         }
     }
 
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index af65c65..bd25ab2 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -428,12 +428,11 @@
      * A list of available devices can be obtained via
      * {@link CallAudioState#getSupportedBluetoothDevices()}
      *
-     * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
-     *                         {@link BluetoothDevice#getAddress()}.
+     * @param bluetoothDevice The bluetooth device to connect to.
      */
-    public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+    public final void requestBluetoothAudio(@NonNull BluetoothDevice bluetoothDevice) {
         if (mPhone != null) {
-            mPhone.requestBluetoothAudio(bluetoothAddress);
+            mPhone.requestBluetoothAudio(bluetoothDevice.getAddress());
         }
     }
 
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 4b1f0ad..05a3020 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1257,7 +1257,7 @@
      * @hide
      */
     @SystemApi
-    public int getCallState() {
+    public @TelephonyManager.CallState int getCallState() {
         try {
             if (isServiceConnected()) {
                 return getTelecomService().getCallState();
diff --git a/telephony/java/android/telephony/AccessNetworkUtils.java b/telephony/java/android/telephony/AccessNetworkUtils.java
new file mode 100644
index 0000000..5d2c225
--- /dev/null
+++ b/telephony/java/android/telephony/AccessNetworkUtils.java
@@ -0,0 +1,167 @@
+package android.telephony;
+
+import static android.telephony.ServiceState.DUPLEX_MODE_FDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_TDD;
+import static android.telephony.ServiceState.DUPLEX_MODE_UNKNOWN;
+
+import android.telephony.AccessNetworkConstants.EutranBand;
+import android.telephony.ServiceState.DuplexMode;
+
+
+/**
+ * Utilities to map between radio constants.
+ *
+ * @hide
+ */
+public class AccessNetworkUtils {
+
+    // do not instantiate
+    private AccessNetworkUtils() {}
+
+    public static final int INVALID_BAND = -1;
+
+    /**
+     * Gets the duplex mode for the given EUTRAN operating band.
+     *
+     * <p>See 3GPP 36.101 sec 5.5-1 for calculation
+     *
+     * @param band The EUTRAN band number
+     * @return The duplex mode of the given EUTRAN band
+     */
+    @DuplexMode
+    public static int getDuplexModeForEutranBand(int band) {
+        if (band == INVALID_BAND) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        if (band >= EutranBand.BAND_68) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_65) {
+            return DUPLEX_MODE_FDD;
+        } else if (band >= EutranBand.BAND_47) {
+            return DUPLEX_MODE_UNKNOWN;
+        } else if (band >= EutranBand.BAND_33) {
+            return DUPLEX_MODE_TDD;
+        } else if (band >= EutranBand.BAND_1) {
+            return DUPLEX_MODE_FDD;
+        }
+
+        return DUPLEX_MODE_UNKNOWN;
+    }
+
+    /**
+     * Gets the EUTRAN Operating band for a given downlink EARFCN.
+     *
+     * <p>See 3GPP 36.101 sec 5.7.3-1 for calculation.
+     *
+     * @param earfcn The downlink EARFCN
+     * @return Operating band number, or {@link #INVALID_BAND} if no corresponding band exists
+     */
+    public static int getOperatingBandForEarfcn(int earfcn) {
+        if (earfcn > 67535) {
+            return INVALID_BAND;
+        } else if (earfcn >= 67366) {
+            return INVALID_BAND; // band 67 only for CarrierAgg
+        } else if (earfcn >= 66436) {
+            return EutranBand.BAND_66;
+        } else if (earfcn >= 65536) {
+            return EutranBand.BAND_65;
+        } else if (earfcn > 54339) {
+            return INVALID_BAND;
+        } else if (earfcn >= 46790 /* inferred from the end range of BAND_45 */) {
+            return EutranBand.BAND_46;
+        } else if (earfcn >= 46590) {
+            return EutranBand.BAND_45;
+        } else if (earfcn >= 45590) {
+            return EutranBand.BAND_44;
+        } else if (earfcn >= 43590) {
+            return EutranBand.BAND_43;
+        } else if (earfcn >= 41590) {
+            return EutranBand.BAND_42;
+        } else if (earfcn >= 39650) {
+            return EutranBand.BAND_41;
+        } else if (earfcn >= 38650) {
+            return EutranBand.BAND_40;
+        } else if (earfcn >= 38250) {
+            return EutranBand.BAND_39;
+        } else if (earfcn >= 37750) {
+            return EutranBand.BAND_38;
+        } else if (earfcn >= 37550) {
+            return EutranBand.BAND_37;
+        } else if (earfcn >= 36950) {
+            return EutranBand.BAND_36;
+        } else if (earfcn >= 36350) {
+            return EutranBand.BAND_35;
+        } else if (earfcn >= 36200) {
+            return EutranBand.BAND_34;
+        } else if (earfcn >= 36000) {
+            return EutranBand.BAND_33;
+        } else if (earfcn > 10359) {
+            return INVALID_BAND;
+        } else if (earfcn >= 9920) {
+            return INVALID_BAND; // band 32 only for CarrierAgg
+        } else if (earfcn >= 9870) {
+            return EutranBand.BAND_31;
+        } else if (earfcn >= 9770) {
+            return EutranBand.BAND_30;
+        } else if (earfcn >= 9660) {
+            return INVALID_BAND; // band 29 only for CarrierAgg
+        } else if (earfcn >= 9210) {
+            return EutranBand.BAND_28;
+        } else if (earfcn >= 9040) {
+            return EutranBand.BAND_27;
+        } else if (earfcn >= 8690) {
+            return EutranBand.BAND_26;
+        } else if (earfcn >= 8040) {
+            return EutranBand.BAND_25;
+        } else if (earfcn >= 7700) {
+            return EutranBand.BAND_24;
+        } else if (earfcn >= 7500) {
+            return EutranBand.BAND_23;
+        } else if (earfcn >= 6600) {
+            return EutranBand.BAND_22;
+        } else if (earfcn >= 6450) {
+            return EutranBand.BAND_21;
+        } else if (earfcn >= 6150) {
+            return EutranBand.BAND_20;
+        } else if (earfcn >= 6000) {
+            return EutranBand.BAND_19;
+        } else if (earfcn >= 5850) {
+            return EutranBand.BAND_18;
+        } else if (earfcn >= 5730) {
+            return EutranBand.BAND_17;
+        } else if (earfcn > 5379) {
+            return INVALID_BAND;
+        } else if (earfcn >= 5280) {
+            return EutranBand.BAND_14;
+        } else if (earfcn >= 5180) {
+            return EutranBand.BAND_13;
+        } else if (earfcn >= 5010) {
+            return EutranBand.BAND_12;
+        } else if (earfcn >= 4750) {
+            return EutranBand.BAND_11;
+        } else if (earfcn >= 4150) {
+            return EutranBand.BAND_10;
+        } else if (earfcn >= 3800) {
+            return EutranBand.BAND_9;
+        } else if (earfcn >= 3450) {
+            return EutranBand.BAND_8;
+        } else if (earfcn >= 2750) {
+            return EutranBand.BAND_7;
+        } else if (earfcn >= 2650) {
+            return EutranBand.BAND_6;
+        } else if (earfcn >= 2400) {
+            return EutranBand.BAND_5;
+        } else if (earfcn >= 1950) {
+            return EutranBand.BAND_4;
+        } else if (earfcn >= 1200) {
+            return EutranBand.BAND_3;
+        } else if (earfcn >= 600) {
+            return EutranBand.BAND_2;
+        } else if (earfcn >= 0) {
+            return EutranBand.BAND_1;
+        }
+
+        return INVALID_BAND;
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fa07777..cc3cd4d 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1926,7 +1926,7 @@
         sDefaults.putBoolean(KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_VOLTE_OVERRIDE_WFC_PROVISIONING_BOOL, false);
-        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, false);
+        sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false);
         sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
@@ -2209,7 +2209,9 @@
     /**
      * Gets the configuration values for a particular subscription, which is associated with a
      * specific SIM card. If an invalid subId is used, the returned config will contain default
-     * values.
+     * values. After using this method to get the configuration bundle,
+     * {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be called to confirm whether
+     * any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2236,7 +2238,9 @@
     }
 
     /**
-     * Gets the configuration values for the default subscription.
+     * Gets the configuration values for the default subscription. After using this method to get
+     * the configuration bundle, {@link #isConfigForIdentifiedCarrier(PersistableBundle)} should be
+     * called to confirm whether any carrier specific configuration has been applied.
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -2265,6 +2269,9 @@
      * <p>
      * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always
      * use this method to confirm whether any carrier specific configuration has been applied.
+     * Especially when an app misses the broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED} but it
+     * still needs to get the current configuration, it must use this method to verify whether the
+     * configuration is default or carrier overridden.
      * </p>
      *
      * @param bundle the configuration bundle to be checked.
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index e092d52..890a6ea 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -18,11 +18,14 @@
 
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
 
 /**
  * CellIdentity represents the identity of a unique cell. This is the base class for
@@ -68,6 +71,9 @@
      */
     public static final int TYPE_TDSCDMA        = 5;
 
+    /** @hide */
+    public static final int INVALID_CHANNEL_NUMBER = -1;
+
     // Log tag
     /** @hide */
     protected final String mTag;
@@ -81,8 +87,16 @@
     /** @hide */
     protected final String mMncStr;
 
+    // long alpha Operator Name String or Enhanced Operator Name String
     /** @hide */
-    protected CellIdentity(String tag, int type, String mcc, String mnc) {
+    protected final String mAlphaLong;
+    // short alpha Operator Name String or Enhanced Operator Name String
+    /** @hide */
+    protected final String mAlphaShort;
+
+    /** @hide */
+    protected CellIdentity(String tag, int type, String mcc, String mnc, String alphal,
+                           String alphas) {
         mTag = tag;
         mType = type;
 
@@ -110,6 +124,8 @@
             mMncStr = null;
             log("invalid MNC format: " + mnc);
         }
+        mAlphaLong = alphal;
+        mAlphaShort = alphas;
     }
 
     /** Implement the Parcelable interface */
@@ -125,6 +141,50 @@
     public @Type int getType() { return mType; }
 
     /**
+     * Returns the channel number of the cell identity.
+     *
+     * @hide
+     * @return The channel number, or {@link #INVALID_CHANNEL_NUMBER} if not implemented
+     */
+    public int getChannelNumber() {
+        return INVALID_CHANNEL_NUMBER;
+    }
+
+    /**
+     * @return The long alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string). May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaLong() {
+        return mAlphaLong;
+    }
+
+    /**
+     * @return The short alpha tag associated with the current scan result (may be the operator
+     * name string or extended operator name string).  May be null if unknown.
+     */
+    @Nullable
+    public CharSequence getOperatorAlphaShort() {
+        return mAlphaShort;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof CellIdentity)) {
+            return false;
+        }
+
+        CellIdentity o = (CellIdentity) other;
+        return TextUtils.equals(mAlphaLong, o.mAlphaLong)
+                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mAlphaLong, mAlphaShort, mMccStr, mMncStr, mType);
+    }
+
+    /**
      * Used by child classes for parceling.
      *
      * @hide
@@ -134,6 +194,8 @@
         dest.writeInt(type);
         dest.writeString(mMccStr);
         dest.writeString(mMncStr);
+        dest.writeString(mAlphaLong);
+        dest.writeString(mAlphaShort);
     }
 
     /**
@@ -141,7 +203,8 @@
      * @hide
      */
     protected CellIdentity(String tag, int type, Parcel source) {
-        this(tag, type, source.readString(), source.readString());
+        this(tag, type, source.readString(), source.readString(),
+                source.readString(), source.readString());
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityCdma.java b/telephony/java/android/telephony/CellIdentityCdma.java
index 105ddb0..9a18c30 100644
--- a/telephony/java/android/telephony/CellIdentityCdma.java
+++ b/telephony/java/android/telephony/CellIdentityCdma.java
@@ -17,7 +17,6 @@
 package android.telephony;
 
 import android.os.Parcel;
-import android.text.TextUtils;
 
 import java.util.Objects;
 
@@ -48,23 +47,17 @@
      * to +90 degrees).
      */
     private final int mLatitude;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityCdma() {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, null, null);
         mNetworkId = Integer.MAX_VALUE;
         mSystemId = Integer.MAX_VALUE;
         mBasestationId = Integer.MAX_VALUE;
         mLongitude = Integer.MAX_VALUE;
         mLatitude = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -99,7 +92,7 @@
      */
     public CellIdentityCdma(int nid, int sid, int bid, int lon, int lat, String alphal,
                              String alphas) {
-        super(TAG, TYPE_CDMA, null, null);
+        super(TAG, TYPE_CDMA, null, null, alphal, alphas);
         mNetworkId = nid;
         mSystemId = sid;
         mBasestationId = bid;
@@ -109,8 +102,6 @@
         } else {
             mLongitude = mLatitude = Integer.MAX_VALUE;
         }
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityCdma(CellIdentityCdma cid) {
@@ -177,26 +168,10 @@
         return mLatitude;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
         return Objects.hash(mNetworkId, mSystemId, mBasestationId, mLatitude, mLongitude,
-                mAlphaLong, mAlphaShort);
+                super.hashCode());
     }
 
     @Override
@@ -216,8 +191,7 @@
                 && mBasestationId == o.mBasestationId
                 && mLatitude == o.mLatitude
                 && mLongitude == o.mLongitude
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -243,8 +217,6 @@
         dest.writeInt(mBasestationId);
         dest.writeInt(mLongitude);
         dest.writeInt(mLatitude);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -255,8 +227,6 @@
         mBasestationId = in.readInt();
         mLongitude = in.readInt();
         mLatitude = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index d35eb60..b167850 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -36,22 +36,16 @@
     private final int mArfcn;
     // 6-bit Base Station Identity Code
     private final int mBsic;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityGsm() {
-        super(TAG, TYPE_GSM, null, null);
+        super(TAG, TYPE_GSM, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mArfcn = Integer.MAX_VALUE;
         mBsic = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -97,16 +91,13 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, String mccStr,
                             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_GSM, mccStr, mncStr);
+        super(TAG, TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mArfcn = arfcn;
         // In RIL BSIC is a UINT8, so 0xFF is the 'INVALID' designator
         // for inbound parcels
         mBsic = (bsic == 0xFF) ? Integer.MAX_VALUE : bsic;
-
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityGsm(CellIdentityGsm cid) {
@@ -187,24 +178,13 @@
         return mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mArfcn;
     }
 
     /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
-
-    /**
      * @deprecated Primary Scrambling Code is not applicable to GSM.
      * @return Integer.MAX_VALUE, undefined for GSM
      */
@@ -215,7 +195,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, super.hashCode());
     }
 
     @Override
@@ -235,8 +215,7 @@
                 && mBsic == o.mBsic
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -262,8 +241,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -273,8 +250,6 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 2b8eb5f..d421cbd 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -36,10 +36,6 @@
     private final int mTac;
     // 18-bit Absolute RF Channel Number
     private final int mEarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
     // cell bandwidth, in kHz
     private final int mBandwidth;
 
@@ -47,14 +43,12 @@
      * @hide
      */
     public CellIdentityLte() {
-        super(TAG, TYPE_LTE, null, null);
+        super(TAG, TYPE_LTE, null, null, null, null);
         mCi = Integer.MAX_VALUE;
         mPci = Integer.MAX_VALUE;
         mTac = Integer.MAX_VALUE;
         mEarfcn = Integer.MAX_VALUE;
         mBandwidth = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
 
     /**
@@ -104,14 +98,12 @@
      */
     public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth, String mccStr,
             String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_LTE, mccStr, mncStr);
+        super(TAG, TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = ci;
         mPci = pci;
         mTac = tac;
         mEarfcn = earfcn;
         mBandwidth = bandwidth;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityLte(CellIdentityLte cid) {
@@ -197,25 +189,15 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mEarfcn;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mCi, mPci, mTac, mAlphaLong, mAlphaShort);
+        return Objects.hash(mCi, mPci, mTac, super.hashCode());
     }
 
     @Override
@@ -236,8 +218,7 @@
                 && mBandwidth == o.mBandwidth
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -265,8 +246,6 @@
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
         dest.writeInt(mBandwidth);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -277,8 +256,6 @@
         mTac = in.readInt();
         mEarfcn = in.readInt();
         mBandwidth = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 992545d..3070bd1 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -39,7 +39,7 @@
      * @hide
      */
     public CellIdentityTdscdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mCpid = Integer.MAX_VALUE;
@@ -55,7 +55,7 @@
      * @hide
      */
     public CellIdentityTdscdma(int mcc, int mnc, int lac, int cid, int cpid) {
-        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid);
+        this(String.valueOf(mcc), String.valueOf(mnc), lac, cid, cpid, null, null);
     }
 
     /**
@@ -65,17 +65,38 @@
      * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
      * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
      *
+     * FIXME: This is a temporary constructor to facilitate migration.
      * @hide
      */
     public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid) {
-        super(TAG, TYPE_TDSCDMA, mcc, mnc);
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, null, null);
+        mLac = lac;
+        mCid = cid;
+        mCpid = cpid;
+    }
+
+    /**
+     * @param mcc 3-digit Mobile Country Code in string format
+     * @param mnc 2 or 3-digit Mobile Network Code in string format
+     * @param lac 16-bit Location Area Code, 0..65535, INT_MAX if unknown
+     * @param cid 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455, INT_MAX if unknown
+     * @param cpid 8-bit Cell Parameters ID described in TS 25.331, 0..127, INT_MAX if unknown
+     * @param alphal long alpha Operator Name String or Enhanced Operator Name String
+     * @param alphas short alpha Operator Name String or Enhanced Operator Name String
+     *
+     * @hide
+     */
+    public CellIdentityTdscdma(String mcc, String mnc, int lac, int cid, int cpid,
+            String alphal, String alphas) {
+        super(TAG, TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mCpid = cpid;
     }
 
     private CellIdentityTdscdma(CellIdentityTdscdma cid) {
-        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid, cid.mCpid);
+        this(cid.mMccStr, cid.mMncStr, cid.mLac, cid.mCid,
+                cid.mCpid, cid.mAlphaLong, cid.mAlphaShort);
     }
 
     CellIdentityTdscdma copy() {
@@ -121,7 +142,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mCpid);
+        return Objects.hash(mLac, mCid, mCpid, super.hashCode());
     }
 
     @Override
@@ -139,7 +160,8 @@
                 && TextUtils.equals(mMncStr, o.mMncStr)
                 && mLac == o.mLac
                 && mCid == o.mCid
-                && mCpid == o.mCpid;
+                && mCpid == o.mCpid
+                && super.equals(other);
     }
 
     @Override
@@ -150,6 +172,8 @@
         .append(" mLac=").append(mLac)
         .append(" mCid=").append(mCid)
         .append(" mCpid=").append(mCpid)
+        .append(" mAlphaLong=").append(mAlphaLong)
+        .append(" mAlphaShort=").append(mAlphaShort)
         .append("}").toString();
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index a5fd7dd..f1167a6 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -36,22 +36,16 @@
     private final int mPsc;
     // 16-bit UMTS Absolute RF Channel Number
     private final int mUarfcn;
-    // long alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaLong;
-    // short alpha Operator Name String or Enhanced Operator Name String
-    private final String mAlphaShort;
 
     /**
      * @hide
      */
     public CellIdentityWcdma() {
-        super(TAG, TYPE_TDSCDMA, null, null);
+        super(TAG, TYPE_TDSCDMA, null, null, null, null);
         mLac = Integer.MAX_VALUE;
         mCid = Integer.MAX_VALUE;
         mPsc = Integer.MAX_VALUE;
         mUarfcn = Integer.MAX_VALUE;
-        mAlphaLong = null;
-        mAlphaShort = null;
     }
     /**
      * public constructor
@@ -98,13 +92,11 @@
      */
     public CellIdentityWcdma (int lac, int cid, int psc, int uarfcn,
                               String mccStr, String mncStr, String alphal, String alphas) {
-        super(TAG, TYPE_WCDMA, mccStr, mncStr);
+        super(TAG, TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = lac;
         mCid = cid;
         mPsc = psc;
         mUarfcn = uarfcn;
-        mAlphaLong = alphal;
-        mAlphaShort = alphas;
     }
 
     private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -178,25 +170,9 @@
         return (mMccStr == null || mMncStr == null) ? null : mMccStr + mMncStr;
     }
 
-    /**
-     * @return The long alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string). May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaLong() {
-        return mAlphaLong;
-    }
-
-    /**
-     * @return The short alpha tag associated with the current scan result (may be the operator
-     * name string or extended operator name string).  May be null if unknown.
-     */
-    public CharSequence getOperatorAlphaShort() {
-        return mAlphaShort;
-    }
-
     @Override
     public int hashCode() {
-        return Objects.hash(mMccStr, mMncStr, mLac, mCid, mPsc, mAlphaLong, mAlphaShort);
+        return Objects.hash(mLac, mCid, mPsc, super.hashCode());
     }
 
     /**
@@ -206,6 +182,12 @@
         return mUarfcn;
     }
 
+    /** @hide */
+    @Override
+    public int getChannelNumber() {
+        return mUarfcn;
+    }
+
     @Override
     public boolean equals(Object other) {
         if (this == other) {
@@ -223,8 +205,7 @@
                 && mUarfcn == o.mUarfcn
                 && TextUtils.equals(mMccStr, o.mMccStr)
                 && TextUtils.equals(mMncStr, o.mMncStr)
-                && TextUtils.equals(mAlphaLong, o.mAlphaLong)
-                && TextUtils.equals(mAlphaShort, o.mAlphaShort);
+                && super.equals(other);
     }
 
     @Override
@@ -250,8 +231,6 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeString(mAlphaLong);
-        dest.writeString(mAlphaShort);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -261,8 +240,6 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAlphaLong = in.readString();
-        mAlphaShort = in.readString();
         if (DBG) log(toString());
     }
 
@@ -286,4 +263,4 @@
     protected static CellIdentityWcdma createFromParcelBody(Parcel in) {
         return new CellIdentityWcdma(in);
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index ce1b80c..d9fdd97 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,13 +33,15 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
-import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStatusListener;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.InternalDownloadProgressListener;
 import android.telephony.mbms.InternalDownloadSessionCallback;
-import android.telephony.mbms.InternalDownloadStateCallback;
-import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
 import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsErrors;
 import android.telephony.mbms.MbmsTempFileProvider;
 import android.telephony.mbms.MbmsUtils;
@@ -56,8 +60,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for file download over MBMS.
  */
@@ -131,6 +133,14 @@
      */
     public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
 
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {RESULT_SUCCESSFUL, RESULT_CANCELLED, RESULT_EXPIRED, RESULT_IO_ERROR,
+            RESULT_SERVICE_ID_NOT_DEFINED, RESULT_DOWNLOAD_FAILURE, RESULT_OUT_OF_STORAGE,
+            RESULT_FILE_ROOT_UNREACHABLE}, prefix = { "RESULT_" })
+    public @interface DownloadResultCode{}
+
     /**
      * Indicates that the download was successful.
      */
@@ -232,8 +242,10 @@
 
     private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
     private final InternalDownloadSessionCallback mInternalCallback;
-    private final Map<DownloadStateCallback, InternalDownloadStateCallback>
-            mInternalDownloadCallbacks = new HashMap<>();
+    private final Map<DownloadStatusListener, InternalDownloadStatusListener>
+            mInternalDownloadStatusListeners = new HashMap<>();
+    private final Map<DownloadProgressListener, InternalDownloadProgressListener>
+            mInternalDownloadProgressListeners = new HashMap<>();
 
     private MbmsDownloadSession(Context context, Executor executor, int subscriptionId,
             MbmsDownloadSessionCallback callback) {
@@ -325,6 +337,12 @@
                             sIsInitialized.set(false);
                             return;
                         }
+                        if (result == MbmsErrors.UNKNOWN) {
+                            // Unbind and throw an obvious error
+                            close();
+                            throw new IllegalStateException("Middleware must not return an"
+                                    + " unknown error code");
+                        }
                         if (result != MbmsErrors.SUCCESS) {
                             sendErrorToApp(result, "Error returned during initialization");
                             sIsInitialized.set(false);
@@ -376,6 +394,11 @@
         }
         try {
             int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
             }
@@ -431,6 +454,11 @@
 
         try {
             int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 sendErrorToApp(result, null);
                 return;
@@ -502,11 +530,13 @@
      *
      * Asynchronous errors through the callback may include any error not specific to the
      * streaming use-case.
+     *
+     * If no error is delivered via the callback after calling this method, that means that the
+     * middleware has successfully started the download or scheduled the download, if the download
+     * is at a future time.
      * @param request The request that specifies what should be downloaded.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int download(@NonNull DownloadRequest request) {
+    public void download(@NonNull DownloadRequest request) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -528,12 +558,19 @@
             int result = downloadService.download(request);
             if (result == MbmsErrors.SUCCESS) {
                 writeDownloadRequestToken(request);
+            } else {
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an unknown"
+                            + " error code");
+                }
+                sendErrorToApp(result, null);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
@@ -561,111 +598,232 @@
     }
 
     /**
-     * Registers a callback for a {@link DownloadRequest} previously requested via
+     * Registers a listener download status for a {@link DownloadRequest} previously requested via
      * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
      * app and the middleware are both running -- if either one stops, no further calls on the
-     * provided {@link DownloadStateCallback} will be enqueued.
+     * provided {@link DownloadStatusListener} will be enqueued.
      *
      * If the middleware is not aware of the specified download request,
      * this method will throw an {@link IllegalArgumentException}.
      *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
      * @param request The {@link DownloadRequest} that you want updates on.
-     * @param executor The {@link Executor} on which calls to {@code callback} should be executed.
-     * @param callback The callback that should be called when the middleware has information to
-     *                 share on the download.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @param executor The {@link Executor} on which calls to {@code listener } should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the status download.
      */
-    public int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
+    public void addStatusListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadStatusListener listener) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
         }
 
-        InternalDownloadStateCallback internalCallback =
-                new InternalDownloadStateCallback(callback, executor);
+        InternalDownloadStatusListener internalListener =
+                new InternalDownloadStatusListener(listener, executor);
 
         try {
-            int result = downloadService.registerStateCallback(request, internalCallback,
-                    callback.getCallbackFilterFlags());
+            int result = downloadService.addStatusListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
                 }
-                return result;
+                sendErrorToApp(result, null);
+                return;
             }
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
         }
-        mInternalDownloadCallbacks.put(callback, internalCallback);
-        return MbmsErrors.SUCCESS;
+        mInternalDownloadStatusListeners.put(listener, internalListener);
     }
 
     /**
-     * Un-register a callback previously registered via
-     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}. After
-     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * Un-register a listener previously registered via
+     * {@link #addStatusListener(DownloadRequest, Executor, DownloadStatusListener)}. After
+     * this method is called, no further calls will be enqueued on the {@link Executor}
      * provided upon registration, even if this method throws an exception.
      *
      * If the middleware is not aware of the specified download request,
      * this method will throw an {@link IllegalArgumentException}.
      *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
      * @param request The {@link DownloadRequest} provided during registration
-     * @param callback The callback provided during registration.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
+     * @param listener The listener provided during registration.
      */
-    public int unregisterStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback) {
+    public void removeStatusListener(@NonNull DownloadRequest request,
+            @NonNull DownloadStatusListener listener) {
         try {
             IMbmsDownloadService downloadService = mService.get();
             if (downloadService == null) {
                 throw new IllegalStateException("Middleware not yet bound");
             }
 
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.get(callback);
-            if (internalCallback == null) {
-                throw new IllegalArgumentException("Provided callback was never registered");
+            InternalDownloadStatusListener internalListener =
+                    mInternalDownloadStatusListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
             }
 
             try {
-                int result = downloadService.unregisterStateCallback(request, internalCallback);
+                int result = downloadService.removeStatusListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not return an"
+                            + " unknown error code");
+                }
                 if (result != MbmsErrors.SUCCESS) {
                     if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                         throw new IllegalArgumentException("Unknown download request.");
                     }
-                    return result;
+                    sendErrorToApp(result, null);
+                    return;
                 }
             } catch (RemoteException e) {
                 mService.set(null);
                 sIsInitialized.set(false);
-                return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
             }
         } finally {
-            InternalDownloadStateCallback internalCallback =
-                    mInternalDownloadCallbacks.remove(callback);
+            InternalDownloadStatusListener internalCallback =
+                    mInternalDownloadStatusListeners.remove(listener);
             if (internalCallback != null) {
                 internalCallback.stop();
             }
         }
-        return MbmsErrors.SUCCESS;
+    }
+
+    /**
+     * Registers a listener for progress for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This listener will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadProgressListener} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param executor The {@link Executor} on which calls to {@code listener} should be executed.
+     * @param listener The listener that should be called when the middleware has information to
+     *                 share on the progress of the download.
+     */
+    public void addProgressListener(@NonNull DownloadRequest request,
+            @NonNull Executor executor, @NonNull DownloadProgressListener listener) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadProgressListener internalListener =
+                new InternalDownloadProgressListener(listener, executor);
+
+        try {
+            int result = downloadService.addProgressListener(request, internalListener);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadProgressListeners.put(listener, internalListener);
+    }
+
+    /**
+     * Un-register a listener previously registered via
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}. After
+     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param listener The listener provided during registration.
+     */
+    public void removeProgressListener(@NonNull DownloadRequest request,
+            @NonNull DownloadProgressListener listener) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadProgressListener internalListener =
+                    mInternalDownloadProgressListeners.get(listener);
+            if (internalListener == null) {
+                throw new IllegalArgumentException("Provided listener was never registered");
+            }
+
+            try {
+                int result = downloadService.removeProgressListener(request, internalListener);
+                if (result == MbmsErrors.UNKNOWN) {
+                    // Unbind and throw an obvious error
+                    close();
+                    throw new IllegalStateException("Middleware must not"
+                            + " return an unknown error code");
+                }
+                if (result != MbmsErrors.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                    return;
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sIsInitialized.set(false);
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+                return;
+            }
+        } finally {
+            InternalDownloadProgressListener internalCallback =
+                    mInternalDownloadProgressListeners.remove(listener);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
+        }
     }
 
     /**
      * Attempts to cancel the specified {@link DownloadRequest}.
      *
-     * If the middleware is not aware of the specified download request,
-     * this method will throw an {@link IllegalArgumentException}.
+     * If the operation encountered an error, the error code will be delivered via
+     * {@link MbmsDownloadSessionCallback#onError}.
      *
      * @param downloadRequest The download request that you wish to cancel.
-     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
-     * and some other error code otherwise.
      */
-    public int cancelDownload(@NonNull DownloadRequest downloadRequest) {
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
         IMbmsDownloadService downloadService = mService.get();
         if (downloadService == null) {
             throw new IllegalStateException("Middleware not yet bound");
@@ -673,18 +831,20 @@
 
         try {
             int result = downloadService.cancelDownload(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
-                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
-                    throw new IllegalArgumentException("Unknown download request.");
-                }
+                sendErrorToApp(result, null);
             } else {
                 deleteDownloadRequestToken(downloadRequest);
             }
-            return result;
         } catch (RemoteException e) {
             mService.set(null);
             sIsInitialized.set(false);
-            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
         }
     }
 
@@ -692,9 +852,9 @@
      * Requests information about the state of a file pending download.
      *
      * The state will be delivered as a callback via
-     * {@link DownloadStateCallback#onStateUpdated(DownloadRequest, FileInfo, int)}. If no such
+     * {@link DownloadStatusListener#onStatusUpdated(DownloadRequest, FileInfo, int)}. If no such
      * callback has been registered via
-     * {@link #registerStateCallback(DownloadRequest, Executor, DownloadStateCallback)}, this
+     * {@link #addProgressListener(DownloadRequest, Executor, DownloadProgressListener)}, this
      * method will be a no-op.
      *
      * If the middleware has no record of the
@@ -712,6 +872,11 @@
 
         try {
             int result = downloadService.requestDownloadState(downloadRequest, fileInfo);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
@@ -756,6 +921,11 @@
 
         try {
             int result = downloadService.resetDownloadKnowledge(downloadRequest);
+            if (result == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (result != MbmsErrors.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
@@ -814,11 +984,11 @@
         try {
             if (!token.createNewFile()) {
                 throw new RuntimeException("Failed to create download token for request "
-                        + request);
+                        + request + ". Token location is " + token.getPath());
             }
         } catch (IOException e) {
             throw new RuntimeException("Failed to create download token for request " + request
-                    + " due to IOException " + e);
+                    + " due to IOException " + e + ". Attempted to write to " + token.getPath());
         }
     }
 
@@ -872,10 +1042,6 @@
     }
 
     private void sendErrorToApp(int errorCode, String message) {
-        try {
-            mInternalCallback.onError(errorCode, message);
-        } catch (RemoteException e) {
-            // Ignore, should not happen locally.
-        }
+        mInternalCallback.onError(errorCode, message);
     }
 }
diff --git a/telephony/java/android/telephony/MbmsStreamingSession.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index 42c760d4..cd465d2 100644
--- a/telephony/java/android/telephony/MbmsStreamingSession.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -26,8 +28,8 @@
 import android.content.ServiceConnection;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.InternalStreamingSessionCallback;
 import android.telephony.mbms.MbmsErrors;
 import android.telephony.mbms.MbmsStreamingSessionCallback;
 import android.telephony.mbms.MbmsUtils;
@@ -44,8 +46,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
 /**
  * This class provides functionality for streaming media over MBMS.
  */
@@ -208,6 +208,11 @@
         try {
             int returnCode = streamingService.requestUpdateStreamingServices(
                     mSubscriptionId, serviceClassList);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
             }
@@ -255,6 +260,11 @@
         try {
             int returnCode = streamingService.startStreaming(
                     mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+            if (returnCode == MbmsErrors.UNKNOWN) {
+                // Unbind and throw an obvious error
+                close();
+                throw new IllegalStateException("Middleware must not return an unknown error code");
+            }
             if (returnCode != MbmsErrors.SUCCESS) {
                 sendErrorToApp(returnCode, null);
                 return null;
@@ -301,6 +311,12 @@
                             sIsInitialized.set(false);
                             return;
                         }
+                        if (result == MbmsErrors.UNKNOWN) {
+                            // Unbind and throw an obvious error
+                            close();
+                            throw new IllegalStateException("Middleware must not return"
+                                    + " an unknown error code");
+                        }
                         if (result != MbmsErrors.SUCCESS) {
                             sendErrorToApp(result, "Error returned during initialization");
                             sIsInitialized.set(false);
diff --git a/telephony/java/android/telephony/NetworkScan.java b/telephony/java/android/telephony/NetworkScan.java
index 7f43ee5..073c313 100644
--- a/telephony/java/android/telephony/NetworkScan.java
+++ b/telephony/java/android/telephony/NetworkScan.java
@@ -115,6 +115,8 @@
             telephony.stopNetworkScan(mSubId, mScanId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "stopNetworkScan  RemoteException", ex);
+        } catch (RuntimeException ex) {
+            Rlog.e(TAG, "stopNetworkScan  RuntimeException", ex);
         }
     }
 
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 92ebf36..dbad02f 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -83,6 +83,8 @@
             } catch (RemoteException e) {
                 Rlog.e(mTag, "Failed to onGetNetworkRegistrationStateComplete on the remote");
             }
+        } else {
+            Rlog.e(mTag, "Weak reference of callback is null.");
         }
     }
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0446925..7db83f6 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -20,6 +20,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.telecom.TelecomManager;
 
 import com.android.internal.telephony.IPhoneStateListener;
 
@@ -61,9 +62,6 @@
     /**
      * Listen for changes to the network signal strength (cellular).
      * {@more}
-     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
-     * <p>
      *
      * @see #onSignalStrengthChanged
      *
@@ -76,7 +74,8 @@
      * Listen for changes to the message-waiting indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
      * <p>
      * Example: The status bar uses this to determine when to display the
      * voicemail icon.
@@ -89,7 +88,9 @@
      * Listen for changes to the call-forwarding indicator.
      * {@more}
      * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE
-     * READ_PHONE_STATE}
+     * READ_PHONE_STATE} or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @see #onCallForwardingIndicatorChanged
      */
     public static final int LISTEN_CALL_FORWARDING_INDICATOR                = 0x00000008;
@@ -428,16 +429,21 @@
 
     /**
      * Callback invoked when device call state changes.
+     * <p>
+     * Reports the state of Telephony (mobile) calls on the device.
+     * <p>
+     * Note: The state returned here may differ from that returned by
+     * {@link TelephonyManager#getCallState()}. Receivers of this callback should be aware that
+     * calling {@link TelephonyManager#getCallState()} from within this callback may return a
+     * different state than the callback reports.
+     *
      * @param state call state
      * @param phoneNumber call phone number. If application does not have
-     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission, an empty
-     * string will be passed as an argument.
-     *
-     * @see TelephonyManager#CALL_STATE_IDLE
-     * @see TelephonyManager#CALL_STATE_RINGING
-     * @see TelephonyManager#CALL_STATE_OFFHOOK
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission or carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), an empty string will be
+     * passed as an argument.
      */
-    public void onCallStateChanged(int state, String phoneNumber) {
+    public void onCallStateChanged(@TelephonyManager.CallState int state, String phoneNumber) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index b6be3c5..e971d08 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -17,8 +17,8 @@
 package android.telephony;
 
 import android.annotation.IntDef;
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -220,7 +220,7 @@
     public static final int ROAMING_TYPE_INTERNATIONAL = 3;
 
     /**
-     * Unknown ID. Could be returned by {@link #getNetworkId()} or {@link #getSystemId()}
+     * Unknown ID. Could be returned by {@link #getCdmaNetworkId()} or {@link #getCdmaSystemId()}
      */
     public static final int UNKNOWN_ID = -1;
 
@@ -470,9 +470,13 @@
      */
     @DuplexMode
     public int getDuplexMode() {
-        // TODO(b/72117602) determine duplex mode from channel number, using 3GPP 36.101 sections
-        // 5.7.3-1 and 5.5-1
-        return DUPLEX_MODE_UNKNOWN;
+        // only support LTE duplex mode
+        if (!isLte(mRilDataRadioTechnology)) {
+            return DUPLEX_MODE_UNKNOWN;
+        }
+
+        int band = AccessNetworkUtils.getOperatingBandForEarfcn(mChannelNumber);
+        return AccessNetworkUtils.getDuplexModeForEutranBand(band);
     }
 
     /**
@@ -491,9 +495,8 @@
      *
      * @return Current serving cell bandwidths
      */
-    @Nullable
     public int[] getCellBandwidths() {
-        return mCellBandwidths;
+        return mCellBandwidths == null ? new int[0] : mCellBandwidths;
     }
 
     /**
@@ -890,6 +893,7 @@
             .append(", mDataRegState=").append(mDataRegState)
             .append("(" + rilServiceStateToString(mDataRegState) + ")")
             .append(", mChannelNumber=").append(mChannelNumber)
+            .append(", duplexMode()=").append(getDuplexMode())
             .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
             .append(", mVoiceRoamingType=").append(getRoamingLogString(mVoiceRoamingType))
             .append(", mDataRoamingType=").append(getRoamingLogString(mDataRoamingType))
@@ -1215,7 +1219,8 @@
     }
 
     /** @hide */
-    public void setSystemAndNetworkId(int systemId, int networkId) {
+    @TestApi
+    public void setCdmaSystemAndNetworkId(int systemId, int networkId) {
         this.mSystemId = systemId;
         this.mNetworkId = networkId;
     }
@@ -1381,7 +1386,7 @@
      * within a wireless system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA NID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getNetworkId() {
+    public int getCdmaNetworkId() {
         return this.mNetworkId;
     }
 
@@ -1390,7 +1395,7 @@
      * system. (Defined in 3GPP2 C.S0023 3.4.8)
      * @return The CDMA SID or {@link #UNKNOWN_ID} if not available.
      */
-    public int getSystemId() {
+    public int getCdmaSystemId() {
         return this.mSystemId;
     }
 
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 2074084..3feec992 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -906,11 +906,8 @@
 
         if (rsrpIconLevel != -1) return rsrpIconLevel;
 
-        /* Valid values are (0-63, 99) as defined in TS 36.331 */
-        // TODO the range here is probably supposed to be (0..31, 99). It's unclear if anyone relies
-        // on the current incorrect range check, so this will be fixed in a future release with more
-        // soak time
-        if (mLteSignalStrength > 63) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        /* Valid values are (0-31, 99) as defined in TS 27.007 8.5 */
+        if (mLteSignalStrength > 31) rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
         else if (mLteSignalStrength >= 12) rssiIconLevel = SIGNAL_STRENGTH_GREAT;
         else if (mLteSignalStrength >= 8) rssiIconLevel = SIGNAL_STRENGTH_GOOD;
         else if (mLteSignalStrength >= 5) rssiIconLevel = SIGNAL_STRENGTH_MODERATE;
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b44f830..7506f4a 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
@@ -338,16 +339,17 @@
     /**
      * Send a text based SMS without writing it into the SMS Provider.
      *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#SEND_SMS} and
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier
-     * privileges.
-     * </p>
+     * <p>Requires Permission: Both {@link android.Manifest.permission#SEND_SMS} and
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}, or that the calling app has carrier
+     * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is
+     * the default IMS app (see
+     * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}).
      *
      * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
      * @hide
      */
     @SystemApi
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(allOf = {
             android.Manifest.permission.MODIFY_PHONE_STATE,
             android.Manifest.permission.SEND_SMS
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index ea74bac..f336af1 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -198,7 +198,10 @@
      */
     public static SmsMessage createFromPdu(byte[] pdu, String format) {
         SmsMessageBase wrappedMessage;
-
+        if (pdu == null) {
+            Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
+            return null;
+        }
         if (SmsConstants.FORMAT_3GPP2.equals(format)) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
         } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 77413d9c..936505c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -335,7 +335,7 @@
         return this.mCountryIso;
     }
 
-    /** @return whether the subscription is an embedded one. */
+    /** @return whether the subscription is an eUICC one. */
     public boolean isEmbedded() {
         return this.mIsEmbedded;
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index d09d426..1d7b724 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -17,9 +17,11 @@
 package android.telephony;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.content.Intent;
@@ -47,9 +49,6 @@
 /**
  * SubscriptionManager is the application interface to SubscriptionController
  * and provides information about the current Telephony Subscriptions.
- * <p>
- * All SDK public methods require android.Manifest.permission.READ_PHONE_STATE unless otherwise
- * specified.
  */
 @SystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
 public class SubscriptionManager {
@@ -590,9 +589,15 @@
     /**
      * Get the active SubscriptionInfo with the input subId.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param subId The unique SubscriptionInfo key in database.
      * @return SubscriptionInfo, maybe null if its not active.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
         if (VDBG) logd("[getActiveSubscriptionInfo]+ subId=" + subId);
         if (!isValidSubscriptionId(subId)) {
@@ -646,9 +651,16 @@
 
     /**
      * Get the active SubscriptionInfo associated with the slotIndex
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}).
+     *
      * @param slotIndex the slot which the subscription is inserted
      * @return SubscriptionInfo, maybe null if its not active
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex) {
         if (VDBG) logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex);
         if (!isValidSlotIndex(slotIndex)) {
@@ -700,6 +712,11 @@
      * Get the SubscriptionInfo(s) of the currently inserted SIM(s). The records will be sorted
      * by {@link SubscriptionInfo#getSimSlotIndex} then by {@link SubscriptionInfo#getSubscriptionId}.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
+     * to the calling app are returned.
+     *
      * @return Sorted list of the currently {@link SubscriptionInfo} records available on the device.
      * <ul>
      * <li>
@@ -716,6 +733,8 @@
      * </li>
      * </ul>
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
         List<SubscriptionInfo> result = null;
 
@@ -858,10 +877,18 @@
     }
 
     /**
+     *
+     * Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see
+     * {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, the count will include
+     * only those subscriptions accessible to the caller.
+     *
      * @return the current number of active subscriptions. There is no guarantee the value
      * returned by this method will be the same as the length of the list returned by
      * {@link #getActiveSubscriptionInfoList}.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getActiveSubscriptionInfoCount() {
         int result = 0;
 
@@ -1677,26 +1704,28 @@
      *
      * @param info The subscription to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
+     * @throws IllegalArgumentException if this subscription is not embedded.
      */
     public boolean canManageSubscription(SubscriptionInfo info) {
         return canManageSubscription(info, mContext.getPackageName());
     }
 
     /**
-     * Checks whether the given app is authorized to manage the given subscription according to its
-     * metadata. Only supported for embedded subscriptions (if {@code SubscriptionInfo#isEmbedded}
+     * Checks whether the given app is authorized to manage the given subscription. An app can only
+     * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the
+     * {@link android.telephony.SubscriptionInfo} with the access status.
+     * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded}
      * returns true).
      *
      * @param info The subscription to check.
      * @param packageName Package name of the app to check.
-     * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
+     * @return whether the app is authorized to manage this subscription per its access rules.
+     * @throws IllegalArgumentException if this subscription is not embedded.
      * @hide
      */
     public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
         if (!info.isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
+            throw new IllegalArgumentException("Not an embedded subscription");
         }
         if (info.getAccessRules() == null) {
             return false;
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index fb52ff7..f1653ce 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -3165,8 +3165,8 @@
             values.put(RIL_VOICE_RADIO_TECHNOLOGY, state.getRilVoiceRadioTechnology());
             values.put(RIL_DATA_RADIO_TECHNOLOGY, state.getRilDataRadioTechnology());
             values.put(CSS_INDICATOR, state.getCssIndicator());
-            values.put(NETWORK_ID, state.getNetworkId());
-            values.put(SYSTEM_ID, state.getSystemId());
+            values.put(NETWORK_ID, state.getCdmaNetworkId());
+            values.put(SYSTEM_ID, state.getCdmaSystemId());
             values.put(CDMA_ROAMING_INDICATOR, state.getCdmaRoamingIndicator());
             values.put(CDMA_DEFAULT_ROAMING_INDICATOR, state.getCdmaDefaultRoamingIndicator());
             values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex());
@@ -3296,13 +3296,13 @@
         public static final String CSS_INDICATOR = "css_indicator";
 
         /**
-         * This is the same as {@link ServiceState#getNetworkId()}.
+         * This is the same as {@link ServiceState#getCdmaNetworkId()}.
          * @hide
          */
         public static final String NETWORK_ID = "network_id";
 
         /**
-         * This is the same as {@link ServiceState#getSystemId()}.
+         * This is the same as {@link ServiceState#getCdmaSystemId()}.
          * @hide
          */
         public static final String SYSTEM_ID = "system_id";
@@ -3380,7 +3380,7 @@
          * on the given subscriptionId
          * <p>
          * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
-         * carrier identity {@link TelephonyManager#getAndroidCarrierIdForSubscription()}
+         * carrier identity {@link TelephonyManager#getSimCarrierId()}
          * while your app is running. You can also use a {@link JobService} to ensure your app
          * is notified of changes to the {@link Uri} even when it is not running.
          * Note, however, that using a {@link JobService} does not guarantee timely delivery of
@@ -3396,14 +3396,14 @@
 
         /**
          * A user facing carrier name.
-         * @see TelephonyManager#getAndroidCarrierNameForSubscription()
+         * @see TelephonyManager#getSimCarrierIdName()
          * <P>Type: TEXT </P>
          */
         public static final String CARRIER_NAME = "carrier_name";
 
         /**
          * A unique carrier id
-         * @see TelephonyManager#getAndroidCarrierIdForSubscription()
+         * @see TelephonyManager#getSimCarrierId()
          * <P>Type: INTEGER </P>
          */
         public static final String CARRIER_ID = "carrier_id";
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 0e84e7c..c2cbd06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressAutoDoc;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -39,6 +40,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -47,12 +49,12 @@
 import android.service.carrier.CarrierIdentifier;
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telephony.VisualVoicemailService.VisualVoicemailTask;
 import android.telephony.ims.aidl.IImsConfig;
 import android.telephony.ims.aidl.IImsMmTelFeature;
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
-import android.telephony.ims.feature.ImsFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
@@ -110,6 +112,13 @@
             BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY;
 
     /**
+     * The process name of the Phone app as well as many other apps that use this process name, such
+     * as settings and vendor components.
+     * @hide
+     */
+    public static final String PHONE_PROCESS_NAME = "com.android.phone";
+
+    /**
      * The allowed states of Wi-Fi calling.
      *
      * @hide
@@ -206,6 +215,10 @@
         return ActivityThread.currentOpPackageName();
     }
 
+    private boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
+
     /**
      * Returns the multi SIM variant
      * Returns DSDS for Dual SIM Dual Standby
@@ -1049,7 +1062,7 @@
 
     /**
      * An int extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which indicates
-     * the updated carrier id {@link TelephonyManager#getAndroidCarrierIdForSubscription()} of
+     * the updated carrier id {@link TelephonyManager#getSimCarrierId()} of
      * the current subscription.
      * <p>Will be {@link TelephonyManager#UNKNOWN_CARRIER_ID} if the subscription is unavailable or
      * the carrier cannot be identified.
@@ -1059,7 +1072,7 @@
     /**
      * An string extra used with {@link #ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED} which
      * indicates the updated carrier name of the current subscription.
-     * {@see TelephonyManager#getSubscriptionCarrierName()}
+     * {@see TelephonyManager#getSimCarrierIdName()}
      * <p>Carrier name is a user-facing name of the carrier id {@link #EXTRA_CARRIER_ID},
      * usually the brand name of the subsidiary (e.g. T-Mobile).
      */
@@ -1082,7 +1095,11 @@
      * Returns the software version number for the device, for example,
      * the IMEI/SV for GSM phones. Return null if the software version is
      * not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceSoftwareVersion() {
         return getDeviceSoftwareVersion(getSlotIndex());
@@ -1114,10 +1131,14 @@
      * Returns the unique device ID, for example, the IMEI for GSM and the MEID
      * or ESN for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId() {
         try {
@@ -1136,12 +1157,16 @@
      * Returns the unique device ID of a subscription, for example, the IMEI for
      * GSM and the MEID for CDMA phones. Return null if device ID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which deviceID is returned
      *
      * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns
      * MEID for CDMA.
      */
     @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getDeviceId(int slotIndex) {
         // FIXME this assumes phoneId == slotIndex
@@ -1160,7 +1185,11 @@
     /**
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei() {
         return getImei(getSlotIndex());
@@ -1170,8 +1199,12 @@
      * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
      * available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which IMEI is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getImei(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1188,7 +1221,11 @@
 
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid() {
         return getMeid(getSlotIndex());
@@ -1197,8 +1234,12 @@
     /**
      * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @param slotIndex of which MEID is returned
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getMeid(int slotIndex) {
         ITelephony telephony = getITelephony();
@@ -1215,10 +1256,11 @@
 
     /**
      * Returns the Network Access Identifier (NAI). Return null if NAI is not available.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getNai() {
         return getNaiBySubscriberId(getSubId());
@@ -1715,10 +1757,17 @@
      * invalid subscription ID is pinned to the TelephonyManager, the returned config will contain
      * default values.
      *
+     * <p>This method may take several seconds to complete, so it should only be called from a
+     * worker thread.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see CarrierConfigManager#getConfigForSubId(int)
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @WorkerThread
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public PersistableBundle getCarrierConfig() {
@@ -1918,6 +1967,9 @@
      * If this object has been created with {@link #createForSubscriptionId}, applies to the given
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return the network type
      *
      * @see #NETWORK_TYPE_UNKNOWN
@@ -1937,6 +1989,7 @@
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getDataNetworkType() {
         return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
@@ -1971,7 +2024,11 @@
 
     /**
      * Returns the NETWORK_TYPE_xxxx for voice
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public int getVoiceNetworkType() {
         return getVoiceNetworkType(getSubId());
@@ -2557,7 +2614,11 @@
     /**
      * Returns the serial number of the SIM, if applicable. Return null if it is
      * unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSimSerialNumber() {
          return getSimSerialNumber(getSubId());
@@ -2682,7 +2743,11 @@
     /**
      * Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getSubscriberId() {
         return getSubscriberId(getSubId());
@@ -2725,18 +2790,17 @@
      * @return ImsiEncryptionInfo Carrier specific information that will be used to encrypt the
      *         IMSI and IMPI. This includes the public key and the key identifier. This information
      *         will be stored in the device keystore. The system will return a null when no key was
-     *         found, and the carrier does not require a key. The system will throw the following
-     *         exceptions:
-     *         1. IllegalArgumentException when an invalid key is sent.
-     *         2. RuntimeException if the key is required but not found; and also if there was an
-     *         internal exception.
+     *         found, and the carrier does not require a key. The system will throw
+     *         IllegalArgumentException when an invalid key is sent or when key is required but
+     *         not found.
      * @hide
      */
     public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
         try {
             IPhoneSubInfo info = getSubscriberInfo();
             if (info == null) {
-                throw new RuntimeException("IMSI error: Subscriber Info is null");
+                Rlog.e(TAG,"IMSI error: Subscriber Info is null");
+                return null;
             }
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
             if (keyType != KEY_TYPE_EPDG && keyType != KEY_TYPE_WLAN) {
@@ -2744,19 +2808,46 @@
             }
             ImsiEncryptionInfo imsiEncryptionInfo = info.getCarrierInfoForImsiEncryption(
                     subId, keyType, mContext.getOpPackageName());
-            if (imsiEncryptionInfo  == null
-                    && isImsiEncryptionRequired(subId, keyType)) {
+            if (imsiEncryptionInfo == null && isImsiEncryptionRequired(subId, keyType)) {
                 Rlog.e(TAG, "IMSI error: key is required but not found");
-                throw new RuntimeException("IMSI error: key is required but not found");
+                throw new IllegalArgumentException("IMSI error: key is required but not found");
             }
             return imsiEncryptionInfo;
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
-            throw new RuntimeException("IMSI error: Remote Exception");
         } catch (NullPointerException ex) {
             // This could happen before phone restarts due to crashing
             Rlog.e(TAG, "getCarrierInfoForImsiEncryption NullPointerException" + ex);
-            throw new RuntimeException("IMSI error: Null Pointer exception");
+        }
+        return null;
+    }
+
+    /**
+     * Resets the Carrier Keys in the database. This involves 2 steps:
+     *  1. Delete the keys from the database.
+     *  2. Send an intent to download new Certificates.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * @hide
+     */
+    public void resetCarrierKeysForImsiEncryption() {
+        try {
+            IPhoneSubInfo info = getSubscriberInfo();
+            if (info == null) {
+                Rlog.e(TAG, "IMSI error: Subscriber Info is null");
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("IMSI error: Subscriber Info is null");
+                }
+                return;
+            }
+            int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
+            info.resetCarrierKeysForImsiEncryption(subId, mContext.getOpPackageName());
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCarrierInfoForImsiEncryption RemoteException" + ex);
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -2795,7 +2886,7 @@
      * device keystore.
      * <p>
      * Requires Permission:
-     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * @param imsiEncryptionInfo which includes the Key Type, the Public Key
      *        (java.security.PublicKey) and the Key Identifier.and the Key Identifier.
      *        The keyIdentifier Attribute value pair that helps a server locate
@@ -2823,7 +2914,11 @@
     /**
      * Returns the Group Identifier Level1 for a GSM phone.
      * Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getGroupIdLevel1() {
         try {
@@ -2864,9 +2959,15 @@
     /**
      * Returns the phone number string for line 1, for example, the MSISDN
      * for a GSM phone. Return null if it is unavailable.
-     * <p>
-     * The default SMS app can also use this.
+     *
+     * <p>Requires Permission:
+     *     {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE},
+     *     {@link android.Manifest.permission#READ_SMS READ_SMS},
+     *     {@link android.Manifest.permission#READ_PHONE_NUMBERS READ_PHONE_NUMBERS},
+     *     that the caller is the default SMS app,
+     *     or that the caller has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges or default SMS app
     @RequiresPermission(anyOf = {
             android.Manifest.permission.READ_PHONE_STATE,
             android.Manifest.permission.READ_SMS,
@@ -2921,8 +3022,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag alpha-tagging of the dailing nubmer
      * @param number The dialing number
@@ -2938,8 +3038,7 @@
      * change the actual MSISDN/MDN. To unset alphatag or number, pass in a null
      * value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the subscriber that the alphatag and dialing number belongs to.
      * @param alphaTag alpha-tagging of the dailing nubmer
@@ -3058,7 +3157,11 @@
 
     /**
      * Returns the voice mail number. Return null if it is unavailable.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailNumber() {
         return getVoiceMailNumber(getSubId());
@@ -3119,8 +3222,7 @@
     /**
      * Sets the voice mail number.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param alphaTag The alpha tag to display.
      * @param number The voicemail number.
@@ -3132,8 +3234,7 @@
     /**
      * Sets the voicemail number for the given subscriber.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param alphaTag The alpha tag to display.
@@ -3154,9 +3255,9 @@
     /**
      * Enables or disables the visual voicemail client for a phone account.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or
-     * has permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}), or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle the phone account to change the client state
      * @param enabled the new state of the client
@@ -3219,11 +3320,15 @@
      * to the TelephonyManager. Returns {@code null} when there is no package responsible for
      * processing visual voicemail for the subscription.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @see #createForSubscriptionId(int)
      * @see #createForPhoneAccountHandle(PhoneAccountHandle)
      * @see VisualVoicemailService
      */
     @Nullable
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVisualVoicemailPackageName() {
         try {
@@ -3412,6 +3517,7 @@
      * Initial SIM activation state, unknown. Not set by any carrier apps.
      * @hide
      */
+    @SystemApi
     public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0;
 
     /**
@@ -3422,12 +3528,14 @@
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
      * @hide
      */
+    @SystemApi
     public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1;
 
     /**
      * Indicate SIM has been successfully activated with full service
      * @hide
      */
+    @SystemApi
     public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2;
 
     /**
@@ -3437,6 +3545,7 @@
      * deactivated sim state and set it back to activated after successfully run activation service.
      * @hide
      */
+    @SystemApi
     public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3;
 
     /**
@@ -3444,14 +3553,46 @@
      * note this is currently available for data activation state. For example out of byte sim.
      * @hide
      */
+    @SystemApi
     public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4;
 
+    /** @hide */
+    @IntDef({
+            SIM_ACTIVATION_STATE_UNKNOWN,
+            SIM_ACTIVATION_STATE_ACTIVATING,
+            SIM_ACTIVATION_STATE_ACTIVATED,
+            SIM_ACTIVATION_STATE_DEACTIVATED,
+            SIM_ACTIVATION_STATE_RESTRICTED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SimActivationState{}
+
+     /**
+      * Sets the voice activation state
+      *
+      * <p>Requires Permission:
+      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+      * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+      *
+      * @param activationState The voice activation state
+      * @see #SIM_ACTIVATION_STATE_UNKNOWN
+      * @see #SIM_ACTIVATION_STATE_ACTIVATING
+      * @see #SIM_ACTIVATION_STATE_ACTIVATED
+      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+      * @hide
+      */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoiceActivationState(@SimActivationState int activationState) {
+        setVoiceActivationState(getSubId(), activationState);
+    }
+
     /**
      * Sets the voice activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The voice activation state of the given subscriber.
@@ -3461,22 +3602,44 @@
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @hide
      */
-    public void setVoiceActivationState(int subId, int activationState) {
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setVoiceActivationState(int subId, @SimActivationState int activationState) {
         try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setVoiceActivationState(subId, activationState);
-        } catch (RemoteException ex) {
-        } catch (NullPointerException ex) {
-        }
+           ITelephony telephony = getITelephony();
+           if (telephony != null)
+               telephony.setVoiceActivationState(subId, activationState);
+       } catch (RemoteException ex) {
+       } catch (NullPointerException ex) {
+       }
+    }
+
+    /**
+     * Sets the data activation state
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param activationState The data activation state
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDataActivationState(@SimActivationState int activationState) {
+        setDataActivationState(getSubId(), activationState);
     }
 
     /**
      * Sets the data activation state for the given subscriber.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription id.
      * @param activationState The data activation state of the given subscriber.
@@ -3487,7 +3650,8 @@
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
      * @hide
      */
-    public void setDataActivationState(int subId, int activationState) {
+    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDataActivationState(int subId, @SimActivationState int activationState) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -3498,8 +3662,32 @@
     }
 
     /**
+     * Returns the voice activation state
+     *
+     * <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 voiceActivationState
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getVoiceActivationState() {
+        return getVoiceActivationState(getSubId());
+    }
+
+    /**
      * Returns the voice activation state for the given subscriber.
      *
+     * <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}).
+     *
      * @param subId The subscription id.
      *
      * @return voiceActivationState for the given subscriber
@@ -3509,8 +3697,8 @@
      * @see #SIM_ACTIVATION_STATE_DEACTIVATED
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getVoiceActivationState(int subId) {
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getVoiceActivationState(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -3522,8 +3710,33 @@
     }
 
     /**
+     * Returns the data activation state
+     *
+     * <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 dataActivationState for the given subscriber
+     * @see #SIM_ACTIVATION_STATE_UNKNOWN
+     * @see #SIM_ACTIVATION_STATE_ACTIVATING
+     * @see #SIM_ACTIVATION_STATE_ACTIVATED
+     * @see #SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see #SIM_ACTIVATION_STATE_RESTRICTED
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getDataActivationState() {
+        return getDataActivationState(getSubId());
+    }
+
+    /**
      * Returns the data activation state for the given subscriber.
      *
+     * <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}).
+     *
      * @param subId The subscription id.
      *
      * @return dataActivationState for the given subscriber
@@ -3534,8 +3747,8 @@
      * @see #SIM_ACTIVATION_STATE_RESTRICTED
      * @hide
      */
-    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
-    public int getDataActivationState(int subId) {
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public @SimActivationState int getDataActivationState(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
@@ -3579,7 +3792,11 @@
     /**
      * Retrieves the alphabetic identifier associated with the voice
      * mail number.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String getVoiceMailAlphaTag() {
         return getVoiceMailAlphaTag(getSubId());
@@ -3608,27 +3825,29 @@
     }
 
     /**
-     * Send the special dialer code. The IPC caller must be the current default dialer or has
-     * carrier privileges.
-     * @see #hasCarrierPrivileges
+     * Send the special dialer code. The IPC caller must be the current default dialer or have
+     * carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param inputCode The special dialer code to send
      *
      * @throws SecurityException if the caller does not have carrier privileges or is not the
      *         current default dialer
-     *
-     * @throws IllegalStateException if telephony service is unavailable.
      */
     public void sendDialerSpecialCode(String inputCode) {
         try {
             final ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                if (!isSystemProcess()) {
+                    throw new RuntimeException("Telephony service unavailable");
+                }
+                return;
+            }
             telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowFromSystemServer();
-        } catch (NullPointerException ex) {
-            // This could happen before phone restarts due to crashing
-            throw new IllegalStateException("Telephony service unavailable");
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -3701,26 +3920,45 @@
         return IPhoneSubInfo.Stub.asInterface(ServiceManager.getService("iphonesubinfo"));
     }
 
-    /** Device call state: No activity. */
+    /**
+     * Device call state: No activity.
+     */
     public static final int CALL_STATE_IDLE = 0;
-    /** Device call state: Ringing. A new call arrived and is
+    /**
+     * Device call state: Ringing. A new call arrived and is
      *  ringing or waiting. In the latter case, another call is
-     *  already active. */
+     *  already active.
+     */
     public static final int CALL_STATE_RINGING = 1;
-    /** Device call state: Off-hook. At least one call exists
-      * that is dialing, active, or on hold, and no calls are ringing
-      * or waiting. */
+    /**
+     * Device call state: Off-hook. At least one call exists
+     * that is dialing, active, or on hold, and no calls are ringing
+     * or waiting.
+     */
     public static final int CALL_STATE_OFFHOOK = 2;
 
+    /** @hide */
+    @IntDef(prefix = { "CALL_STATE_" }, value = {
+            CALL_STATE_IDLE,
+            CALL_STATE_RINGING,
+            CALL_STATE_OFFHOOK
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CallState{}
+
     /**
-     * Returns one of the following constants that represents the current state of all
-     * phone calls.
+     * Returns the state of all calls on the device.
+     * <p>
+     * This method considers not only calls in the Telephony stack, but also calls via other
+     * {@link android.telecom.ConnectionService} implementations.
+     * <p>
+     * Note: The call state returned via this method may differ from what is reported by
+     * {@link PhoneStateListener#onCallStateChanged(int, String)}, as that callback only considers
+     * Telephony (mobile) calls.
      *
-     * {@link TelephonyManager#CALL_STATE_RINGING}
-     * {@link TelephonyManager#CALL_STATE_OFFHOOK}
-     * {@link TelephonyManager#CALL_STATE_IDLE}
+     * @return the current call state.
      */
-    public int getCallState() {
+    public @CallState int getCallState() {
         try {
             ITelecomService telecom = getTelecomService();
             if (telecom != null) {
@@ -3733,23 +3971,31 @@
     }
 
     /**
-     * Returns a constant indicating the call state (cellular) on the device
-     * for a subscription.
+     * Returns the Telephony call state for calls on a specific subscription.
+     * <p>
+     * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
+     * considers the state of calls from other {@link android.telecom.ConnectionService}
+     * implementations.
      *
-     * @param subId whose call state is returned
+     * @param subId the subscription to check call state for.
      * @hide
      */
-    public int getCallState(int subId) {
+    public @CallState int getCallState(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
         return getCallStateForSlot(phoneId);
     }
 
     /**
-     * See getCallState.
+     * Returns the Telephony call state for calls on a specific SIM slot.
+     * <p>
+     * Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
+     * considers the state of calls from other {@link android.telecom.ConnectionService}
+     * implementations.
      *
+     * @param slotIndex the SIM slot index to check call state for.
      * @hide
      */
-    public int getCallStateForSlot(int slotIndex) {
+    public @CallState int getCallStateForSlot(int slotIndex) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony == null)
@@ -4138,8 +4384,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return an IccOpenLogicalChannelResponse object.
@@ -4156,8 +4402,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @param p2 P2 parameter (described in ISO 7816-4).
@@ -4173,8 +4419,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param AID Application id. See ETSI 102.221 and 101.220.
@@ -4199,8 +4445,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as retruned by a successful
      *            iccOpenLogicalChannel.
@@ -4216,8 +4462,8 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as retruned by a successful
@@ -4242,8 +4488,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
@@ -4269,8 +4515,8 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param channel is the channel id to be closed as returned by a successful
@@ -4305,8 +4551,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param cla Class of the APDU command.
      * @param instruction Instruction of the APDU command.
@@ -4330,8 +4576,8 @@
      * Input parameters equivalent to TS 27.007 AT+CSIM command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param cla Class of the APDU command.
@@ -4362,8 +4608,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param fileID
      * @param command
@@ -4382,8 +4628,8 @@
      * Returns the response APDU for a command APDU sent through SIM_IO.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param fileID
@@ -4411,8 +4657,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param content String containing SAT/USAT response in hexadecimal
      *                format starting with command tag. See TS 102 223 for
@@ -4429,8 +4675,8 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param content String containing SAT/USAT response in hexadecimal
@@ -4455,10 +4701,10 @@
     /**
      * Read one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @return the NV item as a String, or null on any failure.
@@ -4481,10 +4727,10 @@
     /**
      * Write one of the NV items defined in com.android.internal.telephony.RadioNVItems.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param itemID the ID of the item to read.
      * @param itemValue the value to write, as a String.
@@ -4508,10 +4754,10 @@
     /**
      * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param preferredRoamingList byte array containing the new PRL.
      * @return true on success; false on any failure.
@@ -4535,10 +4781,10 @@
      * Perform the specified type of NV config reset. The radio will be taken offline
      * and the device must be rebooted after the operation. Used for device
      * configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param resetType reset type: 1: reload NV reset, 2: erase NV reset, 3: factory NV reset
      * @return true on success; false on any failure.
@@ -4753,10 +4999,10 @@
         String v = android.provider.Settings.Global.getString(cr, name);
 
         if (index == Integer.MAX_VALUE) {
-            throw new RuntimeException("putIntAtIndex index == MAX_VALUE index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index == MAX_VALUE index=" + index);
         }
         if (index < 0) {
-            throw new RuntimeException("putIntAtIndex index < 0 index=" + index);
+            throw new IllegalArgumentException("putIntAtIndex index < 0 index=" + index);
         }
         if (v != null) {
             valArray = v.split(",");
@@ -4886,8 +5132,8 @@
      * Returns the response of authentication for the default subscription.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges or READ_PRIVILEGED_PHONE_STATE
-     * permission.
+     * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
      * @param authType the authentication type, {@link #AUTHTYPE_EAP_AKA} or
@@ -4895,9 +5141,10 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      */
+    // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
+    // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
+    // it's not public API.
     public String getIccAuthentication(int appType, int authType, String data) {
         return getIccAuthentication(getSubId(), appType, authType, data);
     }
@@ -4906,7 +5153,7 @@
      * Returns the response of USIM Authentication for specified subId.
      * Returns null if the authentication hasn't been successful
      *
-     * <p>Requires that the calling app has carrier privileges.
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId subscription ID used for authentication
      * @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -4915,8 +5162,6 @@
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
      * @return the response of authentication, or null if not available
-     *
-     * @see #hasCarrierPrivileges
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
@@ -4937,8 +5182,12 @@
      * Returns an array of Forbidden PLMNs from the USIM App
      * Returns null if the query fails.
      *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
      * @return an array of forbidden PLMNs or null if not available
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public String[] getForbiddenPlmns() {
       return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
@@ -5109,19 +5358,18 @@
     }
 
     /**
-     * Determines if emergency calling is allowed for the MMTEL feature on the slot provided.
-     * @param slotIndex The SIM slot of the MMTEL feature
-     * @return true if emergency calling is allowed, false otherwise.
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
      * @hide
      */
-    public boolean isEmergencyMmTelAvailable(int slotIndex) {
+    public boolean isResolvingImsBinding() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.isEmergencyMmTelAvailable(slotIndex);
+                return telephony.isResolvingImsBinding();
             }
         } catch (RemoteException e) {
-            Rlog.e(TAG, "isEmergencyMmTelAvailable, RemoteException: " + e.getMessage());
+            Rlog.e(TAG, "isResolvingImsBinding, RemoteException: " + e.getMessage());
         }
         return false;
     }
@@ -5144,10 +5392,10 @@
     /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return the preferred network type, defined in RILConstants.java.
      * @hide
@@ -5167,11 +5415,12 @@
 
     /**
      * Sets the network selection mode to automatic.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setNetworkSelectionModeAutomatic() {
         try {
@@ -5187,15 +5436,14 @@
     }
 
     /**
-     * Perform a radio scan and return the list of avialble networks.
+     * Perform a radio scan and return the list of available networks.
      *
      * The return value is a list of the OperatorInfo of the networks found. Note that this
      * scan can take a long time (sometimes minutes) to happen.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @hide
      * TODO: Add an overload that takes no args.
@@ -5219,17 +5467,19 @@
      * This method is asynchronous, so the network scan results will be returned by callback.
      * The returned NetworkScan will contain a callback method which can be used to stop the scan.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges()
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param request Contains all the RAT with bands/channels that need to be scanned.
-     * @param executor The executor through which the callback should be invoked.
+     * @param executor The executor through which the callback should be invoked. Since the scan
+     *        request may trigger multiple callbacks and they must be invoked in the same order as
+     *        they are received by the platform, the user should provide an executor which executes
+     *        tasks one at a time in serial order. For example AsyncTask.SERIAL_EXECUTOR.
      * @param callback Returns network scan results or errors.
      * @return A NetworkScan obj which contains a callback which can be used to stop the scan.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
             NetworkScanRequest request, Executor executor,
@@ -5251,16 +5501,15 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public NetworkScan requestNetworkScan(
         NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
-        return requestNetworkScan(request, AsyncTask.THREAD_POOL_EXECUTOR, callback);
+        return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
     }
 
     /**
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param operatorNumeric the PLMN ID of the network to select.
      * @param persistSelection whether the selection will persist until reboot. If true, only allows
@@ -5268,6 +5517,7 @@
      * normal network selection next time.
      * @return true on success; false on any failure.
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public boolean setNetworkSelectionModeManual(String operatorNumeric, boolean persistSelection) {
         try {
@@ -5287,10 +5537,10 @@
     /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
-     * <p>
-     * Requires Permission:
-     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
-     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
@@ -5314,9 +5564,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      */
@@ -5327,9 +5575,7 @@
     /**
      * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
      *
-     * <p>
-     * Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return true on success; false on any failure.
      * @hide
@@ -5419,8 +5665,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param brand The brand name to display/set.
      * @return true if the operation was executed correctly.
@@ -5437,8 +5682,7 @@
      * brand value input. To unset the value, the same function should be
      * called with a null brand value.
      *
-     * <p>Requires that the calling app has carrier privileges.
-     * @see #hasCarrierPrivileges
+     * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param subId The subscription to use.
      * @param brand The brand name to display/set.
@@ -6092,39 +6336,44 @@
      * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
      *
      * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+     * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
      *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
-    @Deprecated
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
      * @hide
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+     * @deprecated use {@link #setDataEnabled(boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
-        setUserMobileDataEnabled(subId, enable);
+        try {
+            Log.d(TAG, "setDataEnabled: enabled=" + enable);
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setUserDataEnabled(subId, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+        }
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @SystemApi
     @Deprecated
     public boolean getDataEnabled() {
-        return isUserMobileDataEnabled();
+        return isDataEnabled();
     }
 
     /**
@@ -6137,30 +6386,35 @@
      * <p>Requires one of the following permissions:
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
+     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * <p>Note that this does not take into account any data restrictions that may be present on the
      * calling app. Such restrictions may be inspected with
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
-     *
-     * @see #hasCarrierPrivileges
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
-    @Deprecated
     public boolean isDataEnabled() {
-        return isUserMobileDataEnabled();
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
     public boolean getDataEnabled(int subId) {
-        return isUserMobileDataEnabled(subId);
+        boolean retVal = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isUserDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+        } catch (NullPointerException e) {
+        }
+        return retVal;
     }
 
     /** @hide */
@@ -6875,7 +7129,11 @@
 
     /**
      * Returns the current {@link ServiceState} information.
+     *
+     * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
      */
+    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public ServiceState getServiceState() {
         return getServiceStateForSubscriber(getSubId());
@@ -6921,14 +7179,14 @@
     /**
      * Sets the per-account voicemail ringtone.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail ringtone.
      * @param uri The URI for the ringtone to play when receiving a voicemail from a specific
      * PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -6966,14 +7224,14 @@
     /**
      * Sets the per-account preference whether vibration is enabled for voicemail notifications.
      *
-     * <p>Requires that the calling app is the default dialer, or has carrier privileges, or has
-     * permission {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
+     * <p>Requires that the calling app is the default dialer, or has carrier privileges (see
+     * {@link #hasCarrierPrivileges}, or has permission
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}.
      *
      * @param phoneAccountHandle The handle for the {@link PhoneAccount} for which to set the
      * voicemail vibration setting.
      * @param enabled Whether to enable or disable vibration for voicemail notifications from a
      * specific PhoneAccount.
-     * @see #hasCarrierPrivileges
      *
      * @deprecated Use {@link android.provider.Settings#ACTION_CHANNEL_NOTIFICATION_SETTINGS}
      * instead.
@@ -6994,8 +7252,8 @@
     /**
      * Returns carrier id of the current subscription.
      * <p>To recognize a carrier (including MVNO) as a first-class identity, Android assigns each
-     * carrier with a canonical integer a.k.a. android carrier id. The Android carrier ID is an
-     * Android platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
+     * carrier with a canonical integer a.k.a. carrier id. The carrier ID is an Android
+     * platform-wide identifier for a carrier. AOSP maintains carrier ID assignments in
      * <a href="https://android.googlesource.com/platform/packages/providers/TelephonyProvider/+/master/assets/carrier_list.textpb">here</a>
      *
      * <p>Apps which have carrier-specific configurations or business logic can use the carrier id
@@ -7004,7 +7262,7 @@
      * @return Carrier id of the current subscription. Return {@link #UNKNOWN_CARRIER_ID} if the
      * subscription is unavailable or the carrier cannot be identified.
      */
-    public int getAndroidCarrierIdForSubscription() {
+    public int getSimCarrierId() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
@@ -7012,24 +7270,23 @@
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
         }
         return UNKNOWN_CARRIER_ID;
     }
 
     /**
-     * Returns carrier name of the current subscription.
-     * <p>Carrier name is a user-facing name of carrier id
-     * {@link #getAndroidCarrierIdForSubscription()}, usually the brand name of the subsidiary
+     * Returns carrier id name of the current subscription.
+     * <p>Carrier id name is a user-facing name of carrier id
+     * {@link #getSimCarrierId()}, usually the brand name of the subsidiary
      * (e.g. T-Mobile). Each carrier could configure multiple {@link #getSimOperatorName() SPN} but
      * should have a single carrier name. Carrier name is not a canonical identity,
-     * use {@link #getAndroidCarrierIdForSubscription()} instead.
+     * use {@link #getSimCarrierId()} instead.
      * <p>The returned carrier name is unlocalized.
      *
      * @return Carrier name of the current subscription. Return {@code null} if the subscription is
      * unavailable or the carrier cannot be identified.
      */
-    public CharSequence getAndroidCarrierNameForSubscription() {
+    public CharSequence getSimCarrierIdName() {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
@@ -7037,7 +7294,6 @@
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
         }
         return null;
     }
@@ -7387,60 +7643,12 @@
     }
 
     /**
-     * Turns mobile data on or off.
-     * If the {@link TelephonyManager} object has been created with
-     * {@link #createForSubscriptionId}, this API applies to the given subId.
-     * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires Permission:
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
-     *     calling app has carrier privileges.
-     *
-     * @param enable Whether to enable mobile data.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUserMobileDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
-    }
-
-    /**
-     * Returns whether mobile data is enabled or not per user setting. There are other factors
-     * that could disable mobile data, but they are not considered here.
-     *
-     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
-     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires one of the following permissions:
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges.
-     *
-     * <p>Note that this does not take into account any data restrictions that may be present on the
-     * calling app. Such restrictions may be inspected with
-     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
-     *
-     * @return true if mobile data is enabled.
-     *
-     * @see #hasCarrierPrivileges
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE
-    })
-    public boolean isUserMobileDataEnabled() {
-        return isUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
-    }
-
-    /**
      * @hide
-     * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
-     * policyDataEnabled etc to give a final decision.
+     * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+     * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+     * capable of using.
      */
-    public boolean isMobileDataEnabled() {
+    public boolean isDataCapable() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7455,35 +7663,6 @@
     }
 
     /**
-     * Utility class of {@link #isUserMobileDataEnabled()};
-     */
-    private boolean isUserMobileDataEnabled(int subId) {
-        boolean retVal = false;
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                retVal = telephony.isUserDataEnabled(subId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
-        } catch (NullPointerException e) {
-        }
-        return retVal;
-    }
-
-    /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    private void setUserMobileDataEnabled(int subId, boolean enable) {
-        try {
-            Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setUserDataEnabled(subId, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
-        }
-    }
-
-    /**
      * In this mode, modem will not send specified indications when screen is off.
      * @hide
      */
@@ -7521,11 +7700,25 @@
      */
     public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED    = 0x4;
 
+    /**
+     * The indication for link capacity estimate update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE        = 0x8;
+
+    /**
+     * The indication for physical channel config update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG       = 0x10;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
             INDICATION_FILTER_SIGNAL_STRENGTH,
             INDICATION_FILTER_FULL_NETWORK_STATE,
-            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+            INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+            INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface IndicationFilters{}
@@ -7558,7 +7751,9 @@
             }
         } catch (RemoteException ex) {
             // This could happen if binder process crashes.
-            ex.rethrowAsRuntimeException();
+            if (!isSystemProcess()) {
+                ex.rethrowAsRuntimeException();
+            }
         }
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 946cecf..96ff332 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -137,22 +137,31 @@
                             for (int i = 0; i < parcelables.length; i++) {
                                 ci[i] = (CellInfo) parcelables[i];
                             }
-                            executor.execute(() ->
-                                    callback.onResults((List<CellInfo>) Arrays.asList(ci)));
+                            executor.execute(() ->{
+                                Rlog.d(TAG, "onResults: " + ci.toString());
+                                callback.onResults((List<CellInfo>) Arrays.asList(ci));
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onResults", e);
                         }
                         break;
                     case CALLBACK_SCAN_ERROR:
                         try {
-                            executor.execute(() -> callback.onError(message.arg1));
+                            final int errorCode = message.arg1;
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onError: " + errorCode);
+                                callback.onError(errorCode);
+                            });
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onError", e);
                         }
                         break;
                     case CALLBACK_SCAN_COMPLETE:
                         try {
-                            executor.execute(() -> callback.onComplete());
+                            executor.execute(() -> {
+                                Rlog.d(TAG, "onComplete");
+                                callback.onComplete();
+                            });
                             mScanInfo.remove(message.arg2);
                         } catch (Exception e) {
                             Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java
index 0c17147..125161d 100644
--- a/telephony/java/android/telephony/UiccSlotInfo.java
+++ b/telephony/java/android/telephony/UiccSlotInfo.java
@@ -60,6 +60,7 @@
     private final String mCardId;
     private final @CardStateInfo int mCardStateInfo;
     private final int mLogicalSlotIdx;
+    private final boolean mIsExtendedApduSupported;
 
     public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() {
         @Override
@@ -79,6 +80,7 @@
         mCardId = in.readString();
         mCardStateInfo = in.readInt();
         mLogicalSlotIdx = in.readInt();
+        mIsExtendedApduSupported = in.readByte() != 0;
     }
 
     @Override
@@ -88,6 +90,7 @@
         dest.writeString(mCardId);
         dest.writeInt(mCardStateInfo);
         dest.writeInt(mLogicalSlotIdx);
+        dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0));
     }
 
     @Override
@@ -96,12 +99,13 @@
     }
 
     public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId,
-            @CardStateInfo int cardStateInfo, int logicalSlotIdx) {
+            @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) {
         this.mIsActive = isActive;
         this.mIsEuicc = isEuicc;
         this.mCardId = cardId;
         this.mCardStateInfo = cardStateInfo;
         this.mLogicalSlotIdx = logicalSlotIdx;
+        this.mIsExtendedApduSupported = isExtendedApduSupported;
     }
 
     public boolean getIsActive() {
@@ -125,6 +129,13 @@
         return mLogicalSlotIdx;
     }
 
+    /**
+     * @return {@code true} if this slot supports extended APDU from ATR, {@code false} otherwise.
+     */
+    public boolean getIsExtendedApduSupported() {
+        return mIsExtendedApduSupported;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -139,7 +150,8 @@
                 && (mIsEuicc == that.mIsEuicc)
                 && (mCardId == that.mCardId)
                 && (mCardStateInfo == that.mCardStateInfo)
-                && (mLogicalSlotIdx == that.mLogicalSlotIdx);
+                && (mLogicalSlotIdx == that.mLogicalSlotIdx)
+                && (mIsExtendedApduSupported == that.mIsExtendedApduSupported);
     }
 
     @Override
@@ -150,6 +162,7 @@
         result = 31 * result + Objects.hashCode(mCardId);
         result = 31 * result + mCardStateInfo;
         result = 31 * result + mLogicalSlotIdx;
+        result = 31 * result + (mIsExtendedApduSupported ? 1 : 0);
         return result;
     }
 
@@ -165,6 +178,8 @@
                 + mCardStateInfo
                 + ", phoneId="
                 + mLogicalSlotIdx
+                + ", mIsExtendedApduSupported="
+                + mIsExtendedApduSupported
                 + ")";
     }
 }
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 73a05af..145ed7e 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -17,10 +17,10 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.StringDef;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.hardware.radio.V1_0.ApnTypes;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Telephony;
@@ -28,17 +28,15 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.net.InetAddress;
-import java.net.MalformedURLException;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -46,25 +44,184 @@
  */
 public class ApnSetting implements Parcelable {
 
-    static final String LOG_TAG = "ApnSetting";
+    private static final String LOG_TAG = "ApnSetting";
     private static final boolean VDBG = false;
 
+    private static final Map<String, Integer> APN_TYPE_STRING_MAP;
+    private static final Map<Integer, String> APN_TYPE_INT_MAP;
+    private static final Map<String, Integer> PROTOCOL_STRING_MAP;
+    private static final Map<Integer, String> PROTOCOL_INT_MAP;
+    private static final Map<String, Integer> MVNO_TYPE_STRING_MAP;
+    private static final Map<Integer, String> MVNO_TYPE_INT_MAP;
+    private static final int NOT_IN_MAP_INT = -1;
+    private static final int NO_PORT_SPECIFIED = -1;
+
+    /** All APN types except IA. */
+    private static final int TYPE_ALL_BUT_IA = ApnTypes.ALL & (~ApnTypes.IA);
+
+    /** APN type for default data traffic and HiPri traffic. */
+    public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
+    /** APN type for MMS traffic. */
+    public static final int TYPE_MMS = ApnTypes.MMS;
+    /** APN type for SUPL assisted GPS. */
+    public static final int TYPE_SUPL = ApnTypes.SUPL;
+    /** APN type for DUN traffic. */
+    public static final int TYPE_DUN = ApnTypes.DUN;
+    /** APN type for HiPri traffic. */
+    public static final int TYPE_HIPRI = ApnTypes.HIPRI;
+    /** APN type for accessing the carrier's FOTA portal, used for over the air updates. */
+    public static final int TYPE_FOTA = ApnTypes.FOTA;
+    /** APN type for IMS. */
+    public static final int TYPE_IMS = ApnTypes.IMS;
+    /** APN type for CBS. */
+    public static final int TYPE_CBS = ApnTypes.CBS;
+    /** APN type for IA Initial Attach APN. */
+    public static final int TYPE_IA = ApnTypes.IA;
+    /**
+     * APN type for Emergency PDN. This is not an IA apn, but is used
+     * for access to carrier services in an emergency call situation.
+     */
+    public static final int TYPE_EMERGENCY = ApnTypes.EMERGENCY;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+        TYPE_DEFAULT,
+        TYPE_MMS,
+        TYPE_SUPL,
+        TYPE_DUN,
+        TYPE_HIPRI,
+        TYPE_FOTA,
+        TYPE_IMS,
+        TYPE_CBS,
+        TYPE_IA,
+        TYPE_EMERGENCY
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ApnType {}
+
+    // Possible values for authentication types.
+    /** No authentication type. */
+    public static final int AUTH_TYPE_NONE = 0;
+    /** Authentication type for PAP. */
+    public static final int AUTH_TYPE_PAP = 1;
+    /** Authentication type for CHAP. */
+    public static final int AUTH_TYPE_CHAP = 2;
+    /** Authentication type for PAP or CHAP. */
+    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "AUTH_TYPE_" }, value = {
+        AUTH_TYPE_NONE,
+        AUTH_TYPE_PAP,
+        AUTH_TYPE_CHAP,
+        AUTH_TYPE_PAP_OR_CHAP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AuthType {}
+
+    // Possible values for protocol.
+    /** Protocol type for IP. */
+    public static final int PROTOCOL_IP = 0;
+    /** Protocol type for IPV6. */
+    public static final int PROTOCOL_IPV6 = 1;
+    /** Protocol type for IPV4V6. */
+    public static final int PROTOCOL_IPV4V6 = 2;
+    /** Protocol type for PPP. */
+    public static final int PROTOCOL_PPP = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "PROTOCOL_" }, value = {
+        PROTOCOL_IP,
+        PROTOCOL_IPV6,
+        PROTOCOL_IPV4V6,
+        PROTOCOL_PPP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ProtocolType {}
+
+    // Possible values for MVNO type.
+    /** MVNO type for service provider name. */
+    public static final int MVNO_TYPE_SPN = 0;
+    /** MVNO type for IMSI. */
+    public static final int MVNO_TYPE_IMSI = 1;
+    /** MVNO type for group identifier level 1. */
+    public static final int MVNO_TYPE_GID = 2;
+    /** MVNO type for ICCID. */
+    public static final int MVNO_TYPE_ICCID = 3;
+
+    /** @hide */
+    @IntDef(prefix = { "MVNO_TYPE_" }, value = {
+        MVNO_TYPE_SPN,
+        MVNO_TYPE_IMSI,
+        MVNO_TYPE_GID,
+        MVNO_TYPE_ICCID,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MvnoType {}
+
+    static {
+        APN_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        APN_TYPE_STRING_MAP.put("*", TYPE_ALL_BUT_IA);
+        APN_TYPE_STRING_MAP.put("default", TYPE_DEFAULT);
+        APN_TYPE_STRING_MAP.put("mms", TYPE_MMS);
+        APN_TYPE_STRING_MAP.put("supl", TYPE_SUPL);
+        APN_TYPE_STRING_MAP.put("dun", TYPE_DUN);
+        APN_TYPE_STRING_MAP.put("hipri", TYPE_HIPRI);
+        APN_TYPE_STRING_MAP.put("fota", TYPE_FOTA);
+        APN_TYPE_STRING_MAP.put("ims", TYPE_IMS);
+        APN_TYPE_STRING_MAP.put("cbs", TYPE_CBS);
+        APN_TYPE_STRING_MAP.put("ia", TYPE_IA);
+        APN_TYPE_STRING_MAP.put("emergency", TYPE_EMERGENCY);
+        APN_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        APN_TYPE_INT_MAP.put(TYPE_DEFAULT, "default");
+        APN_TYPE_INT_MAP.put(TYPE_MMS, "mms");
+        APN_TYPE_INT_MAP.put(TYPE_SUPL, "supl");
+        APN_TYPE_INT_MAP.put(TYPE_DUN, "dun");
+        APN_TYPE_INT_MAP.put(TYPE_HIPRI, "hipri");
+        APN_TYPE_INT_MAP.put(TYPE_FOTA, "fota");
+        APN_TYPE_INT_MAP.put(TYPE_IMS, "ims");
+        APN_TYPE_INT_MAP.put(TYPE_CBS, "cbs");
+        APN_TYPE_INT_MAP.put(TYPE_IA, "ia");
+        APN_TYPE_INT_MAP.put(TYPE_EMERGENCY, "emergency");
+
+        PROTOCOL_STRING_MAP = new ArrayMap<String, Integer>();
+        PROTOCOL_STRING_MAP.put("IP", PROTOCOL_IP);
+        PROTOCOL_STRING_MAP.put("IPV6", PROTOCOL_IPV6);
+        PROTOCOL_STRING_MAP.put("IPV4V6", PROTOCOL_IPV4V6);
+        PROTOCOL_STRING_MAP.put("PPP", PROTOCOL_PPP);
+        PROTOCOL_INT_MAP = new ArrayMap<Integer, String>();
+        PROTOCOL_INT_MAP.put(PROTOCOL_IP, "IP");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV6, "IPV6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_IPV4V6, "IPV4V6");
+        PROTOCOL_INT_MAP.put(PROTOCOL_PPP, "PPP");
+
+        MVNO_TYPE_STRING_MAP = new ArrayMap<String, Integer>();
+        MVNO_TYPE_STRING_MAP.put("spn", MVNO_TYPE_SPN);
+        MVNO_TYPE_STRING_MAP.put("imsi", MVNO_TYPE_IMSI);
+        MVNO_TYPE_STRING_MAP.put("gid", MVNO_TYPE_GID);
+        MVNO_TYPE_STRING_MAP.put("iccid", MVNO_TYPE_ICCID);
+        MVNO_TYPE_INT_MAP = new ArrayMap<Integer, String>();
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_SPN, "spn");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_IMSI, "imsi");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_GID, "gid");
+        MVNO_TYPE_INT_MAP.put(MVNO_TYPE_ICCID, "iccid");
+    }
+
     private final String mEntryName;
     private final String mApnName;
-    private final InetAddress mProxy;
-    private final int mPort;
-    private final URL mMmsc;
-    private final InetAddress mMmsProxy;
-    private final int mMmsPort;
+    private final InetAddress mProxyAddress;
+    private final int mProxyPort;
+    private final Uri mMmsc;
+    private final InetAddress mMmsProxyAddress;
+    private final int mMmsProxyPort;
     private final String mUser;
     private final String mPassword;
     private final int mAuthType;
-    private final List<String> mTypes;
-    private final int mTypesBitmap;
+    private final int mApnTypeBitmask;
     private final int mId;
     private final String mOperatorNumeric;
-    private final String mProtocol;
-    private final String mRoamingProtocol;
+    private final int mProtocol;
+    private final int mRoamingProtocol;
     private final int mMtu;
 
     private final boolean mCarrierEnabled;
@@ -78,22 +235,12 @@
     private final int mWaitTime;
     private final int mMaxConnsTime;
 
-    private final String mMvnoType;
+    private final int mMvnoType;
     private final String mMvnoMatchData;
 
     private boolean mPermanentFailed = false;
 
     /**
-     * Returns the types bitmap of the APN.
-     *
-     * @return types bitmap of the APN
-     * @hide
-     */
-    public int getTypesBitmap() {
-        return mTypesBitmap;
-    }
-
-    /**
      * Returns the MTU size of the mobile interface to which the APN connected.
      *
      * @return the MTU size of the APN
@@ -211,8 +358,8 @@
      *
      * @return proxy address.
      */
-    public InetAddress getProxy() {
-        return mProxy;
+    public InetAddress getProxyAddress() {
+        return mProxyAddress;
     }
 
     /**
@@ -220,15 +367,15 @@
      *
      * @return proxy port
      */
-    public int getPort() {
-        return mPort;
+    public int getProxyPort() {
+        return mProxyPort;
     }
     /**
-     * Returns the MMSC URL of the APN.
+     * Returns the MMSC Uri of the APN.
      *
-     * @return MMSC URL.
+     * @return MMSC Uri.
      */
-    public URL getMmsc() {
+    public Uri getMmsc() {
         return mMmsc;
     }
 
@@ -237,8 +384,8 @@
      *
      * @return MMS proxy address.
      */
-    public InetAddress getMmsProxy() {
-        return mMmsProxy;
+    public InetAddress getMmsProxyAddress() {
+        return mMmsProxyAddress;
     }
 
     /**
@@ -246,8 +393,8 @@
      *
      * @return MMS proxy port
      */
-    public int getMmsPort() {
-        return mMmsPort;
+    public int getMmsProxyPort() {
+        return mMmsProxyPort;
     }
 
     /**
@@ -268,21 +415,9 @@
         return mPassword;
     }
 
-    /** @hide */
-    @IntDef({
-            AUTH_TYPE_NONE,
-            AUTH_TYPE_PAP,
-            AUTH_TYPE_CHAP,
-            AUTH_TYPE_PAP_OR_CHAP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AuthType {}
-
     /**
      * Returns the authentication type of the APN.
      *
-     * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-     *
      * @return authentication type
      */
     @AuthType
@@ -290,32 +425,20 @@
         return mAuthType;
     }
 
-    /** @hide */
-    @StringDef({
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ApnType {}
-
     /**
-     * Returns the list of APN types of the APN.
+     * Returns the bitmask of APN types.
      *
-     * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+     * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+     * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+     * MMS-specific connections.
      *
-     * @return the list of APN types
+     * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+     *
+     * @see Builder#setApnTypeBitmask(int)
+     * @return a bitmask describing the types of the APN
      */
-    @ApnType
-    public List<String> getTypes() {
-        return mTypes;
+    public @ApnType int getApnTypeBitmask() {
+        return mApnTypeBitmask;
     }
 
     /**
@@ -328,7 +451,7 @@
     }
 
     /**
-     * Returns the numeric operator ID for the APN. Usually
+     * Returns the numeric operator ID for the APN. Numeric operator ID is defined as
      * {@link android.provider.Telephony.Carriers#MCC} +
      * {@link android.provider.Telephony.Carriers#MNC}.
      *
@@ -338,37 +461,29 @@
         return mOperatorNumeric;
     }
 
-    /** @hide */
-    @StringDef({
-            PROTOCOL_IP,
-            PROTOCOL_IPV6,
-            PROTOCOL_IPV4V6,
-            PROTOCOL_PPP,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProtocolType {}
-
     /**
      * Returns the protocol to use to connect to this APN.
      *
-     * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-     * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+     * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setProtocol(int)
      * @return the protocol
      */
     @ProtocolType
-    public String getProtocol() {
+    public int getProtocol() {
         return mProtocol;
     }
 
     /**
-     * Returns the protocol to use to connect to this APN when roaming.
+     * Returns the protocol to use to connect to this APN while the device is roaming.
      *
-     * The syntax is the same as {@link android.provider.Telephony.Carriers#PROTOCOL}.
+     * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
      *
+     * @see Builder#setRoamingProtocol(int)
      * @return the roaming protocol
      */
-    public String getRoamingProtocol() {
+    @ProtocolType
+    public int getRoamingProtocol() {
         return mRoamingProtocol;
     }
 
@@ -398,41 +513,29 @@
         return mNetworkTypeBitmask;
     }
 
-    /** @hide */
-    @StringDef({
-            MVNO_TYPE_SPN,
-            MVNO_TYPE_IMSI,
-            MVNO_TYPE_GID,
-            MVNO_TYPE_ICCID,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface MvnoType {}
-
     /**
      * Returns the MVNO match type for this APN.
      *
-     * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-     *
+     * @see Builder#setMvnoType(int)
      * @return the MVNO match type
      */
     @MvnoType
-    public String getMvnoType() {
+    public int getMvnoType() {
         return mMvnoType;
     }
 
     private ApnSetting(Builder builder) {
         this.mEntryName = builder.mEntryName;
         this.mApnName = builder.mApnName;
-        this.mProxy = builder.mProxy;
-        this.mPort = builder.mPort;
+        this.mProxyAddress = builder.mProxyAddress;
+        this.mProxyPort = builder.mProxyPort;
         this.mMmsc = builder.mMmsc;
-        this.mMmsProxy = builder.mMmsProxy;
-        this.mMmsPort = builder.mMmsPort;
+        this.mMmsProxyAddress = builder.mMmsProxyAddress;
+        this.mMmsProxyPort = builder.mMmsProxyPort;
         this.mUser = builder.mUser;
         this.mPassword = builder.mPassword;
         this.mAuthType = builder.mAuthType;
-        this.mTypes = (builder.mTypes == null ? new ArrayList<String>() : builder.mTypes);
-        this.mTypesBitmap = builder.mTypesBitmap;
+        this.mApnTypeBitmask = builder.mApnTypeBitmask;
         this.mId = builder.mId;
         this.mOperatorNumeric = builder.mOperatorNumeric;
         this.mProtocol = builder.mProtocol;
@@ -451,25 +554,25 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(int id, String operatorNumeric, String entryName,
-            String apnName, InetAddress proxy, int port, URL mmsc, InetAddress mmsProxy,
-            int mmsPort, String user, String password, int authType, List<String> types,
-            String protocol, String roamingProtocol, boolean carrierEnabled,
+            String apnName, InetAddress proxy, int port, Uri mmsc, InetAddress mmsProxy,
+            int mmsPort, String user, String password, int authType, int mApnTypeBitmask,
+            int protocol, int roamingProtocol, boolean carrierEnabled,
             int networkTypeBitmask, int profileId, boolean modemCognitive, int maxConns,
-            int waitTime, int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+            int waitTime, int maxConnsTime, int mtu, int mvnoType, String mvnoMatchData) {
         return new Builder()
                 .setId(id)
                 .setOperatorNumeric(operatorNumeric)
                 .setEntryName(entryName)
                 .setApnName(apnName)
-                .setProxy(proxy)
-                .setPort(port)
+                .setProxyAddress(proxy)
+                .setProxyPort(port)
                 .setMmsc(mmsc)
-                .setMmsProxy(mmsProxy)
-                .setMmsPort(mmsPort)
+                .setMmsProxyAddress(mmsProxy)
+                .setMmsProxyPort(mmsPort)
                 .setUser(user)
                 .setPassword(password)
                 .setAuthType(authType)
-                .setTypes(types)
+                .setApnTypeBitmask(mApnTypeBitmask)
                 .setProtocol(protocol)
                 .setRoamingProtocol(roamingProtocol)
                 .setCarrierEnabled(carrierEnabled)
@@ -487,7 +590,7 @@
 
     /** @hide */
     public static ApnSetting makeApnSetting(Cursor cursor) {
-        String[] types = parseTypes(
+        final int apnTypesBitmask = parseTypes(
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.TYPE)));
         int networkTypeBitmask = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Telephony.Carriers.NETWORK_TYPE_BITMASK));
@@ -507,7 +610,7 @@
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PROXY))),
                 portFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.PORT))),
-                URLFromString(cursor.getString(
+                UriFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSC))),
                 inetAddressFromString(cursor.getString(
                         cursor.getColumnIndexOrThrow(Telephony.Carriers.MMSPROXY))),
@@ -516,10 +619,12 @@
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.USER)),
                 cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PASSWORD)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.AUTH_TYPE)),
-                Arrays.asList(types),
-                cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.ROAMING_PROTOCOL)),
+                apnTypesBitmask,
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROTOCOL)))),
+                nullToNotInMapInt(PROTOCOL_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.ROAMING_PROTOCOL)))),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 networkTypeBitmask,
@@ -531,8 +636,9 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MAX_CONNS_TIME)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)),
-                cursor.getString(cursor.getColumnIndexOrThrow(
-                        Telephony.Carriers.MVNO_TYPE)),
+                nullToNotInMapInt(MVNO_TYPE_STRING_MAP.get(
+                    cursor.getString(cursor.getColumnIndexOrThrow(
+                        Telephony.Carriers.MVNO_TYPE)))),
                 cursor.getString(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MVNO_MATCH_DATA)));
     }
@@ -540,8 +646,8 @@
     /** @hide */
     public static ApnSetting makeApnSetting(ApnSetting apn) {
         return makeApnSetting(apn.mId, apn.mOperatorNumeric, apn.mEntryName, apn.mApnName,
-                apn.mProxy, apn.mPort, apn.mMmsc, apn.mMmsProxy, apn.mMmsPort, apn.mUser,
-                apn.mPassword, apn.mAuthType, apn.mTypes, apn.mProtocol, apn.mRoamingProtocol,
+                apn.mProxyAddress, apn.mProxyPort, apn.mMmsc, apn.mMmsProxyAddress, apn.mMmsProxyPort, apn.mUser,
+                apn.mPassword, apn.mAuthType, apn.mApnTypeBitmask, apn.mProtocol, apn.mRoamingProtocol,
                 apn.mCarrierEnabled, apn.mNetworkTypeBitmask, apn.mProfileId,
                 apn.mModemCognitive, apn.mMaxConns, apn.mWaitTime, apn.mMaxConnsTime, apn.mMtu,
                 apn.mMvnoType, apn.mMvnoMatchData);
@@ -555,18 +661,14 @@
                 .append(", ").append(mId)
                 .append(", ").append(mOperatorNumeric)
                 .append(", ").append(mApnName)
-                .append(", ").append(inetAddressToString(mProxy))
-                .append(", ").append(URLToString(mMmsc))
-                .append(", ").append(inetAddressToString(mMmsProxy))
-                .append(", ").append(portToString(mMmsPort))
-                .append(", ").append(portToString(mPort))
+                .append(", ").append(inetAddressToString(mProxyAddress))
+                .append(", ").append(UriToString(mMmsc))
+                .append(", ").append(inetAddressToString(mMmsProxyAddress))
+                .append(", ").append(portToString(mMmsProxyPort))
+                .append(", ").append(portToString(mProxyPort))
                 .append(", ").append(mAuthType).append(", ");
-        for (int i = 0; i < mTypes.size(); i++) {
-            sb.append(mTypes.get(i));
-            if (i < mTypes.size() - 1) {
-                sb.append(" | ");
-            }
-        }
+        final String[] types = deParseTypes(mApnTypeBitmask).split(",");
+        sb.append(TextUtils.join(" | ", types)).append(", ");
         sb.append(", ").append(mProtocol);
         sb.append(", ").append(mRoamingProtocol);
         sb.append(", ").append(mCarrierEnabled);
@@ -588,56 +690,37 @@
      * @hide
      */
     public boolean hasMvnoParams() {
-        return !TextUtils.isEmpty(mMvnoType) && !TextUtils.isEmpty(mMvnoMatchData);
+        return (mMvnoType != NOT_IN_MAP_INT) && !TextUtils.isEmpty(mMvnoMatchData);
     }
 
     /** @hide */
-    public boolean canHandleType(String type) {
-        if (!mCarrierEnabled) return false;
-        boolean wildcardable = true;
-        if (TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
-        for (String t : mTypes) {
-            // DEFAULT handles all, and HIPRI is handled by DEFAULT
-            if (t.equalsIgnoreCase(type)
-                    || (wildcardable && t.equalsIgnoreCase(TYPE_ALL))
-                    || (t.equalsIgnoreCase(TYPE_DEFAULT)
-                    && type.equalsIgnoreCase(TYPE_HIPRI))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean canHandleType(@ApnType int type) {
+        return mCarrierEnabled && ((mApnTypeBitmask & type) == type);
     }
 
     // check whether the types of two APN same (even only one type of each APN is same)
     private boolean typeSameAny(ApnSetting first, ApnSetting second) {
         if (VDBG) {
             StringBuilder apnType1 = new StringBuilder(first.mApnName + ": ");
-            for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-                apnType1.append(first.mTypes.get(index1));
-                apnType1.append(",");
-            }
+            apnType1.append(deParseTypes(first.mApnTypeBitmask));
 
             StringBuilder apnType2 = new StringBuilder(second.mApnName + ": ");
-            for (int index1 = 0; index1 < second.mTypes.size(); index1++) {
-                apnType2.append(second.mTypes.get(index1));
-                apnType2.append(",");
-            }
+            apnType2.append(deParseTypes(second.mApnTypeBitmask));
+
             Rlog.d(LOG_TAG, "APN1: is " + apnType1);
             Rlog.d(LOG_TAG, "APN2: is " + apnType2);
         }
 
-        for (int index1 = 0; index1 < first.mTypes.size(); index1++) {
-            for (int index2 = 0; index2 < second.mTypes.size(); index2++) {
-                if (first.mTypes.get(index1).equals(ApnSetting.TYPE_ALL)
-                        || second.mTypes.get(index2).equals(ApnSetting.TYPE_ALL)
-                        || first.mTypes.get(index1).equals(second.mTypes.get(index2))) {
-                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
-                    return true;
-                }
+        if ((first.mApnTypeBitmask & second.mApnTypeBitmask) != 0) {
+            if (VDBG) {
+                Rlog.d(LOG_TAG, "typeSameAny: return true");
             }
+            return true;
         }
 
-        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        if (VDBG) {
+            Rlog.d(LOG_TAG, "typeSameAny: return false");
+        }
         return false;
     }
 
@@ -655,16 +738,15 @@
                 && Objects.equals(mId, other.mId)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort,other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort,other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && Objects.equals(mProtocol, other.mProtocol)
                 && Objects.equals(mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -701,16 +783,15 @@
         return mEntryName.equals(other.mEntryName)
                 && Objects.equals(mOperatorNumeric, other.mOperatorNumeric)
                 && Objects.equals(mApnName, other.mApnName)
-                && Objects.equals(mProxy, other.mProxy)
+                && Objects.equals(mProxyAddress, other.mProxyAddress)
                 && Objects.equals(mMmsc, other.mMmsc)
-                && Objects.equals(mMmsProxy, other.mMmsProxy)
-                && Objects.equals(mMmsPort, other.mMmsPort)
-                && Objects.equals(mPort, other.mPort)
+                && Objects.equals(mMmsProxyAddress, other.mMmsProxyAddress)
+                && Objects.equals(mMmsProxyPort, other.mMmsProxyPort)
+                && Objects.equals(mProxyPort, other.mProxyPort)
                 && Objects.equals(mUser, other.mUser)
                 && Objects.equals(mPassword, other.mPassword)
                 && Objects.equals(mAuthType, other.mAuthType)
-                && Objects.equals(mTypes, other.mTypes)
-                && Objects.equals(mTypesBitmap, other.mTypesBitmap)
+                && Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
                 && (isDataRoaming || Objects.equals(mProtocol,other.mProtocol))
                 && (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
                 && Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -736,17 +817,17 @@
                 && !other.canHandleType(TYPE_DUN)
                 && Objects.equals(this.mApnName, other.mApnName)
                 && !typeSameAny(this, other)
-                && xorEqualsInetAddress(this.mProxy, other.mProxy)
-                && xorEqualsPort(this.mPort, other.mPort)
+                && xorEquals(this.mProxyAddress, other.mProxyAddress)
+                && xorEqualsPort(this.mProxyPort, other.mProxyPort)
                 && xorEquals(this.mProtocol, other.mProtocol)
                 && xorEquals(this.mRoamingProtocol, other.mRoamingProtocol)
                 && Objects.equals(this.mCarrierEnabled, other.mCarrierEnabled)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mMvnoType, other.mMvnoType)
                 && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
-                && xorEqualsURL(this.mMmsc, other.mMmsc)
-                && xorEqualsInetAddress(this.mMmsProxy, other.mMmsProxy)
-                && xorEqualsPort(this.mMmsPort, other.mMmsPort))
+                && xorEquals(this.mMmsc, other.mMmsc)
+                && xorEquals(this.mMmsProxyAddress, other.mMmsProxyAddress)
+                && xorEqualsPort(this.mMmsProxyPort, other.mMmsProxyPort))
                 && Objects.equals(this.mNetworkTypeBitmask, other.mNetworkTypeBitmask);
     }
 
@@ -757,42 +838,23 @@
                 || TextUtils.isEmpty(second));
     }
 
-    // Equal or one is not specified.
-    private boolean xorEqualsInetAddress(InetAddress first, InetAddress second) {
-        return first == null || second == null || first.equals(second);
-    }
-
-    // Equal or one is not specified.
-    private boolean xorEqualsURL(URL first, URL second) {
+    // Equal or one is not null.
+    private boolean xorEquals(Object first, Object second) {
         return first == null || second == null || first.equals(second);
     }
 
     // Equal or one is not specified.
     private boolean xorEqualsPort(int first, int second) {
-        return first == -1 || second == -1 || Objects.equals(first, second);
+        return first == NO_PORT_SPECIFIED || second == NO_PORT_SPECIFIED
+            || Objects.equals(first, second);
     }
 
-    // Helper function to convert APN string into a 32-bit bitmask.
-    private static int getApnBitmask(String apn) {
-        switch (apn) {
-            case TYPE_DEFAULT: return ApnTypes.DEFAULT;
-            case TYPE_MMS: return ApnTypes.MMS;
-            case TYPE_SUPL: return ApnTypes.SUPL;
-            case TYPE_DUN: return ApnTypes.DUN;
-            case TYPE_HIPRI: return ApnTypes.HIPRI;
-            case TYPE_FOTA: return ApnTypes.FOTA;
-            case TYPE_IMS: return ApnTypes.IMS;
-            case TYPE_CBS: return ApnTypes.CBS;
-            case TYPE_IA: return ApnTypes.IA;
-            case TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
-            case TYPE_ALL: return ApnTypes.ALL;
-            default: return ApnTypes.NONE;
-        }
-    }
-
-    private String deParseTypes(List<String> types) {
-        if (types == null) {
-            return null;
+    private String deParseTypes(int apnTypeBitmask) {
+        List<String> types = new ArrayList<>();
+        for (Integer type : APN_TYPE_INT_MAP.keySet()) {
+            if ((apnTypeBitmask & type) == type) {
+                types.add(APN_TYPE_INT_MAP.get(type));
+            }
         }
         return TextUtils.join(",", types);
     }
@@ -808,21 +870,25 @@
         apnValue.put(Telephony.Carriers.NUMERIC, nullToEmpty(mOperatorNumeric));
         apnValue.put(Telephony.Carriers.NAME, nullToEmpty(mEntryName));
         apnValue.put(Telephony.Carriers.APN, nullToEmpty(mApnName));
-        apnValue.put(Telephony.Carriers.PROXY, mProxy == null ? "" : inetAddressToString(mProxy));
-        apnValue.put(Telephony.Carriers.PORT, portToString(mPort));
-        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : URLToString(mMmsc));
-        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsPort));
-        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxy == null
-                ? "" : inetAddressToString(mMmsProxy));
+        apnValue.put(Telephony.Carriers.PROXY, mProxyAddress == null ? ""
+            : inetAddressToString(mProxyAddress));
+        apnValue.put(Telephony.Carriers.PORT, portToString(mProxyPort));
+        apnValue.put(Telephony.Carriers.MMSC, mMmsc == null ? "" : UriToString(mMmsc));
+        apnValue.put(Telephony.Carriers.MMSPORT, portToString(mMmsProxyPort));
+        apnValue.put(Telephony.Carriers.MMSPROXY, mMmsProxyAddress == null
+                ? "" : inetAddressToString(mMmsProxyAddress));
         apnValue.put(Telephony.Carriers.USER, nullToEmpty(mUser));
         apnValue.put(Telephony.Carriers.PASSWORD, nullToEmpty(mPassword));
         apnValue.put(Telephony.Carriers.AUTH_TYPE, mAuthType);
-        String apnType = deParseTypes(mTypes);
+        String apnType = deParseTypes(mApnTypeBitmask);
         apnValue.put(Telephony.Carriers.TYPE, nullToEmpty(apnType));
-        apnValue.put(Telephony.Carriers.PROTOCOL, nullToEmpty(mProtocol));
-        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL, nullToEmpty(mRoamingProtocol));
+        apnValue.put(Telephony.Carriers.PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mProtocol)));
+        apnValue.put(Telephony.Carriers.ROAMING_PROTOCOL,
+            nullToEmpty(PROTOCOL_INT_MAP.get(mRoamingProtocol)));
         apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
-        apnValue.put(Telephony.Carriers.MVNO_TYPE, nullToEmpty(mMvnoType));
+        apnValue.put(Telephony.Carriers.MVNO_TYPE,
+            nullToEmpty(MVNO_TYPE_INT_MAP.get(mMvnoType)));
         apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
 
         return apnValue;
@@ -830,32 +896,31 @@
 
     /**
      * @param types comma delimited list of APN types
-     * @return array of APN types
+     * @return bitmask of APN types
      * @hide
      */
-    public static String[] parseTypes(String types) {
-        String[] result;
-        // If unset, set to DEFAULT.
+    public static int parseTypes(String types) {
+        // If unset, set to ALL.
         if (TextUtils.isEmpty(types)) {
-            result = new String[1];
-            result[0] = TYPE_ALL;
+            return TYPE_ALL_BUT_IA;
         } else {
-            result = types.split(",");
-        }
-        return result;
-    }
-
-    private static URL URLFromString(String url) {
-        try {
-            return TextUtils.isEmpty(url) ? null : new URL(url);
-        } catch (MalformedURLException e) {
-            Log.e(LOG_TAG, "Can't parse URL from string.");
-            return null;
+            int result = 0;
+            for (String str : types.split(",")) {
+                Integer type = APN_TYPE_STRING_MAP.get(str);
+                if (type != null) {
+                    result |= type;
+                }
+            }
+            return result;
         }
     }
 
-    private static String URLToString(URL url) {
-        return url == null ? "" : url.toString();
+    private static Uri UriFromString(String uri) {
+        return TextUtils.isEmpty(uri) ? null : Uri.parse(uri);
+    }
+
+    private static String UriToString(Uri uri) {
+        return uri == null ? "" : uri.toString();
     }
 
     private static InetAddress inetAddressFromString(String inetAddress) {
@@ -887,7 +952,7 @@
     }
 
     private static int portFromString(String strPort) {
-        int port = -1;
+        int port = NO_PORT_SPECIFIED;
         if (!TextUtils.isEmpty(strPort)) {
             try {
                 port = Integer.parseInt(strPort);
@@ -899,7 +964,7 @@
     }
 
     private static String portToString(int port) {
-        return port == -1 ? "" : Integer.toString(port);
+        return port == NO_PORT_SPECIFIED ? "" : Integer.toString(port);
     }
 
     // Implement Parcelable.
@@ -916,19 +981,19 @@
         dest.writeString(mOperatorNumeric);
         dest.writeString(mEntryName);
         dest.writeString(mApnName);
-        dest.writeValue(mProxy);
-        dest.writeInt(mPort);
+        dest.writeValue(mProxyAddress);
+        dest.writeInt(mProxyPort);
         dest.writeValue(mMmsc);
-        dest.writeValue(mMmsProxy);
-        dest.writeInt(mMmsPort);
+        dest.writeValue(mMmsProxyAddress);
+        dest.writeInt(mMmsProxyPort);
         dest.writeString(mUser);
         dest.writeString(mPassword);
         dest.writeInt(mAuthType);
-        dest.writeStringArray(mTypes.toArray(new String[0]));
-        dest.writeString(mProtocol);
-        dest.writeString(mRoamingProtocol);
+        dest.writeInt(mApnTypeBitmask);
+        dest.writeInt(mProtocol);
+        dest.writeInt(mRoamingProtocol);
         dest.writeInt(mCarrierEnabled ? 1: 0);
-        dest.writeString(mMvnoType);
+        dest.writeInt(mMvnoType);
         dest.writeInt(mNetworkTypeBitmask);
     }
 
@@ -939,23 +1004,23 @@
         final String apnName = in.readString();
         final InetAddress proxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int port = in.readInt();
-        final URL mmsc = (URL)in.readValue(URL.class.getClassLoader());
+        final Uri mmsc = (Uri)in.readValue(Uri.class.getClassLoader());
         final InetAddress mmsProxy = (InetAddress)in.readValue(InetAddress.class.getClassLoader());
         final int mmsPort = in.readInt();
         final String user = in.readString();
         final String password = in.readString();
         final int authType = in.readInt();
-        final List<String> types = Arrays.asList(in.readStringArray());
-        final String protocol = in.readString();
-        final String roamingProtocol = in.readString();
+        final int apnTypesBitmask = in.readInt();
+        final int protocol = in.readInt();
+        final int roamingProtocol = in.readInt();
         final boolean carrierEnabled = in.readInt() > 0;
-        final String mvnoType = in.readString();
+        final int mvnoType = in.readInt();
         final int networkTypeBitmask = in.readInt();
 
         return makeApnSetting(id, operatorNumeric, entryName, apnName,
-                proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, types, protocol,
-                roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
-                0, 0, 0, 0, mvnoType, null);
+            proxy, port, mmsc, mmsProxy, mmsPort, user, password, authType, apnTypesBitmask,
+            protocol, roamingProtocol, carrierEnabled, networkTypeBitmask, 0, false,
+            0, 0, 0, 0, mvnoType, null);
     }
 
     public static final Parcelable.Creator<ApnSetting> CREATOR =
@@ -971,89 +1036,26 @@
                 }
             };
 
-    /**
-     * APN types for data connections.  These are usage categories for an APN
-     * entry.  One APN entry may support multiple APN types, eg, a single APN
-     * may service regular internet traffic ("default") as well as MMS-specific
-     * connections.<br/>
-     * ALL is a special type to indicate that this APN entry can
-     * service all data connections.
-     */
-    public static final String TYPE_ALL = "*";
-    /** APN type for default data traffic */
-    public static final String TYPE_DEFAULT = "default";
-    /** APN type for MMS traffic */
-    public static final String TYPE_MMS = "mms";
-    /** APN type for SUPL assisted GPS */
-    public static final String TYPE_SUPL = "supl";
-    /** APN type for DUN traffic */
-    public static final String TYPE_DUN = "dun";
-    /** APN type for HiPri traffic */
-    public static final String TYPE_HIPRI = "hipri";
-    /** APN type for FOTA */
-    public static final String TYPE_FOTA = "fota";
-    /** APN type for IMS */
-    public static final String TYPE_IMS = "ims";
-    /** APN type for CBS */
-    public static final String TYPE_CBS = "cbs";
-    /** APN type for IA Initial Attach APN */
-    public static final String TYPE_IA = "ia";
-    /** APN type for Emergency PDN. This is not an IA apn, but is used
-     * for access to carrier services in an emergency call situation. */
-    public static final String TYPE_EMERGENCY = "emergency";
-    /**
-     * Array of all APN types
-     *
-     * @hide
-     */
-    public static final String[] ALL_TYPES = {
-            TYPE_DEFAULT,
-            TYPE_MMS,
-            TYPE_SUPL,
-            TYPE_DUN,
-            TYPE_HIPRI,
-            TYPE_FOTA,
-            TYPE_IMS,
-            TYPE_CBS,
-            TYPE_IA,
-            TYPE_EMERGENCY
-    };
-
-    // Possible values for authentication types.
-    public static final int AUTH_TYPE_NONE = 0;
-    public static final int AUTH_TYPE_PAP = 1;
-    public static final int AUTH_TYPE_CHAP = 2;
-    public static final int AUTH_TYPE_PAP_OR_CHAP = 3;
-
-    // Possible values for protocol.
-    public static final String PROTOCOL_IP = "IP";
-    public static final String PROTOCOL_IPV6 = "IPV6";
-    public static final String PROTOCOL_IPV4V6 = "IPV4V6";
-    public static final String PROTOCOL_PPP = "PPP";
-
-    // Possible values for MVNO type.
-    public static final String MVNO_TYPE_SPN = "spn";
-    public static final String MVNO_TYPE_IMSI = "imsi";
-    public static final String MVNO_TYPE_GID = "gid";
-    public static final String MVNO_TYPE_ICCID = "iccid";
+    private static int nullToNotInMapInt(Integer value) {
+        return value == null ? NOT_IN_MAP_INT : value;
+    }
 
     public static class Builder{
         private String mEntryName;
         private String mApnName;
-        private InetAddress mProxy;
-        private int mPort = -1;
-        private URL mMmsc;
-        private InetAddress mMmsProxy;
-        private int mMmsPort = -1;
+        private InetAddress mProxyAddress;
+        private int mProxyPort = NO_PORT_SPECIFIED;
+        private Uri mMmsc;
+        private InetAddress mMmsProxyAddress;
+        private int mMmsProxyPort = NO_PORT_SPECIFIED;
         private String mUser;
         private String mPassword;
         private int mAuthType;
-        private List<String> mTypes;
-        private int mTypesBitmap;
+        private int mApnTypeBitmask;
         private int mId;
         private String mOperatorNumeric;
-        private String mProtocol;
-        private String mRoamingProtocol;
+        private int mProtocol = NOT_IN_MAP_INT;
+        private int mRoamingProtocol = NOT_IN_MAP_INT;
         private int mMtu;
         private int mNetworkTypeBitmask;
         private boolean mCarrierEnabled;
@@ -1062,7 +1064,7 @@
         private int mMaxConns;
         private int mWaitTime;
         private int mMaxConnsTime;
-        private String mMvnoType;
+        private int mMvnoType = NOT_IN_MAP_INT;
         private String mMvnoMatchData;
 
         /**
@@ -1182,8 +1184,8 @@
          *
          * @param proxy the proxy address to set for the APN
          */
-        public Builder setProxy(InetAddress proxy) {
-            this.mProxy = proxy;
+        public Builder setProxyAddress(InetAddress proxy) {
+            this.mProxyAddress = proxy;
             return this;
         }
 
@@ -1192,17 +1194,17 @@
          *
          * @param port the proxy port to set for the APN
          */
-        public Builder setPort(int port) {
-            this.mPort = port;
+        public Builder setProxyPort(int port) {
+            this.mProxyPort = port;
             return this;
         }
 
         /**
-         * Sets the MMSC URL of the APN.
+         * Sets the MMSC Uri of the APN.
          *
-         * @param mmsc the MMSC URL to set for the APN
+         * @param mmsc the MMSC Uri to set for the APN
          */
-        public Builder setMmsc(URL mmsc) {
+        public Builder setMmsc(Uri mmsc) {
             this.mMmsc = mmsc;
             return this;
         }
@@ -1212,8 +1214,8 @@
          *
          * @param mmsProxy the MMS proxy address to set for the APN
          */
-        public Builder setMmsProxy(InetAddress mmsProxy) {
-            this.mMmsProxy = mmsProxy;
+        public Builder setMmsProxyAddress(InetAddress mmsProxy) {
+            this.mMmsProxyAddress = mmsProxy;
             return this;
         }
 
@@ -1222,8 +1224,8 @@
          *
          * @param mmsPort the MMS proxy port to set for the APN
          */
-        public Builder setMmsPort(int mmsPort) {
-            this.mMmsPort = mmsPort;
+        public Builder setMmsProxyPort(int mmsPort) {
+            this.mMmsProxyPort = mmsPort;
             return this;
         }
 
@@ -1251,8 +1253,6 @@
         /**
          * Sets the authentication type of the APN.
          *
-         * Example of possible values: {@link #AUTH_TYPE_NONE}, {@link #AUTH_TYPE_PAP}.
-         *
          * @param authType the authentication type to set for the APN
          */
         public Builder setAuthType(@AuthType int authType) {
@@ -1261,25 +1261,25 @@
         }
 
         /**
-         * Sets the list of APN types of the APN.
+         * Sets the bitmask of APN types.
          *
-         * Example of possible values: {@link #TYPE_DEFAULT}, {@link #TYPE_MMS}.
+         * <p>Apn types are usage categories for an APN entry. One APN entry may support multiple
+         * APN types, eg, a single APN may service regular internet traffic ("default") as well as
+         * MMS-specific connections.
          *
-         * @param types the list of APN types to set for the APN
+         * <p>The bitmask of APN types is calculated from APN types defined in {@link ApnSetting}.
+         *
+         * @param apnTypeBitmask a bitmask describing the types of the APN
          */
-        public Builder setTypes(@ApnType List<String> types) {
-            this.mTypes = types;
-            int apnBitmap = 0;
-            for (int i = 0; i < mTypes.size(); i++) {
-                mTypes.set(i, mTypes.get(i).toLowerCase());
-                apnBitmap |= getApnBitmask(mTypes.get(i));
-            }
-            this.mTypesBitmap = apnBitmap;
+        public Builder setApnTypeBitmask(@ApnType int apnTypeBitmask) {
+            this.mApnTypeBitmask = apnTypeBitmask;
             return this;
         }
 
         /**
-         * Set the numeric operator ID for the APN.
+         * Sets the numeric operator ID for the APN. Numeric operator ID is defined as
+         * {@link android.provider.Telephony.Carriers#MCC} +
+         * {@link android.provider.Telephony.Carriers#MNC}.
          *
          * @param operatorNumeric the numeric operator ID to set for this entry
          */
@@ -1291,22 +1291,23 @@
         /**
          * Sets the protocol to use to connect to this APN.
          *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * Example of possible values: {@link #PROTOCOL_IP}, {@link #PROTOCOL_IPV6}.
+         * <p>Protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param protocol the protocol to set to use to connect to this APN
          */
-        public Builder setProtocol(@ProtocolType String protocol) {
+        public Builder setProtocol(@ProtocolType int protocol) {
             this.mProtocol = protocol;
             return this;
         }
 
         /**
-         * Sets the protocol to use to connect to this APN when roaming.
+         * Sets the protocol to use to connect to this APN when the device is roaming.
+         *
+         * <p>Roaming protocol is one of the {@code PDP_type} values in TS 27.007 section 10.1.1.
          *
          * @param roamingProtocol the protocol to set to use to connect to this APN when roaming
          */
-        public Builder setRoamingProtocol(String roamingProtocol) {
+        public Builder setRoamingProtocol(@ProtocolType  int roamingProtocol) {
             this.mRoamingProtocol = roamingProtocol;
             return this;
         }
@@ -1334,16 +1335,25 @@
         /**
          * Sets the MVNO match type for this APN.
          *
-         * Example of possible values: {@link #MVNO_TYPE_SPN}, {@link #MVNO_TYPE_IMSI}.
-         *
          * @param mvnoType the MVNO match type to set for this APN
          */
-        public Builder setMvnoType(@MvnoType String mvnoType) {
+        public Builder setMvnoType(@MvnoType int mvnoType) {
             this.mMvnoType = mvnoType;
             return this;
         }
 
+        /**
+         * Builds {@link ApnSetting} from this builder.
+         *
+         * @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
+         * is empty, or {@link #setApnTypeBitmask(int)} doesn't contain a valid bit,
+         * {@link ApnSetting} built from this builder otherwise.
+         */
         public ApnSetting build() {
+            if ((mApnTypeBitmask & ApnTypes.ALL) == 0 || TextUtils.isEmpty(mApnName)
+                || TextUtils.isEmpty(mEntryName)) {
+                return null;
+            }
             return new ApnSetting(this);
         }
     }
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 88db22b..edf3b08 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.PendingIntent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.UiccAccessRule;
@@ -26,7 +27,14 @@
 
 import com.android.internal.util.Preconditions;
 
-/** Information about a subscription which is available for download. */
+/**
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
+ *
+ * <p>For example, a DownloadableSubscription can be created through an activation code parsed from
+ * a QR code. A server address can be parsed from the activation code to download more information
+ * about the profile, such as carrier name, access rules, etc.
+ */
 public final class DownloadableSubscription implements Parcelable {
 
     public static final Creator<DownloadableSubscription> CREATOR =
@@ -136,7 +144,15 @@
     /**
      * Create a DownloadableSubscription for the given activation code.
      *
-     * @param encodedActivationCode the activation code to use. Must not be null.
+     * <p>This fills the encodedActivationCode field. Other fields like confirmationCode,
+     * carrierName and accessRules may be filled in the implementation of
+     * {@code android.service.euicc.EuiccService} if exists.
+     *
+     * @param encodedActivationCode the activation code to use. An activation code can be parsed
+     *         from a user scanned QR code. The format of activation code is defined in SGP.22. For
+     *         example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For detail, see
+     *         {@code com.android.euicc.data.ActivationCode}. Must not be null.
+     *
      * @return the {@link DownloadableSubscription} which may be passed to
      *     {@link EuiccManager#downloadSubscription}.
      */
@@ -157,6 +173,9 @@
 
     /**
      * Returns the confirmation code.
+     *
+     * <p>As an example, the confirmation code can be input by the user through a carrier app or the
+     * UI component of the eUICC local profile assistant (LPA) application.
      */
     @Nullable
     public String getConfirmationCode() {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index 71ef5de..d6f94da 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -15,11 +15,12 @@
  */
 package android.telephony.euicc;
 
+import android.Manifest;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -73,6 +74,7 @@
      */
     @SystemApi
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public static final String ACTION_OTA_STATUS_CHANGED =
             "android.telephony.euicc.action.OTA_STATUS_CHANGED";
 
@@ -140,11 +142,9 @@
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
 
     /**
-     * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+     * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
      * callbacks providing the downloadable subscription metadata.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
 
@@ -301,6 +301,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public int getOtaStatus() {
         if (!isEnabled()) {
             return EUICC_OTA_STATUS_UNAVAILABLE;
@@ -325,6 +326,7 @@
      * @param switchAfterDownload if true, the profile will be activated upon successful download.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void downloadSubscription(DownloadableSubscription subscription,
             boolean switchAfterDownload, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -387,6 +389,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
         if (!isEnabled()) {
             PendingIntent callbackIntent =
@@ -422,6 +425,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDownloadableSubscriptionMetadata(
             DownloadableSubscription subscription, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -452,6 +456,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -496,6 +501,7 @@
      * @param subscriptionId the ID of the subscription to delete.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -523,6 +529,7 @@
      *     current profile without activating another profile to replace it.
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
@@ -548,6 +555,7 @@
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void updateSubscriptionNickname(
             int subscriptionId, String nickname, PendingIntent callbackIntent) {
         if (!isEnabled()) {
@@ -566,12 +574,13 @@
      * Erase all subscriptions and reset the eUICC.
      *
      * <p>Requires that the calling app has the
-     * {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission. This is for
-     * internal system use only.
+     * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
      *
      * @param callbackIntent a PendingIntent to launch when the operation completes.
      * @hide
      */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
     public void eraseSubscriptions(PendingIntent callbackIntent) {
         if (!isEnabled()) {
             sendUnavailableError(callbackIntent);
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 2748cb5..c008711 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -161,11 +161,6 @@
         }
 
         @Override
-        public void notifyImsFeatureReady(int slotId, int featureType) {
-            ImsService.this.notifyImsFeatureReady(slotId, featureType);
-        }
-
-        @Override
         public IImsConfig getConfig(int slotId) {
             ImsConfigImplBase c = ImsService.this.getConfig(slotId);
             return c != null ? c.getIImsConfig() : null;
@@ -274,25 +269,6 @@
         }
     }
 
-    private void notifyImsFeatureReady(int slotId, int featureType) {
-        synchronized (mFeaturesBySlot) {
-            // get ImsFeature associated with the slot/feature
-            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
-            if (features == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
-                        "slot " + slotId);
-                return;
-            }
-            ImsFeature f = features.get(featureType);
-            if (f == null) {
-                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
-                        + featureType + " exists on slot " + slotId);
-                return;
-            }
-            f.onFeatureReady();
-        }
-    }
-
     /**
      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
index 86f8606..c7da681 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl
@@ -36,8 +36,6 @@
     ImsFeatureConfiguration querySupportedImsFeatures();
     // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
     void notifyImsServiceReadyForFeatureCreation();
-    // Synchronous call to ensure the new ImsFeature is ready before using the Feature.
-    void notifyImsFeatureReady(int slotId, int featureType);
     void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
     IImsConfig getConfig(int slotId);
     IImsRegistration getRegistration(int slotId);
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 2fffd36..aaf1a1cf8 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -139,34 +139,44 @@
 
         @Override
         public int queryCapabilityStatus() throws RemoteException {
-            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            synchronized (mLock) {
+                return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+            }
         }
 
         @Override
         public void addCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
             MmTelFeature.this.addCapabilityCallback(c);
         }
 
         @Override
         public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            // no need to lock, structure already handles multithreading.
             MmTelFeature.this.removeCapabilityCallback(c);
         }
 
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
                 IImsCapabilityCallback c) throws RemoteException {
-            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            synchronized (mLock) {
+                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+            }
         }
 
         @Override
         public void queryCapabilityConfiguration(int capability, int radioTech,
                 IImsCapabilityCallback c) {
-            queryCapabilityConfigurationInternal(capability, radioTech, c);
+            synchronized (mLock) {
+                queryCapabilityConfigurationInternal(capability, radioTech, c);
+            }
         }
 
         @Override
         public void setSmsListener(IImsSmsListener l) throws RemoteException {
-            MmTelFeature.this.setSmsListener(l);
+            synchronized (mLock) {
+                MmTelFeature.this.setSmsListener(l);
+            }
         }
 
         @Override
@@ -575,7 +585,7 @@
      */
     public ImsUtImplBase getUt() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsUtImplBase();
     }
 
     /**
@@ -584,7 +594,7 @@
      */
     public ImsEcbmImplBase getEcbm() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsEcbmImplBase();
     }
 
     /**
@@ -593,7 +603,7 @@
      */
     public ImsMultiEndpointImplBase getMultiEndpoint() {
         // Base Implementation - Should be overridden
-        return null;
+        return new ImsMultiEndpointImplBase();
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
index 98b67c3..2f52c0a 100644
--- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
+++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java
@@ -21,6 +21,7 @@
 import android.os.Parcelable;
 import android.telephony.ims.feature.ImsFeature;
 import android.util.ArraySet;
+import android.util.Pair;
 
 import java.util.Set;
 
@@ -34,14 +35,57 @@
  */
 @SystemApi
 public final class ImsFeatureConfiguration implements Parcelable {
+
+    public static final class FeatureSlotPair {
+        /**
+         * SIM slot that this feature is associated with.
+         */
+        public final int slotId;
+        /**
+         * The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public final @ImsFeature.FeatureType int featureType;
+
+        /**
+         * A mapping from slotId to IMS Feature type.
+         * @param slotId the SIM slot ID associated with this feature.
+         * @param featureType The feature that this slotId supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
+         */
+        public FeatureSlotPair(int slotId, @ImsFeature.FeatureType int featureType) {
+            this.slotId = slotId;
+            this.featureType = featureType;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            FeatureSlotPair that = (FeatureSlotPair) o;
+
+            if (slotId != that.slotId) return false;
+            return featureType == that.featureType;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = slotId;
+            result = 31 * result + featureType;
+            return result;
+        }
+    }
+
     /**
      * Features that this ImsService supports.
      */
-    private final Set<Integer> mFeatures;
+    private final Set<FeatureSlotPair> mFeatures;
 
     /**
-     * Builder for {@link ImsFeatureConfiguration} that makes adding supported {@link ImsFeature}s
-     * easier.
+     * Builder for {@link ImsFeatureConfiguration}.
      */
     public static class Builder {
             ImsFeatureConfiguration mConfig;
@@ -50,12 +94,15 @@
         }
 
         /**
-         * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
-         *         supports.
+         * Adds an IMS feature associated with a SIM slot ID.
+         * @param slotId The slot ID associated with the IMS feature.
+         * @param featureType The feature that the slot ID supports. Supported values are
+         * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
+         * {@link ImsFeature#FEATURE_RCS}.
          * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
          */
-        public Builder addFeature(@ImsFeature.FeatureType int feature) {
-            mConfig.addFeature(feature);
+        public Builder addFeature(int slotId, @ImsFeature.FeatureType int featureType) {
+            mConfig.addFeature(slotId, featureType);
             return this;
         }
 
@@ -66,8 +113,7 @@
 
     /**
      * Creates with all registration features empty.
-     *
-     * Consider using the provided {@link Builder} to create this configuration instead.
+     * @hide
      */
     public ImsFeatureConfiguration() {
         mFeatures = new ArraySet<>();
@@ -76,45 +122,41 @@
     /**
      * Configuration of the ImsService, which describes which features the ImsService supports
      * (for registration).
-     * @param features an array of feature integers defined in {@link ImsFeature} that describe
-     * which features this ImsService supports. Supported values are
-     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
-     * {@link ImsFeature#FEATURE_RCS}.
+     * @param features a set of {@link FeatureSlotPair}s that describe which features this
+     *         ImsService supports.
      * @hide
      */
-    public ImsFeatureConfiguration(int[] features) {
+    public ImsFeatureConfiguration(Set<FeatureSlotPair> features) {
         mFeatures = new ArraySet<>();
 
         if (features != null) {
-            for (int i : features) {
-                mFeatures.add(i);
-            }
+            mFeatures.addAll(features);
         }
     }
 
     /**
-     * @return an int[] containing the features that this ImsService supports. Supported values are
-     * {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, {@link ImsFeature#FEATURE_MMTEL}, and
-     * {@link ImsFeature#FEATURE_RCS}.
+     * @return a set of supported slot ID to feature type pairs contained within a
+     * {@link FeatureSlotPair}.
      */
-    public int[] getServiceFeatures() {
-        return mFeatures.stream().mapToInt(i->i).toArray();
+    public Set<FeatureSlotPair> getServiceFeatures() {
+        return new ArraySet<>(mFeatures);
     }
 
-    void addFeature(int feature) {
-        mFeatures.add(feature);
+    /**
+     * @hide
+     */
+    void addFeature(int slotId, int feature) {
+        mFeatures.add(new FeatureSlotPair(slotId, feature));
     }
 
     /** @hide */
     protected ImsFeatureConfiguration(Parcel in) {
-        int[] features = in.createIntArray();
-        if (features != null) {
-            mFeatures = new ArraySet<>(features.length);
-            for(Integer i : features) {
-                mFeatures.add(i);
-            }
-        } else {
-            mFeatures = new ArraySet<>();
+        int featurePairLength = in.readInt();
+        // length
+        mFeatures = new ArraySet<>(featurePairLength);
+        for (int i = 0; i < featurePairLength; i++) {
+            // pair of reads for each entry (slotId->featureType)
+            mFeatures.add(new FeatureSlotPair(in.readInt(), in.readInt()));
         }
     }
 
@@ -138,7 +180,15 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
+        FeatureSlotPair[] featureSlotPairs = new FeatureSlotPair[mFeatures.size()];
+        mFeatures.toArray(featureSlotPairs);
+        // length of list
+        dest.writeInt(featureSlotPairs.length);
+        // then pairs of integers for each entry (slotId->featureType).
+        for (FeatureSlotPair featureSlotPair : featureSlotPairs) {
+            dest.writeInt(featureSlotPair.slotId);
+            dest.writeInt(featureSlotPair.featureType);
+        }
     }
 
     /**
diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
index 92d62da..d03f63d 100644
--- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java
@@ -197,7 +197,9 @@
      * platform will deliver the message to the messages database and notify the IMS provider of the
      * result by calling {@link #acknowledgeSms(int, int, int)}.
      *
-     * This method must not be called before {@link #onReady()} is called.
+     * This method must not be called before {@link #onReady()} is called or the call will fail. If
+     * the platform is not available, {@link #acknowledgeSms(int, int, int)} will be called with the
+     * {@link #DELIVER_STATUS_ERROR_GENERIC} result code.
      *
      * @param token unique token generated by IMS providers that the platform will use to trigger
      *              callbacks for this message.
@@ -215,7 +217,14 @@
                 mListener.onSmsReceived(token, format, pdu);
             } catch (RemoteException e) {
                 Log.e(LOG_TAG, "Can not deliver sms: " + e.getMessage());
-                acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+                SmsMessage message = SmsMessage.createFromPdu(pdu, format);
+                if (message != null && message.mWrappedSmsMessage != null) {
+                    acknowledgeSms(token, message.mWrappedSmsMessage.mMessageRef,
+                            DELIVER_STATUS_ERROR_GENERIC);
+                } else {
+                    Log.w(LOG_TAG, "onSmsReceived: Invalid pdu entered.");
+                    acknowledgeSms(token, 0, DELIVER_STATUS_ERROR_GENERIC);
+                }
             }
         }
     }
diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
new file mode 100644
index 0000000..4301cb1
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/DownloadProgressListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadProgressListener {
+    /**
+     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param currentDownloadSize is the current amount downloaded.
+     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+     *   This may be different from the decoded final size, but is useful in gauging download
+     *   progress.
+     * @param currentDecodedSize is the number of bytes that have been decoded.
+     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+     */
+    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+            int currentDownloadSize, int fullDownloadSize,
+            int currentDecodedSize, int fullDecodedSize) {
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/DownloadStateCallback.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
deleted file mode 100644
index 9f60cc3..0000000
--- a/telephony/java/android/telephony/mbms/DownloadStateCallback.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2016 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.telephony.mbms;
-
-import android.annotation.IntDef;
-import android.telephony.MbmsDownloadSession;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A optional listener class used by download clients to track progress. Apps should extend this
- * class and pass an instance into
- * {@link MbmsDownloadSession#download(DownloadRequest)}
- *
- * This is optionally specified when requesting a download and will only be called while the app
- * is running.
- */
-public class DownloadStateCallback {
-
-    /**
-     * Bitmask flags used for filtering out callback methods. Used when constructing the
-     * DownloadStateCallback as an optional parameter.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, value = {ALL_UPDATES, PROGRESS_UPDATES, STATE_UPDATES})
-    public @interface FilterFlag {}
-
-    /**
-     * Receive all callbacks.
-     * Default value.
-     */
-    public static final int ALL_UPDATES = 0x00;
-    /**
-     * Receive callbacks for {@link #onProgressUpdated}.
-     */
-    public static final int PROGRESS_UPDATES = 0x01;
-    /**
-     * Receive callbacks for {@link #onStateUpdated}.
-     */
-    public static final int STATE_UPDATES = 0x02;
-
-    private final int mCallbackFilterFlags;
-
-    /**
-     * Creates a DownloadStateCallback that will receive all callbacks.
-     */
-    public DownloadStateCallback() {
-        mCallbackFilterFlags = ALL_UPDATES;
-    }
-
-    /**
-     * Creates a DownloadStateCallback that will only receive callbacks for the methods specified
-     * via the filterFlags parameter.
-     * @param filterFlags A bitmask of filter flags that will specify which callback this instance
-     *     is interested in.
-     */
-    public DownloadStateCallback(int filterFlags) {
-        mCallbackFilterFlags = filterFlags;
-    }
-
-    /**
-     * Return the currently set filter flags.
-     * @return An integer containing the bitmask of flags that this instance is interested in.
-     * @hide
-     */
-    public int getCallbackFilterFlags() {
-        return mCallbackFilterFlags;
-    }
-
-    /**
-     * Returns true if a filter flag is set for a particular callback method. If the flag is set,
-     * the callback will be delivered to the listening process.
-     * @param flag A filter flag specifying whether or not a callback method is registered to
-     *     receive callbacks.
-     * @return true if registered to receive callbacks in the listening process, false if not.
-     */
-    public final boolean isFilterFlagSet(@FilterFlag int flag) {
-        if (mCallbackFilterFlags == ALL_UPDATES) {
-            return true;
-        }
-        return (mCallbackFilterFlags & flag) > 0;
-    }
-
-    /**
-     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
-     *
-     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
-     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
-     *   the request may result in many files being downloaded and the client
-     *   may not have been able to get a list of them in advance.
-     * @param currentDownloadSize is the current amount downloaded.
-     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
-     *   This may be different from the decoded final size, but is useful in gauging download
-     *   progress.
-     * @param currentDecodedSize is the number of bytes that have been decoded.
-     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
-     */
-    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
-            int currentDownloadSize, int fullDownloadSize,
-            int currentDecodedSize, int fullDecodedSize) {
-    }
-
-    /**
-     * Gives download state callbacks for a file in a {@link DownloadRequest}.
-     *
-     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
-     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
-     *   the request may result in many files being downloaded and the client
-     *   may not have been able to get a list of them in advance.
-     * @param state The current state of the download.
-     */
-    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-            @MbmsDownloadSession.DownloadStatus int state) {
-    }
-}
diff --git a/telephony/java/android/telephony/mbms/DownloadStatusListener.java b/telephony/java/android/telephony/mbms/DownloadStatusListener.java
new file mode 100644
index 0000000..ca77932
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/DownloadStatusListener.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 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.telephony.mbms;
+
+import android.annotation.IntDef;
+import android.telephony.MbmsDownloadSession;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadStatusListener {
+    /**
+     * Gives download status callbacks for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param status The current status of the download.
+     */
+    public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus int status) {
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
similarity index 90%
rename from telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
rename to telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
index cebc70d..d0adcb5 100755
--- a/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
@@ -23,7 +23,7 @@
  * The optional interface used by download clients to track progress.
  * @hide
  */
-interface IDownloadStateCallback
+interface IDownloadProgressListener
 {
     /**
      * Gives progress callbacks for a given DownloadRequest.  Includes a FileInfo
@@ -32,6 +32,4 @@
     void onProgressUpdated(in DownloadRequest request, in FileInfo fileInfo,
             int currentDownloadSize, int fullDownloadSize,
             int currentDecodedSize, int fullDecodedSize);
-
-    void onStateUpdated(in DownloadRequest request, in FileInfo fileInfo, int state);
 }
diff --git a/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
new file mode 100755
index 0000000..799290a
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl
@@ -0,0 +1,29 @@
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.telephony.mbms;
+
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.FileInfo;
+
+/**
+ * The optional interface used by download clients to track download status.
+ * @hide
+ */
+interface IDownloadStatusListener
+{
+    void onStatusUpdated(in DownloadRequest request, in FileInfo fileInfo, int status);
+}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
similarity index 62%
rename from telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
rename to telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
index f30ae27..403f758 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadProgressListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,13 +24,14 @@
 /**
  * @hide
  */
-public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
+public class InternalDownloadProgressListener extends IDownloadProgressListener.Stub {
     private final Executor mExecutor;
-    private final DownloadStateCallback mAppCallback;
+    private final DownloadProgressListener mAppListener;
     private volatile boolean mIsStopped = false;
 
-    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Executor executor) {
-        mAppCallback = appCallback;
+    public InternalDownloadProgressListener(DownloadProgressListener appListener,
+            Executor executor) {
+        mAppListener = appListener;
         mExecutor = executor;
     }
 
@@ -47,7 +48,7 @@
             public void run() {
                 long token = Binder.clearCallingIdentity();
                 try {
-                    mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                    mAppListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
                             fullDownloadSize, currentDecodedSize, fullDecodedSize);
                 } finally {
                     Binder.restoreCallingIdentity(token);
@@ -56,26 +57,6 @@
         });
     }
 
-    @Override
-    public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
-            final int state) throws RemoteException {
-        if (mIsStopped) {
-            return;
-        }
-
-        mExecutor.execute(new Runnable() {
-            @Override
-            public void run() {
-                long token = Binder.clearCallingIdentity();
-                try {
-                    mAppCallback.onStateUpdated(request, fileInfo, state);
-                } finally {
-                    Binder.restoreCallingIdentity(token);
-                }
-            }
-        });
-    }
-
     public void stop() {
         mIsStopped = true;
     }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
index c2a79d8..2916f81 100644
--- a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -17,7 +17,6 @@
 package android.telephony.mbms;
 
 import android.os.Binder;
-import android.os.RemoteException;
 
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -36,7 +35,7 @@
     }
 
     @Override
-    public void onError(final int errorCode, final String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) {
         if (mIsStopped) {
             return;
         }
@@ -55,7 +54,7 @@
     }
 
     @Override
-    public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) {
         if (mIsStopped) {
             return;
         }
@@ -74,7 +73,7 @@
     }
 
     @Override
-    public void onMiddlewareReady() throws RemoteException {
+    public void onMiddlewareReady() {
         if (mIsStopped) {
             return;
         }
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
new file mode 100644
index 0000000..ad6bb54
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStatusListener.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
+
+import java.util.concurrent.Executor;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStatusListener extends IDownloadStatusListener.Stub {
+    private final Executor mExecutor;
+    private final DownloadStatusListener mAppListener;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadStatusListener(DownloadStatusListener appCallback, Executor executor) {
+        mAppListener = appCallback;
+        mExecutor = executor;
+    }
+
+    @Override
+    public void onStatusUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus final int status) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        mExecutor.execute(new Runnable() {
+            @Override
+            public void run() {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mAppListener.onStatusUpdated(request, fileInfo, status);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index b0c00c6..dd1061f 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -31,6 +31,8 @@
 import android.telephony.mbms.vendor.VendorUtils;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.io.FileFilter;
 import java.io.IOException;
@@ -268,7 +270,10 @@
 
         Uri finalLocation;
         try {
-            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination);
+            String relativeLocation = getFileRelativePath(request.getSourceUri().getPath(),
+                    completedFileInfo.getUri().getPath());
+            finalLocation = moveToFinalLocation(finalTempFile, appSpecifiedDestination,
+                    relativeLocation);
         } catch (IOException e) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -297,7 +302,9 @@
         for (Uri tempFileUri : tempFiles) {
             if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
                 File tempFile = new File(tempFileUri.getSchemeSpecificPart());
-                tempFile.delete();
+                if (!tempFile.delete()) {
+                    Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+                }
             }
         }
     }
@@ -440,7 +447,8 @@
     /*
      * Moves a tempfile located at fromPath to its final home where the app wants it
      */
-    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath) throws IOException {
+    private static Uri moveToFinalLocation(Uri fromPath, Path appSpecifiedPath,
+            String relativeLocation) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
             Log.w(LOG_TAG, "Downloaded file location uri " + fromPath +
                     " does not have a file scheme");
@@ -448,16 +456,46 @@
         }
 
         Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
-        if (!Files.isDirectory(appSpecifiedPath)) {
-            Files.createDirectory(appSpecifiedPath);
+        Path toFile = appSpecifiedPath.resolve(relativeLocation);
+
+        if (!Files.isDirectory(toFile.getParent())) {
+            Files.createDirectories(toFile.getParent());
         }
-        // TODO: do we want to support directory trees within the download directory?
-        Path result = Files.move(fromFile, appSpecifiedPath.resolve(fromFile.getFileName()),
+        Path result = Files.move(fromFile, toFile,
                 StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
 
         return Uri.fromFile(result.toFile());
     }
 
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getFileRelativePath(String sourceUriPath, String fileInfoPath) {
+        if (sourceUriPath.endsWith("*")) {
+            // This is a wildcard path. Strip the last path component and use that as the root of
+            // the relative path.
+            int lastSlash = sourceUriPath.lastIndexOf('/');
+            sourceUriPath = sourceUriPath.substring(0, lastSlash);
+        }
+        if (!fileInfoPath.startsWith(sourceUriPath)) {
+            Log.e(LOG_TAG, "File location specified in FileInfo does not match the source URI."
+                    + " source: " + sourceUriPath + " fileinfo path: " + fileInfoPath);
+            return null;
+        }
+        if (fileInfoPath.length() == sourceUriPath.length()) {
+            // This is the single-file download case. Return the name of the file so that the
+            // receiver puts the file directly into the dest directory.
+            return sourceUriPath.substring(sourceUriPath.lastIndexOf('/') + 1);
+        }
+
+        String prefixOmittedPath = fileInfoPath.substring(sourceUriPath.length());
+        if (prefixOmittedPath.startsWith("/")) {
+            prefixOmittedPath = prefixOmittedPath.substring(1);
+        }
+        return prefixOmittedPath;
+    }
+
     private static boolean verifyTempFilePath(Context context, String serviceId,
             Uri filePath) {
         if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
@@ -474,6 +512,8 @@
 
         if (!MbmsUtils.isContainedIn(
                 MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+            Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+                    " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
             return false;
         }
 
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
index 77dea6f..5003b57 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -16,8 +16,11 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.telephony.MbmsDownloadSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -25,6 +28,26 @@
  * cell-broadcast.
  */
 public class MbmsDownloadSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.DownloadErrors.ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST,
+            MbmsErrors.DownloadErrors.ERROR_UNKNOWN_FILE_INFO}, prefix = { "ERROR_" })
+    private @interface DownloadError{}
 
     /**
      * Indicates that the middleware has encountered an asynchronous error.
@@ -32,7 +55,7 @@
      * @param message A message, intended for debugging purposes, describing the error in further
      *                detail.
      */
-    public void onError(int errorCode, String message) {
+    public void onError(@DownloadError int errorCode, String message) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/mbms/MbmsErrors.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index b5fec44..7c4321b 100644
--- a/telephony/java/android/telephony/mbms/MbmsErrors.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -19,6 +19,13 @@
 import android.telephony.MbmsStreamingSession;
 
 public class MbmsErrors {
+    /**
+     * Indicates that the middleware has sent an error code that is not defined in the version of
+     * the SDK targeted by your app. This is an illegal value for the middleware to return -- it
+     * should only ever be generated by the framework.
+     */
+    public static final int UNKNOWN = -1;
+
     /** Indicates that the operation was successful. */
     public static final int SUCCESS = 0;
 
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index 6e03957..1bdb20bf 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,11 +16,13 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.os.Handler;
 import android.telephony.MbmsStreamingSession;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.Executor;
 
@@ -30,13 +32,34 @@
  * {@link MbmsStreamingSession#create(Context, Executor, int, MbmsStreamingSessionCallback)}.
  */
 public class MbmsStreamingSessionCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.InitializationErrors.ERROR_APP_PERMISSIONS_NOT_GRANTED,
+            MbmsErrors.InitializationErrors.ERROR_DUPLICATE_INITIALIZE,
+            MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingError{}
+
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
      * are listed in {@link MbmsErrors}.
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, @Nullable String message) {
+    public void onError(@StreamingError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index ef317ee..06b2120 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -130,8 +130,12 @@
      * Returns a File linked to the directory used to store temp files for this file service
      */
     public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
+        // Replace all non-alphanumerics/underscores with an underscore. Some filesystems don't
+        // like special characters.
+        String sanitizedServiceId = serviceId.replaceAll("[^a-zA-Z0-9_]", "_");
+
         File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
 
-        return new File(embmsTempFileDir, serviceId);
+        return new File(embmsTempFileDir, sanitizedServiceId);
     }
 }
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index 0903824..c265db6 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,13 +16,34 @@
 
 package android.telephony.mbms;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A callback class for use when the application is actively streaming content. The middleware
  * will provide updates on the status of the stream via this callback.
  */
 public class StreamingServiceCallback {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE,
+            MbmsErrors.ERROR_MIDDLEWARE_LOST,
+            MbmsErrors.ERROR_MIDDLEWARE_NOT_BOUND,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_NOT_YET_READY,
+            MbmsErrors.GeneralErrors.ERROR_OUT_OF_MEMORY,
+            MbmsErrors.GeneralErrors.ERROR_MIDDLEWARE_TEMPORARILY_UNAVAILABLE,
+            MbmsErrors.GeneralErrors.ERROR_IN_E911,
+            MbmsErrors.GeneralErrors.ERROR_NOT_CONNECTED_TO_HOME_CARRIER_LTE,
+            MbmsErrors.GeneralErrors.ERROR_UNABLE_TO_READ_SIM,
+            MbmsErrors.GeneralErrors.ERROR_CARRIER_CHANGE_NOT_ALLOWED,
+            MbmsErrors.StreamingErrors.ERROR_CONCURRENT_SERVICE_LIMIT_REACHED,
+            MbmsErrors.StreamingErrors.ERROR_UNABLE_TO_START_SERVICE,
+            MbmsErrors.StreamingErrors.ERROR_DUPLICATE_START_STREAM}, prefix = { "ERROR_" })
+    private @interface StreamingServiceError{}
 
     /**
      * Indicates broadcast signal strength is not available for this service.
@@ -39,7 +60,7 @@
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, @Nullable String message) {
+    public void onError(@StreamingServiceError int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index 7d9845f..445087fb 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -20,8 +20,9 @@
 import android.net.Uri;
 import android.telephony.mbms.DownloadRequest;
 import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
 import android.telephony.mbms.IMbmsDownloadSessionCallback;
-import android.telephony.mbms.IDownloadStateCallback;
 
 /**
  * @hide
@@ -36,11 +37,17 @@
 
     int download(in DownloadRequest downloadRequest);
 
-    int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener,
-        int flags);
+    int addStatusListener(in DownloadRequest downloadRequest,
+        IDownloadStatusListener listener);
 
-    int unregisterStateCallback(in DownloadRequest downloadRequest,
-        IDownloadStateCallback listener);
+    int removeStatusListener(in DownloadRequest downloadRequest,
+        IDownloadStatusListener listener);
+
+    int addProgressListener(in DownloadRequest downloadRequest,
+        IDownloadProgressListener listener);
+
+    int removeProgressListener(in DownloadRequest downloadRequest,
+        IDownloadProgressListener listener);
 
     List<DownloadRequest> listPendingDownloads(int subscriptionId);
 
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 86b1b7a..a9f10b1 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -24,11 +24,13 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.DownloadProgressListener;
 import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadStateCallback;
+import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IDownloadStatusListener;
 import android.telephony.mbms.IMbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsErrors;
@@ -45,47 +47,50 @@
 @SystemApi
 @TestApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
-    private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+    private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap =
+            new HashMap<>();
+    private final Map<IBinder, DownloadProgressListener> mDownloadProgressListenerBinderMap =
+            new HashMap<>();
     private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
 
+    private abstract static class VendorDownloadStatusListener extends DownloadStatusListener {
+        private final IDownloadStatusListener mListener;
+        public VendorDownloadStatusListener(IDownloadStatusListener listener) {
+            mListener = listener;
+        }
 
-    // Filters the DownloadStateCallbacks by its configuration from the app.
-    private abstract static class FilteredDownloadStateCallback extends DownloadStateCallback {
+        @Override
+        public void onStatusUpdated(DownloadRequest request, FileInfo fileInfo,
+                @MbmsDownloadSession.DownloadStatus int state) {
+            try {
+                mListener.onStatusUpdated(request, fileInfo, state);
+            } catch (RemoteException e) {
+                onRemoteException(e);
+            }
+        }
 
-        private final IDownloadStateCallback mCallback;
-        public FilteredDownloadStateCallback(IDownloadStateCallback callback, int callbackFlags) {
-            super(callbackFlags);
-            mCallback = callback;
+        protected abstract void onRemoteException(RemoteException e);
+    }
+
+    private abstract static class VendorDownloadProgressListener extends DownloadProgressListener {
+        private final IDownloadProgressListener mListener;
+
+        public VendorDownloadProgressListener(IDownloadProgressListener listener) {
+            mListener = listener;
         }
 
         @Override
         public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
                 int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
                 int fullDecodedSize) {
-            if (!isFilterFlagSet(PROGRESS_UPDATES)) {
-                return;
-            }
             try {
-                mCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                mListener.onProgressUpdated(request, fileInfo, currentDownloadSize,
                         fullDownloadSize, currentDecodedSize, fullDecodedSize);
             } catch (RemoteException e) {
                 onRemoteException(e);
             }
         }
 
-        @Override
-        public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
-                @MbmsDownloadSession.DownloadStatus int state) {
-            if (!isFilterFlagSet(STATE_UPDATES)) {
-                return;
-            }
-            try {
-                mCallback.onStateUpdated(request, fileInfo, state);
-            } catch (RemoteException e) {
-                onRemoteException(e);
-            }
-        }
-
         protected abstract void onRemoteException(RemoteException e);
     }
 
@@ -125,6 +130,10 @@
             @Override
             public void onError(int errorCode, String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
@@ -223,71 +232,70 @@
     }
 
     /**
-     * Registers a download state callbacks for the provided {@link DownloadRequest}.
+     * Registers a download status listener for the provided {@link DownloadRequest}.
      *
-     * This method is called by the app when it wants to request updates on the progress or
-     * status of the download.
+     * This method is called by the app when it wants to request updates on the status of
+     * the download.
      *
      * If the middleware is not aware of a download having been requested with the provided
-     *
      * {@link DownloadRequest} in the past,
      * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
      * must be returned.
      *
      * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
      *                        for which progress updates are being requested.
-     * @param callback The callback object to use.
+     * @param listener The listener object to use.
      */
-    public int registerStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int addStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
     @Override
-    public final int registerStateCallback(final DownloadRequest downloadRequest,
-            final IDownloadStateCallback callback, int flags) throws RemoteException {
+    public final int addStatusListener(final DownloadRequest downloadRequest,
+            final IDownloadStatusListener listener) throws RemoteException {
         final int uid = Binder.getCallingUid();
         if (downloadRequest == null) {
             throw new NullPointerException("Download request must not be null");
         }
-        if (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
-        DownloadStateCallback exposedCallback = new FilteredDownloadStateCallback(callback, flags) {
+        DownloadStatusListener exposedCallback = new VendorDownloadStatusListener(listener) {
             @Override
             protected void onRemoteException(RemoteException e) {
                 onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
             }
         };
 
-        int result = registerStateCallback(downloadRequest, exposedCallback);
+        int result = addStatusListener(downloadRequest, exposedCallback);
 
         if (result == MbmsErrors.SUCCESS) {
             DeathRecipient deathRecipient = new DeathRecipient() {
                 @Override
                 public void binderDied() {
                     onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
-                    mDownloadCallbackBinderMap.remove(callback.asBinder());
-                    mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                    mDownloadStatusListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
                 }
             };
-            mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
-            callback.asBinder().linkToDeath(deathRecipient, 0);
-            mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadStatusListenerBinderMap.put(listener.asBinder(), exposedCallback);
         }
 
         return result;
     }
 
     /**
-     * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+     * Un-registers a download status listener for the provided {@link DownloadRequest}.
      *
-     * This method is called by the app when it no longer wants to request updates on the
+     * This method is called by the app when it no longer wants to request status updates on the
      * download.
      *
      * If the middleware is not aware of a download having been requested with the provided
@@ -296,45 +304,157 @@
      * must be returned.
      *
      * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
-     * @param callback The callback object that
-     *                 {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+     * @param listener The callback object that
+     *                 {@link #addStatusListener(DownloadRequest, DownloadStatusListener)}
      *                 was called with.
      */
-    public int unregisterStateCallback(DownloadRequest downloadRequest,
-            DownloadStateCallback callback) throws RemoteException {
+    public int removeStatusListener(DownloadRequest downloadRequest,
+            DownloadStatusListener listener) throws RemoteException {
         return 0;
     }
 
     /**
-     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
      * @hide
      */
-    @Override
-    public final int unregisterStateCallback(
-            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+    public final int removeStatusListener(
+            final DownloadRequest downloadRequest, final IDownloadStatusListener listener)
             throws RemoteException {
         if (downloadRequest == null) {
             throw new NullPointerException("Download request must not be null");
         }
-        if (callback == null) {
+        if (listener == null) {
             throw new NullPointerException("Callback must not be null");
         }
 
         DeathRecipient deathRecipient =
-                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
         if (deathRecipient == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        callback.asBinder().unlinkToDeath(deathRecipient, 0);
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
 
-        DownloadStateCallback exposedCallback =
-                mDownloadCallbackBinderMap.remove(callback.asBinder());
+        DownloadStatusListener exposedCallback =
+                mDownloadStatusListenerBinderMap.remove(listener.asBinder());
         if (exposedCallback == null) {
-            throw new IllegalArgumentException("Unknown callback");
+            throw new IllegalArgumentException("Unknown listener");
         }
 
-        return unregisterStateCallback(downloadRequest, exposedCallback);
+        return removeStatusListener(downloadRequest, exposedCallback);
+    }
+
+    /**
+     * Registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the progress of
+     * the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param listener The listener object to use.
+     */
+    public int addProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int addProgressListener(final DownloadRequest downloadRequest,
+            final IDownloadProgressListener listener) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DownloadProgressListener exposedCallback = new VendorDownloadProgressListener(listener) {
+            @Override
+            protected void onRemoteException(RemoteException e) {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+            }
+        };
+
+        int result = addProgressListener(downloadRequest, exposedCallback);
+
+        if (result == MbmsErrors.SUCCESS) {
+            DeathRecipient deathRecipient = new DeathRecipient() {
+                @Override
+                public void binderDied() {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                    mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+                    mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+                }
+            };
+            mDownloadCallbackDeathRecipients.put(listener.asBinder(), deathRecipient);
+            listener.asBinder().linkToDeath(deathRecipient, 0);
+            mDownloadProgressListenerBinderMap.put(listener.asBinder(), exposedCallback);
+        }
+
+        return result;
+    }
+
+    /**
+     * Un-registers a download progress listener for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request progress updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param listener The callback object that
+     *                 {@link #addProgressListener(DownloadRequest, DownloadProgressListener)}
+     *                 was called with.
+     */
+    public int removeProgressListener(DownloadRequest downloadRequest,
+            DownloadProgressListener listener) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the listener AIDL from the API.
+     * @hide
+     */
+    public final int removeProgressListener(
+            final DownloadRequest downloadRequest, final IDownloadProgressListener listener)
+            throws RemoteException {
+        if (downloadRequest == null) {
+            throw new NullPointerException("Download request must not be null");
+        }
+        if (listener == null) {
+            throw new NullPointerException("Callback must not be null");
+        }
+
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(listener.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        listener.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadProgressListener exposedCallback =
+                mDownloadProgressListenerBinderMap.remove(listener.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown listener");
+        }
+
+        return removeProgressListener(downloadRequest, exposedCallback);
     }
 
     /**
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index db177c0..5ce612d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -77,6 +77,10 @@
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
@@ -173,6 +177,10 @@
             @Override
             public void onError(final int errorCode, final String message) {
                 try {
+                    if (errorCode == MbmsErrors.UNKNOWN) {
+                        throw new IllegalArgumentException(
+                                "Middleware cannot send an unknown error.");
+                    }
                     callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 303a068..0ed0820 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -152,6 +152,13 @@
     in ImsiEncryptionInfo imsiEncryptionInfo);
 
     /**
+     * Resets the Carrier Keys in the database. This involves 2 steps:
+     *  1. Delete the keys from the database.
+     *  2. Send an intent to download new Certificates.
+     */
+    void resetCarrierKeysForImsiEncryption(int subId, String callingPackage);
+
+    /**
      * Retrieves the alpha identifier associated with the voice mail number.
      */
     String getVoiceMailAlphaTag(String callingPackage);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a941a56..1ace032 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -623,12 +623,6 @@
     void setCellInfoListRate(int rateInMillis);
 
     /**
-     * get default sim
-     * @return sim id
-     */
-    int getDefaultSim();
-
-    /**
      * Opens a logical channel to the ICC card.
      *
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
@@ -823,10 +817,10 @@
     IImsConfig getImsConfig(int slotId, int feature);
 
     /**
-    * Returns true if emergency calling is available for the MMTEL feature associated with the
-    * slot specified.
-    */
-    boolean isEmergencyMmTelAvailable(int slotId);
+     * @return true if the IMS resolver is busy resolving a binding and should not be considered
+     * available, false if the IMS resolver is idle.
+     */
+    boolean isResolvingImsBinding();
 
     /**
      * Set the network selection mode to automatic.
@@ -1358,10 +1352,10 @@
 
     /**
      * Returns carrier name of the given subscription.
-     * <p>Carrier name is a user-facing name of carrier id {@link #getSubscriptionCarrierId(int)},
+     * <p>Carrier name is a user-facing name of carrier id {@link #getSimCarrierId(int)},
      * usually the brand name of the subsidiary (e.g. T-Mobile). Each carrier could configure
      * multiple {@link #getSimOperatorName() SPN} but should have a single carrier name.
-     * Carrier name is not canonical identity, use {@link #getSubscriptionCarrierId(int)} instead.
+     * Carrier name is not canonical identity, use {@link #getSimCarrierId(int)} instead.
      * <p>Returned carrier name is unlocalized.
      *
      * @return Carrier name of given subscription id. return {@code null} if subscription is
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ee7084a..d25fd3f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -419,6 +419,8 @@
     int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
     int RIL_REQUEST_START_KEEPALIVE = 146;
     int RIL_REQUEST_STOP_KEEPALIVE = 147;
+    int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
+    int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
 
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
 
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index f29d993c..51369d0 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -486,4 +486,10 @@
     */
     public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE =
             "com.android.omadm.service.CONFIGURATION_UPDATE";
+
+    /**
+     * Broadcast action to trigger the Carrier Certificate download.
+     */
+    public static final String ACTION_CARRIER_CERTIFICATE_DOWNLOAD =
+            "com.android.internal.telephony.ACTION_CARRIER_CERTIFICATE_DOWNLOAD";
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
index da8471f..a182f2b 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
@@ -26,7 +26,8 @@
 import android.telephony.TelephonyManager;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.ITelephony;
+
+import java.util.function.Supplier;
 
 /** Utility class for Telephony permission enforcement. */
 public final class TelephonyPermissions {
@@ -34,6 +35,9 @@
 
     private static final boolean DBG = false;
 
+    private static final Supplier<ITelephony> TELEPHONY_SUPPLIER = () ->
+            ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+
     private TelephonyPermissions() {}
 
     /**
@@ -41,8 +45,8 @@
      *
      * <p>This method behaves in one of the following ways:
      * <ul>
-     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
-     *       READ_PHONE_STATE runtime permission.
+     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
      *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
      *       apps which support runtime permissions, if the caller does not currently have any of
      *       these permissions.
@@ -51,20 +55,30 @@
      *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
      *       so we return false to indicate that the calling function should return dummy data.
      * </ul>
+     *
+     * <p>Note: for simplicity, this method always returns false for callers using legacy
+     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+     * devices.
+     *
+     * @param subId the subId of the relevant subscription; used to check carrier privileges. May be
+     *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} to skip this check for cases
+     *              where it isn't relevant (hidden APIs, or APIs which are otherwise okay to leave
+     *              inaccesible to carrier-privileged apps).
      */
     public static boolean checkCallingOrSelfReadPhoneState(
-            Context context, String callingPackage, String message) {
-        return checkReadPhoneState(context, Binder.getCallingPid(), Binder.getCallingUid(),
+            Context context, int subId, String callingPackage, String message) {
+        return checkReadPhoneState(context, subId, Binder.getCallingPid(), Binder.getCallingUid(),
                 callingPackage, message);
     }
 
     /**
      * Check whether the app with the given pid/uid can read phone state.
      *
-     * <p>This method behaves in one of the following ways:
+    * <p>This method behaves in one of the following ways:
      * <ul>
-     *   <li>return true: if the caller has either the READ_PRIVILEGED_PHONE_STATE permission or the
-     *       READ_PHONE_STATE runtime permission.
+     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
      *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
      *       apps which support runtime permissions, if the caller does not currently have any of
      *       these permissions.
@@ -73,9 +87,22 @@
      *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
      *       so we return false to indicate that the calling function should return dummy data.
      * </ul>
+     *
+     * <p>Note: for simplicity, this method always returns false for callers using legacy
+     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+     * devices.
      */
     public static boolean checkReadPhoneState(
-            Context context, int pid, int uid, String callingPackage, String message) {
+            Context context, int subId, int pid, int uid, String callingPackage, String message) {
+        return checkReadPhoneState(
+                context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, message);
+    }
+
+    @VisibleForTesting
+    public static boolean checkReadPhoneState(
+            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+            String callingPackage, String message) {
         try {
             context.enforcePermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid, uid, message);
@@ -83,8 +110,18 @@
             // SKIP checking for run-time permission since caller has PRIVILEGED permission
             return true;
         } catch (SecurityException privilegedPhoneStateException) {
-            context.enforcePermission(
-                    android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+            try {
+                context.enforcePermission(
+                        android.Manifest.permission.READ_PHONE_STATE, pid, uid, message);
+            } catch (SecurityException phoneStateException) {
+                // If we don't have the runtime permission, but do have carrier privileges, that
+                // suffices for reading phone state.
+                if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                    enforceCarrierPrivilege(telephonySupplier, subId, uid, message);
+                    return true;
+                }
+                throw phoneStateException;
+            }
         }
 
         // We have READ_PHONE_STATE permission, so return true as long as the AppOps bit hasn't been
@@ -101,14 +138,16 @@
      * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
      */
     public static boolean checkCallingOrSelfReadPhoneNumber(
-            Context context, String callingPackage, String message) {
+            Context context, int subId, String callingPackage, String message) {
         return checkReadPhoneNumber(
-                context, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, message);
+                context, TELEPHONY_SUPPLIER, subId, Binder.getCallingPid(), Binder.getCallingUid(),
+                callingPackage, message);
     }
 
     @VisibleForTesting
     public static boolean checkReadPhoneNumber(
-            Context context, int pid, int uid, String callingPackage, String message) {
+            Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
+            String callingPackage, String message) {
         // Default SMS app can always read it.
         AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         if (appOps.noteOp(AppOpsManager.OP_WRITE_SMS, uid, callingPackage) ==
@@ -121,7 +160,8 @@
 
         // First, check if we can read the phone state.
         try {
-            return checkReadPhoneState(context, pid, uid, callingPackage, message);
+            return checkReadPhoneState(
+                    context, telephonySupplier, subId, pid, uid, callingPackage, message);
         } catch (SecurityException readPhoneStateSecurityException) {
         }
         // Can be read with READ_SMS too.
@@ -186,16 +226,21 @@
     }
 
     private static void enforceCarrierPrivilege(int subId, int uid, String message) {
-        if (getCarrierPrivilegeStatus(subId, uid) !=
+        enforceCarrierPrivilege(TELEPHONY_SUPPLIER, subId, uid, message);
+    }
+
+    private static void enforceCarrierPrivilege(
+            Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) {
+        if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) !=
                 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
             if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
             throw new SecurityException(message);
         }
     }
 
-    private static int getCarrierPrivilegeStatus(int subId, int uid) {
-        ITelephony telephony =
-                ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
+    private static int getCarrierPrivilegeStatus(
+            Supplier<ITelephony> telephonySupplier, int subId, int uid) {
+        ITelephony telephony = telephonySupplier.get();
         try {
             if (telephony != null) {
                 return telephony.getCarrierPrivilegeStatusForUid(subId, uid);
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 14c5f4b..964a313 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -470,6 +470,9 @@
         int bearerDataLength;
         SmsEnvelope env = new SmsEnvelope();
         CdmaSmsAddress addr = new CdmaSmsAddress();
+        // We currently do not parse subaddress in PDU, but it is required when determining
+        // fingerprint (see getIncomingSmsFingerprint()).
+        CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
 
         try {
             env.messageType = dis.readInt();
@@ -520,6 +523,7 @@
         // link the filled objects to this SMS
         mOriginatingAddress = addr;
         env.origAddress = addr;
+        env.origSubaddress = subaddr;
         mEnvelope = env;
         mPdu = pdu;
 
@@ -1009,8 +1013,11 @@
         output.write(mEnvelope.teleService);
         output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
         output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
-        output.write(mEnvelope.origSubaddress.origBytes, 0,
-                mEnvelope.origSubaddress.origBytes.length);
+        // subaddress is not set when parsing some MT SMS.
+        if (mEnvelope.origSubaddress != null && mEnvelope.origSubaddress.origBytes != null) {
+            output.write(mEnvelope.origSubaddress.origBytes, 0,
+                    mEnvelope.origSubaddress.origBytes.length);
+        }
 
         return output.toByteArray();
     }
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 6bdfdc6..85e8361 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -22,8 +22,12 @@
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
+import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.Map.Entry;
 import java.util.Random;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,19 +44,29 @@
     };
 
     @Test
-    public void testDefaultTruncLen() throws Exception {
-        IpSecAlgorithm explicit =
+    public void testNoTruncLen() throws Exception {
+        Entry<String, Integer>[] authAndAeadList =
+                new Entry[] {
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                };
+
+        // Expect auth and aead algorithms to throw errors if trunclen is omitted.
+        for (Entry<String, Integer> algData : authAndAeadList) {
+            try {
                 new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256);
-        IpSecAlgorithm implicit =
-                new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
-        assertTrue(
-                "Default Truncation Length Incorrect, Explicit: "
-                        + explicit
-                        + "implicit: "
-                        + implicit,
-                IpSecAlgorithm.equals(explicit, implicit));
+                        algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8));
+                fail("Expected exception on unprovided auth trunclen");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+
+        // Ensure crypt works with no truncation length supplied.
+        new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
     @Test
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f186ee5..771faaf 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -62,7 +62,8 @@
         c.setAuthentication(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_HMAC_MD5,
-                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0},
+                        128));
         c.setAuthenticatedEncryption(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index cc3366f..9191bd3 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -37,6 +37,7 @@
 import com.android.server.IpSecService;
 
 import java.net.InetAddress;
+import java.net.Socket;
 import java.net.UnknownHostException;
 
 import org.junit.Before;
@@ -50,13 +51,18 @@
 
     private static final int TEST_UDP_ENCAP_PORT = 34567;
     private static final int DROID_SPI = 0xD1201D;
+    private static final int DUMMY_RESOURCE_ID = 0x1234;
 
     private static final InetAddress GOOGLE_DNS_4;
+    private static final String VTI_INTF_NAME = "ipsec_test";
+    private static final InetAddress VTI_LOCAL_ADDRESS;
+    private static final LinkAddress VTI_INNER_ADDRESS = new LinkAddress("10.0.1.1/24");
 
     static {
         try {
             // Google Public DNS Addresses;
             GOOGLE_DNS_4 = InetAddress.getByName("8.8.8.8");
+            VTI_LOCAL_ADDRESS = InetAddress.getByName("8.8.4.4");
         } catch (UnknownHostException e) {
             throw new RuntimeException("Could not resolve DNS Addresses", e);
         }
@@ -77,9 +83,8 @@
      */
     @Test
     public void testAllocSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(DROID_SPI),
@@ -92,14 +97,13 @@
 
         droidSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     @Test
     public void testAllocRandomSpi() throws Exception {
-        int resourceId = 1;
         IpSecSpiResponse spiResp =
-                new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
+                new IpSecSpiResponse(IpSecManager.Status.OK, DUMMY_RESOURCE_ID, DROID_SPI);
         when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
@@ -113,7 +117,7 @@
 
         randomSpi.close();
 
-        verify(mMockIpSecService).releaseSecurityParameterIndex(resourceId);
+        verify(mMockIpSecService).releaseSecurityParameterIndex(DUMMY_RESOURCE_ID);
     }
 
     /*
@@ -165,11 +169,10 @@
 
     @Test
     public void testOpenEncapsulationSocket() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
         when(mMockIpSecService.openUdpEncapsulationSocket(eq(TEST_UDP_ENCAP_PORT), anyObject()))
@@ -177,21 +180,47 @@
 
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
+    }
+
+    @Test
+    public void testApplyTransportModeTransformEnsuresSocketCreation() throws Exception {
+        Socket socket = new Socket();
+        IpSecConfig dummyConfig = new IpSecConfig();
+        IpSecTransform dummyTransform = new IpSecTransform(null, dummyConfig);
+
+        // Even if underlying SocketImpl is not initalized, this should force the init, and
+        // thereby succeed.
+        mIpSecManager.applyTransportModeTransform(
+                socket, IpSecManager.DIRECTION_IN, dummyTransform);
+
+        // Check to make sure the FileDescriptor is non-null
+        assertNotNull(socket.getFileDescriptor$());
+    }
+
+    @Test
+    public void testRemoveTransportModeTransformsForcesSocketCreation() throws Exception {
+        Socket socket = new Socket();
+
+        // Even if underlying SocketImpl is not initalized, this should force the init, and
+        // thereby succeed.
+        mIpSecManager.removeTransportModeTransforms(socket);
+
+        // Check to make sure the FileDescriptor is non-null
+        assertNotNull(socket.getFileDescriptor$());
     }
 
     @Test
     public void testOpenEncapsulationSocketOnRandomPort() throws Exception {
-        int resourceId = 1;
         IpSecUdpEncapResponse udpEncapResp =
                 new IpSecUdpEncapResponse(
                         IpSecManager.Status.OK,
-                        resourceId,
+                        DUMMY_RESOURCE_ID,
                         TEST_UDP_ENCAP_PORT,
                         Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP));
 
@@ -201,12 +230,12 @@
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket();
 
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();
 
-        verify(mMockIpSecService).closeUdpEncapsulationSocket(resourceId);
+        verify(mMockIpSecService).closeUdpEncapsulationSocket(DUMMY_RESOURCE_ID);
     }
 
     @Test
@@ -219,4 +248,45 @@
     }
 
     // TODO: add test when applicable transform builder interface is available
-}
+
+    private IpSecManager.IpSecTunnelInterface createAndValidateVti(int resourceId, String intfName)
+            throws Exception {
+        IpSecTunnelInterfaceResponse dummyResponse =
+                new IpSecTunnelInterfaceResponse(IpSecManager.Status.OK, resourceId, intfName);
+        when(mMockIpSecService.createTunnelInterface(
+                eq(VTI_LOCAL_ADDRESS.getHostAddress()), eq(GOOGLE_DNS_4.getHostAddress()),
+                anyObject(), anyObject()))
+                        .thenReturn(dummyResponse);
+
+        IpSecManager.IpSecTunnelInterface tunnelIntf = mIpSecManager.createIpSecTunnelInterface(
+                VTI_LOCAL_ADDRESS, GOOGLE_DNS_4, mock(Network.class));
+
+        assertNotNull(tunnelIntf);
+        return tunnelIntf;
+    }
+
+    @Test
+    public void testCreateVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        assertEquals(VTI_INTF_NAME, tunnelIntf.getInterfaceName());
+
+        tunnelIntf.close();
+        verify(mMockIpSecService).deleteTunnelInterface(eq(DUMMY_RESOURCE_ID));
+    }
+
+    @Test
+    public void testAddRemoveAddressesFromVti() throws Exception {
+        IpSecManager.IpSecTunnelInterface tunnelIntf =
+                createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
+
+        tunnelIntf.addAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+
+        tunnelIntf.removeAddress(VTI_INNER_ADDRESS);
+        verify(mMockIpSecService)
+                .addAddressToTunnelInterface(eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS));
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 17932a4..0696592 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -273,14 +273,28 @@
     @Test
     public void testOemPaid() {
         NetworkCapabilities nc = new NetworkCapabilities();
-        nc.maybeMarkCapabilitiesRestricted();
+        // By default OEM_PAID is neither in the unwanted or required lists and the network is not
+        // restricted.
+        assertFalse(nc.hasUnwantedCapability(NET_CAPABILITY_OEM_PAID));
         assertFalse(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
+        nc.maybeMarkCapabilitiesRestricted();
         assertTrue(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
+        // Adding OEM_PAID to capability list should make network restricted.
         nc.addCapability(NET_CAPABILITY_OEM_PAID);
+        nc.addCapability(NET_CAPABILITY_INTERNET);  // Combine with unrestricted capability.
         nc.maybeMarkCapabilitiesRestricted();
         assertTrue(nc.hasCapability(NET_CAPABILITY_OEM_PAID));
         assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+        // Now let's make request for OEM_PAID network.
+        NetworkCapabilities nr = new NetworkCapabilities();
+        nr.addCapability(NET_CAPABILITY_OEM_PAID);
+        nr.maybeMarkCapabilitiesRestricted();
+        assertTrue(nr.satisfiedByNetworkCapabilities(nc));
+
+        // Request fails for network with the default capabilities.
+        assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities()));
     }
 
     @Test
@@ -291,11 +305,12 @@
         assertTrue("Request: " + request + ", Network:" + network,
                 request.satisfiedByNetworkCapabilities(network));
 
-        // Adding capabilities that doesn't exist in the network anyway
+        // Requesting absence of capabilities that network doesn't have. Request should satisfy.
         request.addUnwantedCapability(NET_CAPABILITY_WIFI_P2P);
         request.addUnwantedCapability(NET_CAPABILITY_NOT_METERED);
         assertTrue(request.satisfiedByNetworkCapabilities(network));
-        assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P, NET_CAPABILITY_NOT_METERED},
+        assertArrayEquals(new int[] {NET_CAPABILITY_WIFI_P2P,
+                        NET_CAPABILITY_NOT_METERED},
                 request.getUnwantedCapabilities());
 
         // This is a default capability, just want to make sure its there because we use it below.
@@ -306,7 +321,6 @@
         assertTrue(request.hasUnwantedCapability(NET_CAPABILITY_NOT_RESTRICTED));
         assertFalse(request.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
 
-
         // Now this request won't be satisfied because network contains NOT_RESTRICTED.
         assertFalse(request.satisfiedByNetworkCapabilities(network));
         network.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index 035a4cd7..8f18d07 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.INTERFACES_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
@@ -31,14 +32,17 @@
 import static android.net.NetworkStats.SET_DBG_VPN_OUT;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.os.Process;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
+import android.util.ArrayMap;
 
 import com.google.android.collect.Sets;
 
@@ -641,6 +645,218 @@
                 ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0);
     }
 
+    @Test
+    public void testFilter_NoFilter() {
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                "test2", 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(entry1)
+                .addValues(entry2)
+                .addValues(entry3);
+
+        stats.filter(UID_ALL, INTERFACES_ALL, TAG_ALL);
+        assertEquals(3, stats.size());
+        assertEquals(entry1, stats.getValues(0, null));
+        assertEquals(entry2, stats.getValues(1, null));
+        assertEquals(entry3, stats.getValues(2, null));
+    }
+
+    @Test
+    public void testFilter_UidFilter() {
+        final int testUid = 10101;
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "test2", testUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                "test2", testUid, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(entry1)
+                .addValues(entry2)
+                .addValues(entry3);
+
+        stats.filter(testUid, INTERFACES_ALL, TAG_ALL);
+        assertEquals(2, stats.size());
+        assertEquals(entry2, stats.getValues(0, null));
+        assertEquals(entry3, stats.getValues(1, null));
+    }
+
+    @Test
+    public void testFilter_InterfaceFilter() {
+        final String testIf1 = "testif1";
+        final String testIf2 = "testif2";
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                testIf1, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "otherif", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                testIf1, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry4 = new NetworkStats.Entry(
+                testIf2, 10101, SET_DEFAULT, 123, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 4)
+                .addValues(entry1)
+                .addValues(entry2)
+                .addValues(entry3)
+                .addValues(entry4);
+
+        stats.filter(UID_ALL, new String[] { testIf1, testIf2 }, TAG_ALL);
+        assertEquals(3, stats.size());
+        assertEquals(entry1, stats.getValues(0, null));
+        assertEquals(entry3, stats.getValues(1, null));
+        assertEquals(entry4, stats.getValues(2, null));
+    }
+
+    @Test
+    public void testFilter_EmptyInterfaceFilter() {
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                "if1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "if2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(entry1)
+                .addValues(entry2);
+
+        stats.filter(UID_ALL, new String[] { }, TAG_ALL);
+        assertEquals(0, stats.size());
+    }
+
+    @Test
+    public void testFilter_TagFilter() {
+        final int testTag = 123;
+        final int otherTag = 456;
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                "test1", 10100, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                "test2", 10101, SET_DEFAULT, testTag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                "test2", 10101, SET_DEFAULT, otherTag, METERED_NO, ROAMING_NO,
+                DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(entry1)
+                .addValues(entry2)
+                .addValues(entry3);
+
+        stats.filter(UID_ALL, INTERFACES_ALL, testTag);
+        assertEquals(2, stats.size());
+        assertEquals(entry1, stats.getValues(0, null));
+        assertEquals(entry2, stats.getValues(1, null));
+    }
+
+    @Test
+    public void testApply464xlatAdjustments() {
+        final String v4Iface = "v4-wlan0";
+        final String baseIface = "wlan0";
+        final String otherIface = "other";
+        final int appUid = 10001;
+        final int rootUid = Process.ROOT_UID;
+        ArrayMap<String, String> stackedIface = new ArrayMap<>();
+        stackedIface.put(v4Iface, baseIface);
+
+        NetworkStats.Entry otherEntry = new NetworkStats.Entry(
+                otherIface, appUid, SET_DEFAULT, TAG_NONE,
+                2600  /* rxBytes */,
+                2 /* rxPackets */,
+                3800 /* txBytes */,
+                3 /* txPackets */,
+                0 /* operations */);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 3)
+                .addValues(v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                        30501490  /* rxBytes */,
+                        22401 /* rxPackets */,
+                        876235 /* txBytes */,
+                        13805 /* txPackets */,
+                        0 /* operations */)
+                .addValues(baseIface, rootUid, SET_DEFAULT, TAG_NONE,
+                        31113087,
+                        22588,
+                        1169942,
+                        13902,
+                        0)
+                .addValues(otherEntry);
+
+        stats.apply464xlatAdjustments(stackedIface);
+
+        assertEquals(3, stats.size());
+        assertValues(stats, 0, v4Iface, appUid, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                30949510,
+                22401,
+                1152335,
+                13805,
+                0);
+        assertValues(stats, 1, baseIface, 0, SET_DEFAULT, TAG_NONE,
+                METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO,
+                163577,
+                187,
+                17607,
+                97,
+                0);
+        assertEquals(otherEntry, stats.getValues(2, null));
+    }
+
+    @Test
+    public void testApply464xlatAdjustments_noStackedIface() {
+        NetworkStats.Entry firstEntry = new NetworkStats.Entry(
+                "if1", 10002, SET_DEFAULT, TAG_NONE,
+                2600  /* rxBytes */,
+                2 /* rxPackets */,
+                3800 /* txBytes */,
+                3 /* txPackets */,
+                0 /* operations */);
+        NetworkStats.Entry secondEntry = new NetworkStats.Entry(
+                "if2", 10002, SET_DEFAULT, TAG_NONE,
+                5000  /* rxBytes */,
+                3 /* rxPackets */,
+                6000 /* txBytes */,
+                4 /* txPackets */,
+                0 /* operations */);
+
+        NetworkStats stats = new NetworkStats(TEST_START, 2)
+                .addValues(firstEntry)
+                .addValues(secondEntry);
+
+        // Empty map: no adjustment
+        stats.apply464xlatAdjustments(new ArrayMap<>());
+
+        assertEquals(2, stats.size());
+        assertEquals(firstEntry, stats.getValues(0, null));
+        assertEquals(secondEntry, stats.getValues(1, null));
+    }
+
     private static void assertContains(NetworkStats stats,  String iface, int uid, int set,
             int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets,
             long txBytes, long txPackets, long operations) {
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 9b75a50..fef702e 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -16,6 +16,7 @@
 
 package android.net.apf;
 
+import static android.net.util.NetworkConstants.*;
 import static android.system.OsConstants.*;
 import static com.android.internal.util.BitUtils.bytesToBEInt;
 import static com.android.internal.util.BitUtils.put;
@@ -26,6 +27,7 @@
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.verify;
 
+import android.content.Context;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkUtils;
@@ -82,6 +84,7 @@
     private static final int TIMEOUT_MS = 500;
 
     @Mock IpConnectivityLog mLog;
+    @Mock Context mContext;
 
     @Before
     public void setUp() throws Exception {
@@ -633,9 +636,9 @@
         private FileDescriptor mWriteSocket;
         private final long mFixedTimeMs = SystemClock.elapsedRealtime();
 
-        public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
-                IpConnectivityLog log) throws Exception {
-            super(config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
+        public TestApfFilter(Context context, ApfConfiguration config,
+                IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception {
+            super(context, config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
         }
 
         // Pretend an RA packet has been received and show it to ApfFilter.
@@ -757,6 +760,17 @@
     private static final byte[] ANOTHER_IPV4_ADDR        = {10, 0, 0, 2};
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
+    // Helper to initialize a default apfFilter.
+    private ApfFilter setupApfFilter(IpManager.Callback ipManagerCallback, ApfConfiguration config)
+            throws Exception {
+        LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(link);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+        apfFilter.setLinkProperties(lp);
+        return apfFilter;
+    }
+
     @Test
     public void testApfFilterIPv4() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
@@ -766,7 +780,7 @@
 
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
         apfFilter.setLinkProperties(lp);
 
         byte[] program = ipManagerCallback.getApfProgram();
@@ -818,7 +832,7 @@
     public void testApfFilterIPv6() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfConfiguration config = getDefaultConfig();
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify empty IPv6 packet is passed
@@ -861,7 +875,7 @@
 
         ApfConfiguration config = getDefaultConfig();
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
         apfFilter.setLinkProperties(lp);
 
         byte[] program = ipManagerCallback.getApfProgram();
@@ -925,7 +939,7 @@
         apfFilter.shutdown();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
         apfFilter.setLinkProperties(lp);
         program = ipManagerCallback.getApfProgram();
         assertDrop(program, mcastv4packet.array());
@@ -941,16 +955,47 @@
     }
 
     @Test
+    public void testApfFilterMulticastPingWhileDozing() throws Exception {
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, getDefaultConfig());
+
+        // Construct a multicast ICMPv6 ECHO request.
+        final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+        ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+        packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+        packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6);
+        packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE);
+        put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
+
+        // Normally, we let multicast pings alone...
+        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+
+        // ...and even while dozing...
+        apfFilter.setDozeMode(true);
+        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+
+        // ...but when the multicast filter is also enabled, drop the multicast pings to save power.
+        apfFilter.setMulticastFilter(true);
+        assertDrop(ipManagerCallback.getApfProgram(), packet.array());
+
+        // However, we should still let through all other ICMPv6 types.
+        ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
+        raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
+        assertPass(ipManagerCallback.getApfProgram(), raPacket.array());
+
+        // Now wake up from doze mode to ensure that we no longer drop the packets.
+        // (The multicast filter is still enabled at this point).
+        apfFilter.setDozeMode(false);
+        assertPass(ipManagerCallback.getApfProgram(), packet.array());
+
+        apfFilter.shutdown();
+    }
+
+    @Test
     public void testApfFilter802_3() throws Exception {
         MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
-        LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
-        LinkProperties lp = new LinkProperties();
-        lp.addLinkAddress(link);
-
         ApfConfiguration config = getDefaultConfig();
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
-        apfFilter.setLinkProperties(lp);
-
+        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
@@ -970,8 +1015,7 @@
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
-        apfFilter.setLinkProperties(lp);
+        apfFilter = setupApfFilter(ipManagerCallback, config);
         program = ipManagerCallback.getApfProgram();
 
         // Verify that IEEE802.3 frame is dropped
@@ -992,18 +1036,13 @@
 
     @Test
     public void testApfFilterEthTypeBL() throws Exception {
-        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
-        LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
-        LinkProperties lp = new LinkProperties();
-        lp.addLinkAddress(link);
         final int[] emptyBlackList = {};
         final int[] ipv4BlackList = {ETH_P_IP};
         final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
 
+        MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
         ApfConfiguration config = getDefaultConfig();
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
-        apfFilter.setLinkProperties(lp);
-
+        ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
         byte[] program = ipManagerCallback.getApfProgram();
 
         // Verify empty packet of 100 zero bytes is passed
@@ -1023,8 +1062,7 @@
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ethTypeBlackList = ipv4BlackList;
-        apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
-        apfFilter.setLinkProperties(lp);
+        apfFilter = setupApfFilter(ipManagerCallback, config);
         program = ipManagerCallback.getApfProgram();
 
         // Verify that IPv4 frame will be dropped
@@ -1039,8 +1077,7 @@
         ipManagerCallback.resetApfProgramWait();
         apfFilter.shutdown();
         config.ethTypeBlackList = ipv4Ipv6BlackList;
-        apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
-        apfFilter.setLinkProperties(lp);
+        apfFilter = setupApfFilter(ipManagerCallback, config);
         program = ipManagerCallback.getApfProgram();
 
         // Verify that IPv4 frame will be dropped
@@ -1081,7 +1118,7 @@
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
 
         // Verify initially ARP request filter is off, and GARP filter is on.
         verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
@@ -1205,7 +1242,7 @@
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
         byte[] program = ipManagerCallback.getApfProgram();
 
         final int ROUTER_LIFETIME = 1000;
@@ -1351,7 +1388,7 @@
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
         for (int i = 0; i < 1000; i++) {
             byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
             r.nextBytes(packet);
@@ -1372,7 +1409,7 @@
         ApfConfiguration config = getDefaultConfig();
         config.multicastFilter = DROP_MULTICAST;
         config.ieee802_3Filter = DROP_802_3_FRAMES;
-        TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
         for (int i = 0; i < 1000; i++) {
             byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
             r.nextBytes(packet);
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java
new file mode 100644
index 0000000..8012838
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceSetTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceSetTest {
+    @Test
+    public void testNullNamesIgnored() {
+        final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
+        assertEquals(2, set.ifnames.size());
+        assertTrue(set.ifnames.contains("if1"));
+        assertTrue(set.ifnames.contains("if2"));
+    }
+
+    @Test
+    public void testToString() {
+        final InterfaceSet set = new InterfaceSet("if1", "if2");
+        final String setString = set.toString();
+        assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
+    }
+
+    @Test
+    public void testToString_Empty() {
+        final InterfaceSet set = new InterfaceSet(null, null);
+        assertEquals("[]", set.toString());
+    }
+
+    @Test
+    public void testEquals() {
+        assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
+        assertEquals(new InterfaceSet(null, null), new InterfaceSet());
+        assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
+        assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
+        assertFalse(new InterfaceSet().equals(null));
+    }
+}
diff --git a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
index b14f550..fc46b9c 100644
--- a/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -184,7 +184,7 @@
         assertStatsEntry(stats, "dummy0", 0, SET_DEFAULT, 0x0, 0L, 168L);
         assertStatsEntry(stats, "lo", 0, SET_DEFAULT, 0x0, 1288L, 1288L);
 
-        NetworkStatsFactory.noteStackedIface("v4-wlan0", null);
+        NetworkStatsFactory.clearStackedIfaces();
     }
 
     @Test
@@ -212,7 +212,7 @@
         assertStatsEntry(stats, "v4-wlan0", 10106, SET_FOREGROUND, 0x0, appRxBytesAfter, 7867488L);
         assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, rootRxBytesAfter, 647587L);
 
-        NetworkStatsFactory.noteStackedIface("v4-wlan0", null);
+        NetworkStatsFactory.clearStackedIfaces();
     }
 
     /**
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 5ea21ea..82b7bec 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,7 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -133,6 +134,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.connectivity.ConnectivityConstants;
 import com.android.server.connectivity.DefaultNetworkMetrics;
+import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.MockableSystemProperties;
 import com.android.server.connectivity.NetworkAgentInfo;
@@ -749,6 +751,7 @@
 
     // NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
     private class WrappedNetworkMonitor extends NetworkMonitor {
+        public Handler connectivityHandler;
         // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
         public int gen204ProbeResult = 500;
         public String gen204ProbeRedirectUrl = null;
@@ -758,6 +761,7 @@
                 IpConnectivityLog log) {
             super(context, handler, networkAgentInfo, defaultRequest, log,
                     NetworkMonitor.NetworkMonitorSettings.DEFAULT);
+            connectivityHandler = handler;
         }
 
         @Override
@@ -3664,18 +3668,29 @@
 
     @Test
     public void testBasicDnsConfigurationPushed() throws Exception {
+        final String IFNAME = "test_rmnet_data0";
+        final String[] EMPTY_TLS_SERVERS = new String[0];
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         waitForIdle();
         verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
-                anyInt(), any(), any(), any(), anyBoolean(), anyString());
+                anyInt(), any(), any(), any(), anyString(), eq(EMPTY_TLS_SERVERS));
 
         final LinkProperties cellLp = new LinkProperties();
-        cellLp.setInterfaceName("test_rmnet_data0");
+        cellLp.setInterfaceName(IFNAME);
+        // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
+        // "is-reachable" testing in order to not program netd with unreachable
+        // nameservers that it might try repeated to validate.
+        cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), IFNAME));
+        cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        cellLp.addRoute(
+                new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(false);
         waitForIdle();
         verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
-                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                anyString(), eq(EMPTY_TLS_SERVERS));
         // CS tells netd about the empty DNS config for this network.
         assertEmpty(mStringArrayCaptor.getValue());
         reset(mNetworkManagementService);
@@ -3684,7 +3699,8 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
         verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
-                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                anyString(), eq(EMPTY_TLS_SERVERS));
         assertEquals(1, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
         reset(mNetworkManagementService);
@@ -3693,7 +3709,26 @@
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
         verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
-                anyInt(), mStringArrayCaptor.capture(), any(), any(), anyBoolean(), anyString());
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                anyString(), eq(EMPTY_TLS_SERVERS));
+        assertEquals(2, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        reset(mNetworkManagementService);
+
+        final String TLS_SPECIFIER = "tls.example.com";
+        final String TLS_SERVER6 = "2001:db8:53::53";
+        final InetAddress[] TLS_IPS = new InetAddress[]{ InetAddress.getByName(TLS_SERVER6) };
+        final String[] TLS_SERVERS = new String[]{ TLS_SERVER6 };
+        final Handler h = mCellNetworkAgent.getWrappedNetworkMonitor().connectivityHandler;
+        h.sendMessage(h.obtainMessage(
+                NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0,
+                mCellNetworkAgent.getNetwork().netId,
+                new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
+        waitForIdle();
+        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                eq(TLS_SPECIFIER), eq(TLS_SERVERS));
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 3e1ff6d..410f754 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -17,11 +17,13 @@
 package com.android.server;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +34,9 @@
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
 import android.net.IpSecTransformResponse;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
+import android.net.Network;
 import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
@@ -56,10 +61,15 @@
 
     private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final LinkAddress mLocalInnerAddress;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
-        return Arrays.asList(new Object[][] {{"1.2.3.4", "8.8.4.4"}, {"2601::2", "2601::10"}});
+        return Arrays.asList(
+                new Object[][] {
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
+                {"2601::2", "2601::10", "2001:db8::1/64"}
+        });
     }
 
     private static final byte[] AEAD_KEY = {
@@ -86,6 +96,7 @@
     INetd mMockNetd;
     IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
     IpSecService mIpSecService;
+    Network fakeNetwork = new Network(0xAB);
 
     private static final IpSecAlgorithm AUTH_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4);
@@ -94,9 +105,11 @@
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
 
-    public IpSecServiceParameterizedTest(String sourceAddr, String destAddr) {
+    public IpSecServiceParameterizedTest(
+            String sourceAddr, String destAddr, String localInnerAddr) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
+        mLocalInnerAddress = new LinkAddress(localInnerAddr);
     }
 
     @Before
@@ -308,6 +321,30 @@
     }
 
     @Test
+    public void testReleaseOwnedSpi() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        verify(mMockNetd, times(0))
+                .ipSecDeleteSecurityAssociation(
+                        eq(createTransformResp.resourceId),
+                        anyString(),
+                        anyString(),
+                        eq(TEST_SPI),
+                        anyInt(),
+                        anyInt());
+        // quota is not released until the SPI is released by the Transform
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+    }
+
+    @Test
     public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
         addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
@@ -317,7 +354,7 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder());
         mIpSecService.deleteTransform(createTransformResp.resourceId);
 
-        verify(mMockNetd)
+        verify(mMockNetd, times(1))
                 .ipSecDeleteSecurityAssociation(
                         eq(createTransformResp.resourceId),
                         anyString(),
@@ -330,6 +367,21 @@
         IpSecService.UserRecord userRecord =
                 mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
         assertEquals(0, userRecord.mTransformQuotaTracker.mCurrent);
+        assertEquals(1, userRecord.mSpiQuotaTracker.mCurrent);
+
+        mIpSecService.releaseSecurityParameterIndex(ipSecConfig.getSpiResourceId());
+        // Verify that ipSecDeleteSa was not called when the SPI was released because the
+        // ownedByTransform property should prevent it; (note, the called count is cumulative).
+        verify(mMockNetd, times(1))
+                .ipSecDeleteSecurityAssociation(
+                        anyInt(),
+                        anyString(),
+                        anyString(),
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+        assertEquals(0, userRecord.mSpiQuotaTracker.mCurrent);
+
         try {
             userRecord.mTransformRecords.getRefcountedResourceOrThrow(
                     createTransformResp.resourceId);
@@ -406,4 +458,103 @@
 
         verify(mMockNetd).ipSecRemoveTransportModeTransform(pfd.getFileDescriptor());
     }
+
+    private IpSecTunnelInterfaceResponse createAndValidateTunnel(
+            String localAddr, String remoteAddr) {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                mIpSecService.createTunnelInterface(
+                        mSourceAddr, mDestinationAddr, fakeNetwork, new Binder());
+
+        assertNotNull(createTunnelResp);
+        assertEquals(IpSecManager.Status.OK, createTunnelResp.status);
+        return createTunnelResp;
+    }
+
+    @Test
+    public void testCreateTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        // Check that we have stored the tracking object, and retrieve it
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        assertEquals(1, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd)
+                .addVirtualTunnelInterface(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mSourceAddr),
+                        eq(mDestinationAddr),
+                        anyInt(),
+                        anyInt());
+    }
+
+    @Test
+    public void testDeleteTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+
+        mIpSecService.deleteTunnelInterface(createTunnelResp.resourceId);
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testTunnelInterfaceBinderDeath() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        IpSecService.UserRecord userRecord =
+                mIpSecService.mUserResourceTracker.getUserRecord(Os.getuid());
+        IpSecService.RefcountedResource refcountedRecord =
+                userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                        createTunnelResp.resourceId);
+
+        refcountedRecord.binderDied();
+
+        // Verify quota and RefcountedResource objects cleaned up
+        assertEquals(0, userRecord.mTunnelQuotaTracker.mCurrent);
+        verify(mMockNetd).removeVirtualTunnelInterface(eq(createTunnelResp.interfaceName));
+        try {
+            userRecord.mTunnelInterfaceRecords.getRefcountedResourceOrThrow(
+                    createTunnelResp.resourceId);
+            fail("Expected IllegalArgumentException on attempt to access deleted resource");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testAddRemoveAddressFromTunnelInterface() throws Exception {
+        IpSecTunnelInterfaceResponse createTunnelResp =
+                createAndValidateTunnel(mSourceAddr, mDestinationAddr);
+
+        mIpSecService.addAddressToTunnelInterface(createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceAddAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+
+        mIpSecService.removeAddressFromTunnelInterface(
+                createTunnelResp.resourceId, mLocalInnerAddress);
+        verify(mMockNetd)
+                .interfaceDelAddress(
+                        eq(createTunnelResp.interfaceName),
+                        eq(mLocalInnerAddress.getAddress().getHostAddress()),
+                        eq(mLocalInnerAddress.getPrefixLength()));
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
new file mode 100644
index 0000000..4a83d1b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.connectivity;
+
+import static android.Manifest.permission.CHANGE_NETWORK_STATE;
+import static android.Manifest.permission.CHANGE_WIFI_STATE;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_STACK;
+import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+import static android.content.pm.PackageManager.GET_PERMISSIONS;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PermissionMonitorTest {
+    private static final int MOCK_UID = 10001;
+    private static final String[] MOCK_PACKAGE_NAMES = new String[] { "com.foo.bar" };
+
+    @Mock private Context mContext;
+    @Mock private PackageManager mPackageManager;
+
+    private PermissionMonitor mPermissionMonitor;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getPackagesForUid(MOCK_UID)).thenReturn(MOCK_PACKAGE_NAMES);
+        mPermissionMonitor = new PermissionMonitor(mContext, null);
+    }
+
+    private void expectPermission(String[] permissions, boolean preinstalled) throws Exception {
+        final PackageInfo packageInfo = packageInfoWithPermissions(permissions, preinstalled);
+        when(mPackageManager.getPackageInfo(MOCK_PACKAGE_NAMES[0], GET_PERMISSIONS))
+                .thenReturn(packageInfo);
+    }
+
+    private PackageInfo packageInfoWithPermissions(String[] permissions, boolean preinstalled) {
+        final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.requestedPermissions = permissions;
+        packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.applicationInfo.flags = preinstalled ? FLAG_SYSTEM : 0;
+        return packageInfo;
+    }
+
+    @Test
+    public void testHasPermission() {
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = packageInfoWithPermissions(new String[] {
+                CHANGE_NETWORK_STATE, NETWORK_STACK
+            }, false);
+        assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = packageInfoWithPermissions(new String[] {
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL
+            }, false);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+    }
+
+    @Test
+    public void testIsPreinstalledSystemApp() {
+        PackageInfo app = packageInfoWithPermissions(new String[] {}, false);
+        assertFalse(mPermissionMonitor.isPreinstalledSystemApp(app));
+
+        app = packageInfoWithPermissions(new String[] {}, true);
+        assertTrue(mPermissionMonitor.isPreinstalledSystemApp(app));
+    }
+
+    @Test
+    public void testHasUseBackgroundNetworksPermission() throws Exception {
+        expectPermission(new String[] { CHANGE_NETWORK_STATE }, false);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, false);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        // TODO : make this false when b/31479477 is fixed
+        expectPermission(new String[] {}, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+        expectPermission(new String[] { CHANGE_WIFI_STATE }, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { NETWORK_STACK, CONNECTIVITY_INTERNAL }, true);
+        assertTrue(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] {}, false);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+
+        expectPermission(new String[] { CHANGE_WIFI_STATE }, false);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID));
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd4..8fb87f1 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -19,16 +19,25 @@
 import static android.hardware.usb.UsbManager.USB_CONFIGURED;
 import static android.hardware.usb.UsbManager.USB_CONNECTED;
 import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY;
+import static android.net.ConnectivityManager.EXTRA_ACTIVE_TETHER;
+import static android.net.ConnectivityManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
 import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
 import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -36,6 +45,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -45,18 +55,28 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
-import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
+import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
-import android.net.NetworkRequest;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -74,10 +94,16 @@
 import android.telephony.CarrierConfigManager;
 import android.test.mock.MockContentResolver;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadHardwareInterface;
+import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 
 import org.junit.After;
 import org.junit.Before;
@@ -86,33 +112,45 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.util.ArrayList;
 import java.util.Vector;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class TetheringTest {
+    private static final int IFINDEX_OFFSET = 100;
+
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+    private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+    private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
+    private static final String TEST_USB_IFNAME = "test_rndis0";
+    private static final String TEST_WLAN_IFNAME = "test_wlan0";
 
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
-    @Mock private ConnectivityManager mConnectivityManager;
     @Mock private INetworkManagementService mNMService;
     @Mock private INetworkStatsService mStatsService;
     @Mock private INetworkPolicyManager mPolicyManager;
     @Mock private MockableSystemProperties mSystemProperties;
     @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
     @Mock private Resources mResources;
-    @Mock private TetheringDependencies mTetheringDependencies;
     @Mock private UsbManager mUsbManager;
     @Mock private WifiManager mWifiManager;
     @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+    @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+    @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+    @Mock private INetd mNetd;
+
+    private final MockTetheringDependencies mTetheringDependencies =
+            new MockTetheringDependencies();
 
     // Like so many Android system APIs, these cannot be mocked because it is marked final.
     // We have to use the real versions.
     private final PersistableBundle mCarrierConfig = new PersistableBundle();
     private final TestLooper mLooper = new TestLooper();
-    private final String mTestIfname = "test_wlan0";
 
     private Vector<Intent> mIntents;
     private BroadcastInterceptingContext mServiceContext;
@@ -139,30 +177,141 @@
 
         @Override
         public Object getSystemService(String name) {
-            if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
             if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
             if (Context.USB_SERVICE.equals(name)) return mUsbManager;
             return super.getSystemService(name);
         }
     }
 
+    public class MockTetheringDependencies extends TetheringDependencies {
+        StateMachine upstreamNetworkMonitorMasterSM;
+        ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+        int isTetheringSupportedCalls;
+
+        public void reset() {
+            upstreamNetworkMonitorMasterSM = null;
+            ipv6CoordinatorNotifyList = null;
+            isTetheringSupportedCalls = 0;
+        }
+
+        @Override
+        public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+            return mOffloadHardwareInterface;
+        }
+
+        @Override
+        public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
+                StateMachine target, SharedLog log, int what) {
+            upstreamNetworkMonitorMasterSM = target;
+            return mUpstreamNetworkMonitor;
+        }
+
+        @Override
+        public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+                ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+            ipv6CoordinatorNotifyList = notifyList;
+            return mIPv6TetheringCoordinator;
+        }
+
+        @Override
+        public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+            return mRouterAdvertisementDaemon;
+        }
+
+        @Override
+        public INetd getNetdService() {
+            return mNetd;
+        }
+
+        @Override
+        public InterfaceParams getInterfaceParams(String ifName) {
+            final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
+                    TEST_MOBILE_IFNAME };
+            final int index = ArrayUtils.indexOf(ifaces, ifName);
+            assertTrue("Non-mocked interface: " + ifName, index >= 0);
+            return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+                    MacAddress.ALL_ZEROS_ADDRESS);
+        }
+
+        @Override
+        public boolean isTetheringSupported() {
+            isTetheringSupportedCalls++;
+            return true;
+        }
+    }
+
+    private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
+            boolean with464xlat) {
+        final NetworkInfo info = new NetworkInfo(TYPE_MOBILE, 0, null, null);
+        info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(TEST_MOBILE_IFNAME);
+
+        if (withIPv4) {
+            prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+        }
+
+        if (withIPv6) {
+            prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+            prop.addLinkAddress(
+                    new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+                            NetworkConstants.RFC7421_PREFIX_LENGTH));
+            prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+        }
+
+        if (with464xlat) {
+            final LinkProperties stackedLink = new LinkProperties();
+            stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
+            stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+
+            prop.addStackedLink(stackedLink);
+        }
+
+
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
+        return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
+    }
+
+    private static NetworkState buildMobileIPv4UpstreamState() {
+        return buildMobileUpstreamState(true, false, false);
+    }
+
+    private static NetworkState buildMobileIPv6UpstreamState() {
+        return buildMobileUpstreamState(false, true, false);
+    }
+
+    private static NetworkState buildMobileDualStackUpstreamState() {
+        return buildMobileUpstreamState(true, true, false);
+    }
+
+    private static NetworkState buildMobile464xlatUpstreamState() {
+        return buildMobileUpstreamState(false, true, true);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
                 .thenReturn(new String[0]);
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
-                .thenReturn(new String[0]);
+                .thenReturn(new String[] { "test_rndis\\d" });
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
-                .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
+                .thenReturn(new String[]{ "test_wlan\\d" });
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
                 .thenReturn(new String[0]);
         when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
                 .thenReturn(new int[0]);
         when(mNMService.listInterfaces())
-                .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
+                .thenReturn(new String[] {
+                        TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
         when(mNMService.getInterfaceConfig(anyString()))
                 .thenReturn(new InterfaceConfiguration());
+        when(mRouterAdvertisementDaemon.start())
+                .thenReturn(true);
 
         mServiceContext = new MockContext(mContext);
         mContentResolver = new MockContentResolver(mServiceContext);
@@ -175,9 +324,8 @@
             }
         };
         mServiceContext.registerReceiver(mBroadcastReceiver,
-                new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
-        when(mTetheringDependencies.getOffloadHardwareInterface(
-                any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
+                new IntentFilter(ACTION_TETHER_STATE_CHANGED));
+        mTetheringDependencies.reset();
         mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
                                    mLooper.getLooper(), mSystemProperties,
                                    mTetheringDependencies);
@@ -264,16 +412,16 @@
     }
 
     private void verifyInterfaceServingModeStarted() throws Exception {
-        verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
+        verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(1))
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).tetherInterface(mTestIfname);
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+        verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
     }
 
     private void verifyTetheringBroadcast(String ifname, String whichExtra) {
         // Verify that ifname is in the whichExtra array of the tether state changed broadcast.
         final Intent bcast = mIntents.get(0);
-        assertEquals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, bcast.getAction());
+        assertEquals(ACTION_TETHER_STATE_CHANGED, bcast.getAction());
         final ArrayList<String> ifnames = bcast.getStringArrayListExtra(whichExtra);
         assertTrue(ifnames.contains(ifname));
         mIntents.remove(bcast);
@@ -281,13 +429,11 @@
 
     public void failingLocalOnlyHotspotLegacyApBroadcast(
             boolean emulateInterfaceStatusChanged) throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
-
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // hotspot mode is to be started.
         if (emulateInterfaceStatusChanged) {
-            mTethering.interfaceStatusChanged(mTestIfname, true);
+            mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         }
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
@@ -296,30 +442,31 @@
         // then it creates a TetherInterfaceStateMachine and sends out a
         // broadcast indicating that the interface is "available".
         if (emulateInterfaceStatusChanged) {
-            verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-            verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+            assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+            verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         }
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
 
-    @Test
-    public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+    private void prepareUsbTethering(NetworkState upstreamState) {
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
 
         // Emulate pressing the USB tethering button in Settings UI.
         mTethering.startTethering(TETHERING_USB, null, false);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
 
-        // Pretend we receive a USB connected broadcast. Here we also pretend
-        // that the RNDIS function is somehow enabled, so that we see if we
-        // might trip ourselves up.
-        sendUsbBroadcast(true, false, true);
-        mLooper.dispatchAll();
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+    }
+
+    @Test
+    public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+        NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        prepareUsbTethering(upstreamState);
+
         // This should produce no activity of any kind.
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
 
         // Pretend we then receive USB configured broadcast.
@@ -328,6 +475,10 @@
         // Now we should see the start of tethering mechanics (in this case:
         // tetherMatchingInterfaces() which starts by fetching all interfaces).
         verify(mNMService, times(1)).listInterfaces();
+
+        // UpstreamNetworkMonitor should receive selected upstream
+        verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+        verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
     }
 
     @Test
@@ -342,55 +493,158 @@
 
     public void workingLocalOnlyHotspotEnrichedApBroadcast(
             boolean emulateInterfaceStatusChanged) throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
-
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // hotspot mode is to be started.
         if (emulateInterfaceStatusChanged) {
-            mTethering.interfaceStatusChanged(mTestIfname, true);
+            mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         }
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
         mLooper.dispatchAll();
 
         verifyInterfaceServingModeStarted();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
-        // UpstreamNetworkMonitor will be started, and will register two callbacks:
-        // a "listen all" and a "track default".
-        verify(mConnectivityManager, times(1)).registerNetworkCallback(
-                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
-                any(NetworkCallback.class), any(Handler.class));
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
+        verify(mUpstreamNetworkMonitor, times(1)).start();
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
-        verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyNoMoreInteractions(mConnectivityManager);
+        assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
 
         // Emulate externally-visible WifiManager effects, when hotspot mode
         // is being torn down.
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
-        mTethering.interfaceRemoved(mTestIfname);
+        mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         // TODO: Why is {g,s}etInterfaceConfig() called more than once?
-        verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+        verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, atLeastOnce())
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
-        assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
-                mTethering.getLastTetherError(mTestIfname));
+        assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+    }
+
+    /**
+     * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+     */
+    private void sendIPv6TetherUpdates(NetworkState upstreamState) {
+        // IPv6TetheringCoordinator must have been notified of downstream
+        verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
+                argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
+                eq(IControlsTethering.STATE_TETHERED));
+
+        for (TetherInterfaceStateMachine tism :
+                mTetheringDependencies.ipv6CoordinatorNotifyList) {
+            NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
+            tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+                    upstreamState.linkProperties.isIPv6Provisioned()
+                            ? ipv6OnlyState.linkProperties
+                            : null);
+        }
+        mLooper.dispatchAll();
+    }
+
+    private void runUsbTethering(NetworkState upstreamState) {
+        prepareUsbTethering(upstreamState);
+        sendUsbBroadcast(true, true, true);
+        mLooper.dispatchAll();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv4() throws Exception {
+        NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv6() throws Exception {
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_DualStack() throws Exception {
+        NetworkState upstreamState = buildMobileDualStackUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mRouterAdvertisementDaemon, times(1)).start();
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
+        NetworkState upstreamState = buildMobile464xlatUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
+        // Setup IPv6
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        // Then 464xlat comes up
+        upstreamState = buildMobile464xlatUpstreamState();
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
+
+        // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
+        mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+                Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstreamState);
+        mLooper.dispatchAll();
+
+        // Forwarding is added for 464xlat
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
+        // Forwarding was not re-added for v6 (still times(1))
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
     }
 
     @Test
@@ -406,7 +660,6 @@
     // TODO: Test with and without interfaceStatusChanged().
     @Test
     public void failingWifiTetheringLegacyApBroadcast() throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
@@ -414,19 +667,17 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
 
-        verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
-        verifyNoMoreInteractions(mConnectivityManager);
+        assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
@@ -434,7 +685,6 @@
     // TODO: Test with and without interfaceStatusChanged().
     @Test
     public void workingWifiTetheringEnrichedApBroadcast() throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
 
         // Emulate pressing the WiFi tethering button.
@@ -442,39 +692,30 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
         verifyInterfaceServingModeStarted();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
-        // UpstreamNetworkMonitor will be started, and will register two callbacks:
-        // a "listen all" and a "track default".
-        verify(mConnectivityManager, times(1)).registerNetworkCallback(
-                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
-                any(NetworkCallback.class), any(Handler.class));
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
+        verify(mUpstreamNetworkMonitor, times(1)).start();
         // In tethering mode, in the default configuration, an explicit request
         // for a mobile network is also made.
-        verify(mConnectivityManager, times(1)).requestNetwork(
-                any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
-                any(Handler.class));
+        verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
-        verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyNoMoreInteractions(mConnectivityManager);
+        assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
 
         /////
         // We do not currently emulate any upstream being found.
@@ -488,34 +729,31 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).stopSoftAp();
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
 
         // Emulate externally-visible WifiManager effects, when tethering mode
         // is being torn down.
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
-        mTethering.interfaceRemoved(mTestIfname);
+        mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         // TODO: Why is {g,s}etInterfaceConfig() called more than once?
-        verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+        verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, atLeastOnce())
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
-        assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
-                mTethering.getLastTetherError(mTestIfname));
+        assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
     }
 
     // TODO: Test with and without interfaceStatusChanged().
     @Test
     public void failureEnablingIpForwarding() throws Exception {
-        when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
         when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
         doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true);
 
@@ -524,27 +762,27 @@
         mLooper.dispatchAll();
         verify(mWifiManager, times(1)).startSoftAp(null);
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
 
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
         // We verify get/set called twice here: once for setup and once during
         // teardown because all events happen over the course of the single
         // dispatchAll() above.
-        verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
+        verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(2))
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).tetherInterface(mTestIfname);
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+        verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
-        verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
+        // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
+        assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
         // This is called, but will throw.
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         // This never gets called because of the exception thrown above.
@@ -552,12 +790,11 @@
         // When the master state machine transitions to an error state it tells
         // downstream interfaces, which causes us to tell Wi-Fi about the error
         // so it can take down AP mode.
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
 
         verifyNoMoreInteractions(mWifiManager);
-        verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
     }
 
@@ -596,7 +833,7 @@
 
     @Test
     public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final boolean currDisallow = false;
         final boolean nextDisallow = true;
         final int expectedInteractionsWithShowNotification = 1;
@@ -618,7 +855,7 @@
 
     @Test
     public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final boolean currDisallow = true;
         final boolean nextDisallow = false;
         final int expectedInteractionsWithShowNotification = 0;
@@ -629,7 +866,7 @@
 
     @Test
     public void testDisallowTetheringUnchanged() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final int expectedInteractionsWithShowNotification = 0;
         boolean currDisallow = true;
         boolean nextDisallow = true;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f59850d..e377a47 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -70,6 +70,7 @@
 import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.test.filters.SmallTest;
@@ -88,6 +89,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -639,4 +642,32 @@
         lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
         assertTrue(Vpn.providesRoutesToMostDestinations(lp));
     }
+
+    @Test
+    public void testDoesNotLockUpWithTooManyRoutes() {
+        final LinkProperties lp = new LinkProperties();
+        final byte[] ad = new byte[4];
+        // Actually evaluating this many routes under 1500ms is impossible on
+        // current hardware and for some time, as the algorithm is O(n²).
+        // Make sure the system has a safeguard against this and does not
+        // lock up.
+        final int MAX_ROUTES = 4000;
+        final long MAX_ALLOWED_TIME_MS = 1500;
+        for (int i = 0; i < MAX_ROUTES; ++i) {
+            ad[0] = (byte)((i >> 24) & 0xFF);
+            ad[1] = (byte)((i >> 16) & 0xFF);
+            ad[2] = (byte)((i >> 8) & 0xFF);
+            ad[3] = (byte)(i & 0xFF);
+            try {
+                lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32)));
+            } catch (UnknownHostException e) {
+                // UnknownHostException is only thrown for an address of illegal length,
+                // which can't happen in the case above.
+            }
+        }
+        final long start = SystemClock.currentThreadTimeMillis();
+        assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+        final long end = SystemClock.currentThreadTimeMillis();
+        assertTrue(end - start < MAX_ALLOWED_TIME_MS);
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..7c77cf5 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -31,7 +31,6 @@
 import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
@@ -39,12 +38,12 @@
 import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
 import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
 
-import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
@@ -75,6 +74,7 @@
     @Mock private IControlsTethering mTetherHelper;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private SharedLog mSharedLog;
+    @Mock private TetheringDependencies mTetheringDependencies;
 
     private final TestLooper mLooper = new TestLooper();
     private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -84,7 +84,7 @@
     private void initStateMachine(int interfaceType) throws Exception {
         mTestedSm = new TetherInterfaceStateMachine(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
-                mNMService, mStatsService, mTetherHelper);
+                mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
         mTestedSm.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
@@ -111,7 +111,8 @@
     @Test
     public void startsOutAvailable() {
         mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
-                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper);
+                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
+                mTetheringDependencies);
         mTestedSm.start();
         mLooper.dispatchAll();
         verify(mTetherHelper).updateInterfaceState(
@@ -346,7 +347,7 @@
      * Send a command to the state machine under test, and run the event loop to idle.
      *
      * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
-     * @param obj An additional argument to pass.
+     * @param arg1 An additional argument to pass.
      */
     private void dispatchCommand(int command, int arg1) {
         mTestedSm.sendMessage(command, arg1);
@@ -371,7 +372,7 @@
      */
     private void dispatchTetherConnectionChanged(String upstreamIface) {
         mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                upstreamIface);
+                new InterfaceSet(upstreamIface));
         mLooper.dispatchAll();
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c3b9def..9661dc2 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -147,6 +147,16 @@
     }
 
     @Test
+    public void testCallbacksRegistered() {
+        mUNM.start();
+        verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
+        verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+
+        mUNM.stop();
+        verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+    }
+
+    @Test
     public void testRequestsMobileNetwork() throws Exception {
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 47c3455..17ca651 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -25,6 +25,7 @@
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
 import static android.net.NetworkStats.IFACE_ALL;
+import static android.net.NetworkStats.INTERFACES_ALL;
 import static android.net.NetworkStats.METERED_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
@@ -58,6 +59,9 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -95,6 +99,7 @@
 import android.util.TrustedTime;
 
 import com.android.internal.net.VpnInfo;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
 import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
@@ -668,6 +673,94 @@
     }
 
     @Test
+    public void testDetailedUidStats() throws Exception {
+        // pretend that network comes online
+        expectDefaultSettings();
+        expectNetworkState(buildWifiState());
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectBandwidthControlCheck();
+
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
+
+        NetworkStats.Entry entry1 = new NetworkStats.Entry(
+                TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L);
+        NetworkStats.Entry entry2 = new NetworkStats.Entry(
+                TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 50L, 5L, 50L, 5L, 0L);
+        NetworkStats.Entry entry3 = new NetworkStats.Entry(
+                TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xBEEF, 1024L, 8L, 512L, 4L, 0L);
+
+        incrementCurrentTime(HOUR_IN_MILLIS);
+        expectDefaultSettings();
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+                .addValues(entry1)
+                .addValues(entry2)
+                .addValues(entry3));
+        mService.incrementOperationCount(UID_RED, 0xF00D, 1);
+
+        NetworkStats stats = mService.getDetailedUidStats(INTERFACES_ALL);
+
+        assertEquals(3, stats.size());
+        entry1.operations = 1;
+        assertEquals(entry1, stats.getValues(0, null));
+        entry2.operations = 1;
+        assertEquals(entry2, stats.getValues(1, null));
+        assertEquals(entry3, stats.getValues(2, null));
+    }
+
+    @Test
+    public void testDetailedUidStats_Filtered() throws Exception {
+        // pretend that network comes online
+        expectDefaultSettings();
+
+        final String stackedIface = "stacked-test0";
+        final LinkProperties stackedProp = new LinkProperties();
+        stackedProp.setInterfaceName(stackedIface);
+        final NetworkState wifiState = buildWifiState();
+        wifiState.linkProperties.addStackedLink(stackedProp);
+        expectNetworkState(wifiState);
+
+        expectNetworkStatsSummary(buildEmptyStats());
+        expectNetworkStatsUidDetail(buildEmptyStats());
+        expectBandwidthControlCheck();
+
+        mService.forceUpdateIfaces(NETWORKS_WIFI);
+
+        NetworkStats.Entry uidStats = new NetworkStats.Entry(
+                TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
+        // Stacked on matching interface
+        NetworkStats.Entry tetheredStats1 = new NetworkStats.Entry(
+                stackedIface, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
+        // Different interface
+        NetworkStats.Entry tetheredStats2 = new NetworkStats.Entry(
+                "otherif", UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L);
+
+        final String[] ifaceFilter = new String[] { TEST_IFACE };
+        incrementCurrentTime(HOUR_IN_MILLIS);
+        expectDefaultSettings();
+        expectNetworkStatsSummary(buildEmptyStats());
+        when(mNetManager.getNetworkStatsUidDetail(eq(UID_ALL), any()))
+                .thenReturn(new NetworkStats(getElapsedRealtime(), 1)
+                        .addValues(uidStats));
+        when(mNetManager.getNetworkStatsTethering(STATS_PER_UID))
+                .thenReturn(new NetworkStats(getElapsedRealtime(), 2)
+                        .addValues(tetheredStats1)
+                        .addValues(tetheredStats2));
+
+        NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
+
+        verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces ->
+                ifaces != null && ifaces.length == 2
+                        && ArrayUtils.contains(ifaces, TEST_IFACE)
+                        && ArrayUtils.contains(ifaces, stackedIface)));
+
+        assertEquals(2, stats.size());
+        assertEquals(uidStats, stats.getValues(0, null));
+        assertEquals(tetheredStats1, stats.getValues(1, null));
+    }
+
+    @Test
     public void testForegroundBackground() throws Exception {
         // pretend that network comes online
         expectCurrentTime();
@@ -1056,7 +1149,7 @@
 
     private void expectNetworkStatsUidDetail(NetworkStats detail, NetworkStats tetherStats)
             throws Exception {
-        when(mNetManager.getNetworkStatsUidDetail(UID_ALL)).thenReturn(detail);
+        when(mNetManager.getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL)).thenReturn(detail);
 
         // also include tethering details, since they are folded into UID
         when(mNetManager.getNetworkStatsTethering(STATS_PER_UID)).thenReturn(tetherStats);
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index d415d22..152e6c3 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -34,6 +34,8 @@
             env->GetArrayLength(program),
             (uint8_t*)env->GetByteArrayElements(packet, NULL),
             env->GetArrayLength(packet),
+            nullptr,
+            0,
             filter_age);
 }
 
@@ -143,7 +145,8 @@
         do {
             apf_packet = pcap_next(apf_pcap.get(), &apf_header);
         } while (apf_packet != NULL && !accept_packet(
-                apf_program, apf_program_len, apf_packet, apf_header.len, 0));
+                apf_program, apf_program_len, apf_packet, apf_header.len,
+                nullptr, 0, 0));
 
         // Make sure both filters matched the same packet.
         if (apf_packet == NULL && bpf_packet == NULL)
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
index 9223931..a502a9d 100644
--- a/tools/bit/util.cpp
+++ b/tools/bit/util.cpp
@@ -241,6 +241,8 @@
 
     char* buf = (char*)malloc(size);
     if ((size_t) size != fread(buf, 1, size, file)) {
+        free(buf);
+        fclose(file);
         return string();
     }
 
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index c6ad4c2..dcb90e4 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -13,6 +13,7 @@
 
 LANG_TO_SCRIPT = {
     'as': 'Beng',
+    'be': 'Cyrl',
     'bg': 'Cyrl',
     'bn': 'Beng',
     'cu': 'Cyrl',
@@ -33,6 +34,7 @@
     'ja': 'Jpan',
     'kn': 'Knda',
     'ko': 'Kore',
+    'la': 'Latn',
     'ml': 'Mlym',
     'mn': 'Cyrl',
     'mr': 'Deva',