Merge "Clean new TelephonyManager API."
diff --git a/Android.bp b/Android.bp
index baf8e4f..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",
diff --git a/api/current.txt b/api/current.txt
index 308afda..4c6e621 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37037,10 +37037,8 @@
     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.SecureElementListener {
+    method public abstract void onServiceConnected();
   }
 
   public class Session {
@@ -37050,9 +37048,7 @@
     method public android.se.omapi.Reader getReader();
     method public boolean isClosed();
     method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
     method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
   }
 
 }
@@ -40396,6 +40392,8 @@
   }
 
   public class MbmsDownloadSession implements java.lang.AutoCloseable {
+    method public int addProgressListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadProgressListener);
+    method public int addStatusListener(android.telephony.mbms.DownloadRequest, java.util.concurrent.Executor, android.telephony.mbms.DownloadStatusListener);
     method public int 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);
@@ -40403,12 +40401,12 @@
     method public int 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 int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener);
+    method public int 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";
@@ -41063,21 +41061,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);
@@ -41086,46 +41084,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);
   }
 
@@ -41261,6 +41258,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();
@@ -41284,15 +41286,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 {
diff --git a/api/system-current.txt b/api/system-current.txt
index 6325de7..31a6d4d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5347,18 +5347,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 05f5c77..ce6810f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -470,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/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/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 4157845..dc3c765 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -305,6 +305,19 @@
      * 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 in the outbound direction, the previous transform
@@ -373,6 +386,19 @@
      * 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 in the outbound direction, the previous transform
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/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/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/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 65ce67f..c8efede 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -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");
         }
@@ -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..9be3da6 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -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..311dc4c 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -62,17 +62,32 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+    public interface SecureElementListener {
+        /**
+         * Called by the framework when the service is connected.
+         */
+        void onServiceConnected();
+    }
+
+    /**
+     * 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 SecureElementListener mListener = null;
+
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        /**
-         * Called by the framework when the service is connected.
-         */
-        public void serviceConnected() {};
+        public void onServiceConnected() {
+            if (mListener != null) {
+                mListener.onServiceConnected();
+            }
+        }
     }
+    private SEListener mSEListener = new SEListener();
 
     private static final String TAG = "OMAPI.SEService";
 
@@ -95,34 +110,28 @@
     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.
+     * onServiceConnected() 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 SecureElementListener object.
      */
-    public SEService(Context context, SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
 
         if (context == null) {
             throw new NullPointerException("context must not be null");
         }
 
         mContext = context;
-        mSEListener = listener;
+        mSEListener.mListener = listener;
 
         mConnection = new ServiceConnection() {
 
@@ -131,9 +140,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    try {
-                        mSEListener.serviceConnected();
-                    } catch (RemoteException ignore) { }
+                    mSEListener.onServiceConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -233,7 +240,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 3d8b74b..adfeddd 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -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");
         }
@@ -223,32 +225,6 @@
     }
 
     /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openBasicChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array, or null if no Applet is to be selected.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element session is used after
-     *             being closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
-     *             selected.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device
-     * @return an instance of Channel if available or null.
-     */
-    public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
-        return openBasicChannel(aid, (byte) 0x00);
-    }
-
-    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -300,7 +276,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 {
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -327,32 +304,4 @@
             }
         }
     }
-
-    /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element is used after being
-     *             closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not
-     *             available or cannot be selected or a logical channel is already
-     *             open to a non-multiselectable Applet.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device.
-     * @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 {
-        return openLogicalChannel(aid, (byte) 0x00);
-    }
 }
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/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b13d0b..20a5afe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -178,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/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 4db0034..7828c4c 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -42,6 +42,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;
@@ -147,6 +148,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);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 428f0b8..b83b8e1 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] -->
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/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4f24d9f..6b4cd4b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2696,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);
@@ -2718,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() {
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index cffa834..9581308 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -181,6 +181,7 @@
     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;
@@ -202,12 +203,13 @@
         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();
@@ -215,8 +217,8 @@
                 deps.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();
@@ -1240,7 +1242,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 +1262,7 @@
             addState(mSetDnsForwardersErrorState);
 
             mNotifyList = new ArrayList<>();
-            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+            mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
             mOffload = new OffloadWrapper();
 
             setInitialState(mInitialState);
@@ -1996,7 +1998,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/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..e4c7ca0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -117,6 +117,8 @@
     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
@@ -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;
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..66afb0f 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,26 @@
     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();
+    }
 }
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/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 81a966b..cdab521 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -31,11 +31,13 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.mbms.DownloadStatusListener;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.InternalDownloadProgressListener;
 import android.telephony.mbms.InternalDownloadSessionCallback;
-import android.telephony.mbms.InternalDownloadStateCallback;
+import android.telephony.mbms.InternalDownloadStatusListener;
 import android.telephony.mbms.MbmsDownloadSessionCallback;
 import android.telephony.mbms.MbmsDownloadReceiver;
 import android.telephony.mbms.MbmsErrors;
@@ -240,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) {
@@ -569,34 +573,33 @@
     }
 
     /**
-     * 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}.
      *
      * @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.
+     * @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.
      * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
      * and some other error code otherwise.
      */
-    public int registerStateCallback(@NonNull DownloadRequest request,
-            @NonNull Executor executor, @NonNull DownloadStateCallback callback) {
+    public int 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.SUCCESS) {
                 if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                     throw new IllegalArgumentException("Unknown download request.");
@@ -608,40 +611,41 @@
             sIsInitialized.set(false);
             return MbmsErrors.ERROR_MIDDLEWARE_LOST;
         }
-        mInternalDownloadCallbacks.put(callback, internalCallback);
+        mInternalDownloadStatusListeners.put(listener, internalListener);
         return MbmsErrors.SUCCESS;
+
     }
 
     /**
-     * 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}.
      *
      * @param request The {@link DownloadRequest} provided during registration
-     * @param callback The callback provided during registration.
+     * @param listener The listener provided during registration.
      * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
      * and some other error code otherwise.
      */
-    public int unregisterStateCallback(@NonNull DownloadRequest request,
-            @NonNull DownloadStateCallback callback) {
+    public int 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.SUCCESS) {
                     if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
                         throw new IllegalArgumentException("Unknown download request.");
@@ -654,8 +658,102 @@
                 return MbmsErrors.ERROR_MIDDLEWARE_LOST;
             }
         } 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}.
+     *
+     * @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.
+     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+     * and some other error code otherwise.
+     */
+    public int 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.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                return result;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sIsInitialized.set(false);
+            return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+        }
+        mInternalDownloadProgressListeners.put(listener, internalListener);
+        return MbmsErrors.SUCCESS;
+    }
+
+    /**
+     * 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}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param listener The listener provided during registration.
+     * @return {@link MbmsErrors#SUCCESS} if the operation did not encounter a synchronous error,
+     * and some other error code otherwise.
+     */
+    public int 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.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    return result;
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sIsInitialized.set(false);
+                return MbmsErrors.ERROR_MIDDLEWARE_LOST;
+            }
+        } finally {
+            InternalDownloadProgressListener internalCallback =
+                    mInternalDownloadProgressListeners.remove(listener);
             if (internalCallback != null) {
                 internalCallback.stop();
             }
@@ -700,9 +798,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
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/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f92f191..caf08e9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7668,11 +7668,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{}
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/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/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/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..f9d7161 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);
     }
 
@@ -223,71 +228,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 +300,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/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/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 099cfd4..c727bd2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,8 +27,12 @@
 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.assertNotNull;
 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 +40,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 +50,29 @@
 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 +90,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,13 +108,21 @@
 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;
@@ -103,16 +133,21 @@
     @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;
@@ -146,23 +181,104 @@
         }
     }
 
+    public class MockTetheringDependencies extends TetheringDependencies {
+        private StateMachine upstreamNetworkMonitorMasterSM;
+        private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+
+        @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);
+        }
+    }
+
+    private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6) {
+        final NetworkInfo info = new NetworkInfo(ConnectivityManager.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));
+        }
+
+
+        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);
+    }
+
+    private static NetworkState buildMobileDualStackUpstreamState() {
+        return buildMobileUpstreamState(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);
@@ -176,8 +292,6 @@
         };
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
-        when(mTetheringDependencies.getOffloadHardwareInterface(
-                any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
         mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
                                    mLooper.getLooper(), mSystemProperties,
                                    mTetheringDependencies);
@@ -264,10 +378,10 @@
     }
 
     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) {
@@ -287,7 +401,7 @@
         // 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();
@@ -297,27 +411,31 @@
         // broadcast indicating that the interface is "available".
         if (emulateInterfaceStatusChanged) {
             verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-            verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+            verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         }
         verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
 
-    @Test
-    public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+    private void prepareUsbTethering(NetworkState upstreamState) {
         when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+        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);
@@ -328,6 +446,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
@@ -348,26 +470,21 @@
         // 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, ConnectivityManager.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, ConnectivityManager.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);
@@ -375,14 +492,14 @@
         // 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);
@@ -390,9 +507,62 @@
         // 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));
+                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);
+            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_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 workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception {
         workingLocalOnlyHotspotEnrichedApBroadcast(true);
@@ -420,12 +590,12 @@
         // 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);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
@@ -448,30 +618,23 @@
         // 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, ConnectivityManager.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, ConnectivityManager.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);
@@ -494,14 +657,14 @@
         // 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);
@@ -509,7 +672,7 @@
         // 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));
+                mTethering.getLastTetherError(TEST_WLAN_IFNAME));
     }
 
     // TODO: Test with and without interfaceStatusChanged().
@@ -530,21 +693,21 @@
         // 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);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.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,9 +715,9 @@
         // 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);
@@ -596,7 +759,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 +781,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 +792,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/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..5f3fc54 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,7 +38,6 @@
 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;
@@ -75,6 +73,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 +83,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 +110,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 +346,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);
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());