Merge "Migrate primary external storage."
diff --git a/api/current.txt b/api/current.txt
index 8216304..578f808 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18299,17 +18299,18 @@
     method public boolean bindProcessToNetwork(android.net.Network);
     method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
@@ -18317,6 +18318,7 @@
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
@@ -18324,8 +18326,10 @@
     method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -19286,8 +19290,6 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
-    field public int apBand;
-    field public int apChannel;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
     field public boolean hiddenSSID;
     field public int networkId;
@@ -19356,6 +19358,7 @@
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
+    method public java.lang.String getDomainSubjectMatch();
     method public int getEapMethod();
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
@@ -19367,6 +19370,7 @@
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+    method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
@@ -30218,6 +30222,7 @@
     method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method public android.telecom.Connection getPrimaryConnection();
     method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
     method public void onAudioStateChanged(android.telecom.AudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -30236,6 +30241,7 @@
     method public final void setConnectionCapabilities(int);
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
+    method public final void setStatusHints(android.telecom.StatusHints);
     field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
@@ -30426,6 +30432,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void unregisterCallback();
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
diff --git a/api/system-current.txt b/api/system-current.txt
index 09d32da..2ea68c6 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19758,17 +19758,18 @@
     method public boolean bindProcessToNetwork(android.net.Network);
     method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
     method public android.net.Network getBoundNetworkForProcess();
     method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
@@ -19776,6 +19777,7 @@
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
@@ -19783,8 +19785,10 @@
     method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -20985,8 +20989,6 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
-    field public int apBand;
-    field public int apChannel;
     field public java.lang.String creatorName;
     field public int creatorUid;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
@@ -21077,6 +21079,7 @@
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
+    method public java.lang.String getDomainSubjectMatch();
     method public int getEapMethod();
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
@@ -21088,6 +21091,7 @@
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
+    method public void setDomainSuffixMatch(java.lang.String);
     method public void setEapMethod(int);
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
@@ -32329,6 +32333,7 @@
     method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
     method public android.telecom.Connection getPrimaryConnection();
     method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
     method public void onAudioStateChanged(android.telecom.AudioState);
     method public void onConnectionAdded(android.telecom.Connection);
     method public void onDisconnect();
@@ -32347,6 +32352,7 @@
     method public final void setConnectionCapabilities(int);
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
+    method public final void setStatusHints(android.telecom.StatusHints);
     field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
@@ -32540,6 +32546,7 @@
   public static abstract class InCallService.VideoCall {
     ctor public InCallService.VideoCall();
     method public abstract void registerCallback(android.telecom.InCallService.VideoCall.Callback);
+    method public abstract void unregisterCallback();
     method public abstract void requestCallDataUsage();
     method public abstract void requestCameraCapabilities();
     method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 79e560f..d4e79be 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -121,6 +121,9 @@
      * {@link #STATE_TURNING_ON},
      * {@link #STATE_ON},
      * {@link #STATE_TURNING_OFF},
+     * {@link #STATE_BLE_TURNING_ON},
+     * {@link #STATE_BLE_ON},
+     * {@link #STATE_BLE_TURNING_OFF},
      */
     public static final String EXTRA_STATE =
             "android.bluetooth.adapter.extra.STATE";
@@ -130,7 +133,7 @@
      * {@link #STATE_OFF},
      * {@link #STATE_TURNING_ON},
      * {@link #STATE_ON},
-     * {@link #STATE_TURNING_OFF},
+     * {@link #STATE_TURNING_OFF}
      */
     public static final String EXTRA_PREVIOUS_STATE =
             "android.bluetooth.adapter.extra.PREVIOUS_STATE";
@@ -1301,9 +1304,12 @@
     public boolean isHardwareTrackingFiltersAvailable() {
         if (getState() != STATE_ON) return false;
         try {
-            synchronized(mManagerCallback) {
-                if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0);
+            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
+            if (iGatt == null) {
+                // BLE is not supported
+                return false;
             }
+            return (iGatt.numHwTrackFiltersAvailable() != 0);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
         }
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
index cdb24f4..01778b3 100644
--- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
+++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java
@@ -123,4 +123,7 @@
     public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException {
     }
 
+    @Override
+    public void onScanManagerErrorCallback(int errorCode) throws RemoteException {
+    }
 }
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index f6001bf..a3eceb5 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -100,7 +100,6 @@
     boolean isActivityAndEnergyReportingSupported();
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
-    int numOfHwTrackFiltersAvailable();
 
     // for dumpsys support
     String dump();
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 4ca57f8..72abeaf 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -103,4 +103,5 @@
                             in boolean confirm, in byte[] value);
     void disconnectAll();
     void unregAll();
+    int numHwTrackFiltersAvailable();
 }
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index 91e62ea..cbba9f0 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -67,6 +67,7 @@
     void onReadRemoteRssi(in String address, in int rssi, in int status);
     void onMultiAdvertiseCallback(in int status, boolean isStart,
                                   in AdvertiseSettings advertiseSettings);
+    void onScanManagerErrorCallback(in int errorCode);
     void onConfigureMTU(in String address, in int mtu, in int status);
     void onFoundOrLost(in boolean onFound, in ScanResult scanResult);
 }
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 3078951..687bd5d 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -381,6 +381,18 @@
                 }
             });
         }
+
+        @Override
+        public void onScanManagerErrorCallback(final int errorCode) {
+            if (VDBG) {
+                Log.d(TAG, "onScanManagerErrorCallback() - errorCode = " + errorCode);
+            }
+            synchronized (this) {
+                if (mClientIf <= 0)
+                    return;
+            }
+            postCallbackError(mScanCallback, errorCode);
+        }
     }
 
     private void postCallbackError(final ScanCallback callback, final int errorCode) {
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 691798f..a4d6be09 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -326,6 +326,9 @@
         }
 
         try {
+            startPreview(); // If preview is not running (i.e. after a JPEG capture), we need to
+                            // explicitely start and stop preview before setting preview surface.
+                            // null.
             stopPreview();
         }  catch (RuntimeException e) {
             Log.e(TAG, "Received device exception in configure call: ", e);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 63f48cf..c531e7e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -98,13 +98,41 @@
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
+     * The device has connected to a network that has presented a captive
+     * portal, which is blocking Internet connectivity. The user was presented
+     * with a notification that network sign in is required,
+     * and the user invoked the notification's action indicating they
+     * desire to sign in to the network. Apps handling this action should
+     * facilitate signing in to the network. This action includes a
+     * {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents
+     * the network presenting the captive portal; all communication with the
+     * captive portal must be done using this {@code Network} object.
+     * <p/>
+     * When the app handling this action believes the user has signed in to
+     * the network and the captive portal has been dismissed, the app should call
+     * {@link #reportCaptivePortalDismissed} so the system can reevaluate the network.
+     * If reevaluation finds the network no longer subject to a captive portal,
+     * the network may become the default active data network.
+     * <p/>
+     * When the app handling this action believes the user explicitly wants
+     * to ignore the captive portal and the network, the app should call
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     * <p/>
+     * Note that this action includes a {@code String} extra named
+     * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} that must
+     * be passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+
+    /**
      * The lookup key for a {@link NetworkInfo} object. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      *
      * @deprecated Since {@link NetworkInfo} can vary based on UID, applications
      *             should always obtain network information through
-     *             {@link #getActiveNetworkInfo()} or
-     *             {@link #getAllNetworkInfo()}.
+     *             {@link #getActiveNetworkInfo()}.
      * @see #EXTRA_NETWORK_TYPE
      */
     @Deprecated
@@ -112,8 +140,6 @@
 
     /**
      * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
-     * Can be used with {@link #getNetworkInfo(int)} to get {@link NetworkInfo}
-     * state based on the calling application.
      *
      * @see android.content.Intent#getIntExtra(String, int)
      */
@@ -163,6 +189,15 @@
     public static final String EXTRA_INET_CONDITION = "inetCondition";
 
     /**
+     * The lookup key for a string that is sent out with
+     * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}. This string must be
+     * passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}. Retrieve it with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
+
+    /**
      * Broadcast action to indicate the change of data activity status
      * (idle or active) on a network in a recent period.
      * The network becomes active when data transmission is started, or
@@ -660,6 +695,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
@@ -699,6 +738,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo[] getAllNetworkInfo() {
         try {
@@ -716,6 +759,9 @@
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @hide
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public Network getNetworkForType(int networkType) {
         try {
@@ -808,6 +854,10 @@
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks},
+     *             {@link #getNetworkInfo(android.net.Network)}, and
+     *             {@link #getLinkProperties(android.net.Network)} instead.
      */
     public LinkProperties getLinkProperties(int networkType) {
         try {
@@ -1748,6 +1798,82 @@
         }
     }
 
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_DISMISSED    = 0;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED     = 1;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate to the system that the captive portal has been
+     * dismissed.  In response the framework will re-evaluate the network's
+     * connectivity and might take further action thereafter.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void reportCaptivePortalDismissed(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate that the user does not want to pursue signing in to
+     * captive portal and the system should continue to prefer other networks
+     * without captive portals for use as the default active data network.  The
+     * system will not retest the network for a captive portal so as to avoid
+     * disturbing the user with further sign in to network notifications.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void ignoreNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate the user wants to use this network as is, even though
+     * the captive portal is still in place.  The system will treat the network
+     * as if it did not have a captive portal when selecting the network to use
+     * as the default active data network. This may result in this network
+     * becoming the default active data network, which could disrupt network
+     * connectivity for apps because the captive portal is still in place.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @hide
+     */
+    public void useNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
     /**
      * Set a network-independent global http proxy.  This is not normally what you want
      * for typical HTTP proxies - they are general network dependent.  However if you're
@@ -1941,6 +2067,7 @@
      * @param networkType
      *
      * {@hide}
+     * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index efc76b3..77200a5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -98,6 +98,8 @@
 
     void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
 
+    void captivePortalAppResponse(in Network network, int response, String actionToken);
+
     ProxyInfo getGlobalProxy();
 
     void setGlobalProxy(in ProxyInfo p);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f430eb5..018c1a1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -212,6 +212,7 @@
     <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
     <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
 
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     <!-- @deprecated.  Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. -->
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 2ec15be..aea8585 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
 
     <application android:label="@string/app_name" >
         <activity
@@ -28,9 +29,8 @@
             android:label="@string/action_bar_label"
             android:theme="@style/AppTheme" >
             <intent-filter>
-                <action android:name="android.intent.action.ACTION_SEND"/>
+                <action android:name="android.net.conn.CAPTIVE_PORTAL"/>
                 <category android:name="android.intent.category.DEFAULT"/>
-                <data android:mimeType="text/plain"/>
             </intent-filter>
         </activity>
     </application>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index e4054ac..b86fc4b 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -56,24 +56,13 @@
     private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
-    // Keep this in sync with NetworkMonitor.
-    // Intent broadcast to ConnectivityService indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
-    private static final String RESPONSE_TOKEN = "response_token";
+    private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
 
     private URL mURL;
-    private int mNetId;
+    private Network mNetwork;
     private String mResponseToken;
     private NetworkCallback mNetworkCallback;
+    private ConnectivityManager mCm;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -81,23 +70,19 @@
 
         String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
         if (server == null) server = DEFAULT_SERVER;
+        mCm = ConnectivityManager.from(this);
         try {
             mURL = new URL("http", server, "/generate_204");
-            final Uri dataUri = getIntent().getData();
-            if (!dataUri.getScheme().equals("netid")) {
-                throw new MalformedURLException();
-            }
-            mNetId = Integer.parseInt(dataUri.getSchemeSpecificPart());
-            mResponseToken = dataUri.getFragment();
-        } catch (MalformedURLException|NumberFormatException e) {
+        } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            Log.e(TAG, "Invalid captive portal URL, server=" + server);
+            done(Result.WANTED_AS_IS);
         }
+        mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+        mResponseToken = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN);
 
-        final ConnectivityManager cm = ConnectivityManager.from(this);
-        final Network network = new Network(mNetId);
         // Also initializes proxy system properties.
-        cm.bindProcessToNetwork(network);
+        mCm.bindProcessToNetwork(mNetwork);
 
         // Proxy system properties must be initialized before setContentView is called because
         // setContentView initializes the WebView logic which in turn reads the system properties.
@@ -106,7 +91,7 @@
         getActionBar().setDisplayShowHomeEnabled(false);
 
         // Exit app if Network disappears.
-        final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network);
+        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
         if (networkCapabilities == null) {
             finish();
             return;
@@ -114,14 +99,14 @@
         mNetworkCallback = new NetworkCallback() {
             @Override
             public void onLost(Network lostNetwork) {
-                if (network.equals(lostNetwork)) done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
             }
         };
         final NetworkRequest.Builder builder = new NetworkRequest.Builder();
         for (int transportType : networkCapabilities.getTransportTypes()) {
             builder.addTransportType(transportType);
         }
-        cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+        mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
 
         final WebView myWebView = (WebView) findViewById(R.id.webview);
         myWebView.clearCache(true);
@@ -158,15 +143,21 @@
         }
     }
 
-    private void done(int result) {
+    private void done(Result result) {
         if (mNetworkCallback != null) {
-            ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
+            mCm.unregisterNetworkCallback(mNetworkCallback);
         }
-        Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-        intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
-        intent.putExtra(LOGGED_IN_RESULT, String.valueOf(result));
-        intent.putExtra(RESPONSE_TOKEN, mResponseToken);
-        sendBroadcast(intent);
+        switch (result) {
+            case DISMISSED:
+                mCm.reportCaptivePortalDismissed(mNetwork, mResponseToken);
+                break;
+            case UNWANTED:
+                mCm.ignoreNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+            case WANTED_AS_IS:
+                mCm.useNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+        }
         finish();
     }
 
@@ -190,11 +181,11 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.action_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            done(Result.WANTED_AS_IS);
             return true;
         }
         if (id == R.id.action_do_not_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+            done(Result.UNWANTED);
             return true;
         }
         return super.onOptionsItemSelected(item);
@@ -223,7 +214,7 @@
                     if (urlConnection != null) urlConnection.disconnect();
                 }
                 if (httpResponseCode == 204) {
-                    done(CAPTIVE_PORTAL_APP_RETURN_APPEASED);
+                    done(Result.DISMISSED);
                 }
             }
         }).start();
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3e5eee8..ef82bb7 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -56,6 +56,7 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import java.util.*;
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7d8e9de..12a99b0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2608,6 +2608,16 @@
         }
     }
 
+    public void captivePortalAppResponse(Network network, int response, String actionToken) {
+        if (response == ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS) {
+            enforceConnectivityInternalPermission();
+        }
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) return;
+        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_CAPTIVE_PORTAL_APP_FINISHED, response, 0,
+                actionToken);
+    }
+
     public ProxyInfo getDefaultProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 7e20276..4e83992 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -83,17 +83,6 @@
     private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
             "android.permission.ACCESS_NETWORK_CONDITIONS";
 
-    // Keep these in sync with CaptivePortalLoginActivity.java.
-    // Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final String RESPONSE_TOKEN = "response_token";
-
     // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
     // The network should be used as a default internet connection.  It was found to be:
     // 1. a functioning network providing internet access, or
@@ -166,11 +155,12 @@
 
     /**
      * Message to self indicating captive portal app finished.
-     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_APPEASED,
+     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
      *                CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
      *                CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS
+     * obj = mCaptivePortalLoggedInResponseToken as String
      */
-    private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
+    public static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
 
     /**
      * Request ConnectivityService display provisioning notification.
@@ -181,26 +171,11 @@
     public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
 
     /**
-     * Message to self indicating sign-in app bypassed captive portal.
+     * Message to self indicating sign-in app should be launched.
+     * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
+     * user touches the sign in notification.
      */
-    private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11;
-
-    /**
-     * Message to self indicating no sign-in app responded.
-     */
-    private static final int EVENT_NO_APP_RESPONSE = BASE + 12;
-
-    /**
-     * Message to self indicating sign-in app indicates sign-in is not possible.
-     */
-    private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 13;
-
-    /**
-     * Return codes from captive portal sign-in app.
-     */
-    public static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
 
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     // Default to 30s linger time-out.
@@ -255,7 +230,7 @@
     private final State mCaptivePortalState = new CaptivePortalState();
     private final State mLingeringState = new LingeringState();
 
-    private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver = null;
+    private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
     private String mCaptivePortalLoggedInResponseToken = null;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
@@ -319,9 +294,9 @@
                     return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
                     if (DBG) log("Disconnected - quitting");
-                    if (mCaptivePortalLoggedInBroadcastReceiver != null) {
-                        mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
-                        mCaptivePortalLoggedInBroadcastReceiver = null;
+                    if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
+                        mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
+                        mLaunchCaptivePortalAppBroadcastReceiver = null;
                     }
                     quit();
                     return HANDLED;
@@ -332,14 +307,21 @@
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_CAPTIVE_PORTAL_APP_FINISHED:
-                    // Previous token was broadcast, come up with a new one.
+                    if (!mCaptivePortalLoggedInResponseToken.equals((String)message.obj))
+                        return HANDLED;
+                    // Previous token was sent out, come up with a new one.
                     mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
                     switch (message.arg1) {
-                        case CAPTIVE_PORTAL_APP_RETURN_APPEASED:
-                        case CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
+                            sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+                                    0 /* INITIAL_ATTEMPTS */);
+                            break;
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                            // TODO: Distinguish this from a network that actually validates.
+                            // Displaying the "!" on the system UI icon may still be a good idea.
                             transitionTo(mValidatedState);
                             break;
-                        case CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
                             mUserDoesNotWant = true;
                             // TODO: Should teardown network.
                             transitionTo(mOfflineState);
@@ -417,6 +399,25 @@
     // is required.  This State takes care to clear the notification upon exit from the State.
     private class MaybeNotifyState extends State {
         @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString());
+            switch (message.what) {
+                case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
+                    final Intent intent = new Intent(
+                            ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+                    intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network);
+                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN,
+                            mCaptivePortalLoggedInResponseToken);
+                    intent.setFlags(
+                            Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        @Override
         public void exit() {
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
                     mNetworkAgentInfo.network.netId, null);
@@ -512,7 +513,9 @@
             mContext.registerReceiver(this, new IntentFilter(mAction));
         }
         public PendingIntent getPendingIntent() {
-            return PendingIntent.getBroadcast(mContext, 0, new Intent(mAction), 0);
+            final Intent intent = new Intent(mAction);
+            intent.setPackage(mContext.getPackageName());
+            return PendingIntent.getBroadcast(mContext, 0, intent, 0);
         }
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -520,48 +523,29 @@
         }
     }
 
-    private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
-                    mNetworkAgentInfo.network.netId &&
-                    mCaptivePortalLoggedInResponseToken.equals(
-                            intent.getStringExtra(RESPONSE_TOKEN))) {
-                sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED,
-                        Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT)), 0));
-            }
-        }
-    }
-
     // Being in the CaptivePortalState State indicates a captive portal was detected and the user
     // has been shown a notification to sign-in.
     private class CaptivePortalState extends State {
+        private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP =
+                "android.net.netmon.launchCaptivePortalApp";
+
         @Override
         public void enter() {
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
-
-            // Assemble Intent to launch captive portal sign-in app.
-            final Intent intent = new Intent(Intent.ACTION_SEND);
-            // Intent cannot use extras because PendingIntent.getActivity will merge matching
-            // Intents erasing extras.  Use data instead of extras to encode NetID.
-            intent.setData(Uri.fromParts("netid", Integer.toString(mNetworkAgentInfo.network.netId),
-                    mCaptivePortalLoggedInResponseToken));
-            intent.setComponent(new ComponentName("com.android.captiveportallogin",
-                    "com.android.captiveportallogin.CaptivePortalLoginActivity"));
-            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            if (mCaptivePortalLoggedInBroadcastReceiver == null) {
+            // Create a CustomIntentReceiver that sends us a
+            // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
+            // touches the notification.
+            if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
                 // Wait for result.
-                mCaptivePortalLoggedInBroadcastReceiver =
-                        new CaptivePortalLoggedInBroadcastReceiver();
-                final IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-                mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
+                mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
+                        ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
+                        CMD_LAUNCH_CAPTIVE_PORTAL_APP);
             }
-            // Initiate notification to sign-in.
+            // Display the sign in notification.
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
                     mNetworkAgentInfo.network.netId,
-                    PendingIntent.getActivity(mContext, 0, intent, 0));
+                    mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
             mConnectivityServiceHandler.sendMessage(message);
         }
 
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index 25b8093..c0e1d19 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -29,9 +29,9 @@
      */
     private final Inet4Address mSrcIp;
 
-    DhcpAckPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+    DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
                   Inet4Address clientIp, byte[] clientMac) {
-        super(transId, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
         mBroadcast = broadcast;
         mSrcIp = serverAddress;
     }
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index ab56493..e1d1787 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -154,6 +154,7 @@
     private byte[] mHwAddr;
     private PacketSocketAddress mInterfaceBroadcastAddr;
     private int mTransactionId;
+    private long mTransactionStartMillis;
     private DhcpResults mDhcpLease;
     private long mDhcpLeaseExpiry;
     private DhcpResults mOffer;
@@ -264,8 +265,9 @@
         }
     }
 
-    private void initTransactionId() {
+    private void startNewTransaction() {
         mTransactionId = mRandom.nextInt();
+        mTransactionStartMillis = SystemClock.elapsedRealtime();
     }
 
     private boolean initSockets() {
@@ -344,6 +346,10 @@
         }
     }
 
+    private short getSecs() {
+        return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000);
+    }
+
     private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
         try {
             if (to.equals(INADDR_BROADCAST)) {
@@ -362,7 +368,8 @@
 
     private boolean sendDiscoverPacket() {
         ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
-                DhcpPacket.ENCAP_L2, mTransactionId, mHwAddr, DO_UNICAST, REQUESTED_PARAMS);
+                DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
+                DO_UNICAST, REQUESTED_PARAMS);
         return transmitPacket(packet, "DHCPDISCOVER", INADDR_BROADCAST);
     }
 
@@ -373,7 +380,7 @@
         int encap = to.equals(INADDR_BROADCAST) ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
 
         ByteBuffer packet = DhcpPacket.buildRequestPacket(
-                encap, mTransactionId, clientAddress,
+                encap, mTransactionId, getSecs(), clientAddress,
                 DO_UNICAST, mHwAddr, requestedAddress,
                 serverAddress, REQUESTED_PARAMS, null);
         String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
@@ -669,7 +676,7 @@
         @Override
         public void enter() {
             super.enter();
-            initTransactionId();
+            startNewTransaction();
         }
 
         protected boolean sendPacket() {
@@ -776,7 +783,7 @@
         @Override
         public void enter() {
             super.enter();
-            initTransactionId();
+            startNewTransaction();
         }
 
         protected boolean sendPacket() {
diff --git a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
index 4a22b65..7ecdea7 100644
--- a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates a DECLINE packet with the specified parameters.
      */
-    DhcpDeclinePacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                       Inet4Address nextIp, Inet4Address relayIp,
                       byte[] clientMac) {
-        super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+        super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
index ed0fdc6..91e6bd6 100644
--- a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -26,8 +26,8 @@
     /**
      * Generates a DISCOVER packet with the specified parameters.
      */
-    DhcpDiscoverPacket(int transId, byte[] clientMac, boolean broadcast) {
-        super(transId, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+    DhcpDiscoverPacket(int transId, short secs, byte[] clientMac, boolean broadcast) {
+        super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpInformPacket.java b/services/net/java/android/net/dhcp/DhcpInformPacket.java
index 2434fc9..7a83466 100644
--- a/services/net/java/android/net/dhcp/DhcpInformPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpInformPacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates an INFORM packet with the specified parameters.
      */
-    DhcpInformPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                      Inet4Address nextIp, Inet4Address relayIp,
                      byte[] clientMac) {
-        super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+        super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
 
     public String toString() {
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
index 1390ea7..6458232 100644
--- a/services/net/java/android/net/dhcp/DhcpNakPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -26,10 +26,10 @@
     /**
      * Generates a NAK packet with the specified parameters.
      */
-    DhcpNakPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    DhcpNakPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                   Inet4Address nextIp, Inet4Address relayIp,
                   byte[] clientMac) {
-        super(transId, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
+        super(transId, secs, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
             clientMac, false);
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index b1f3bbd..af41708 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -31,9 +31,9 @@
     /**
      * Generates a OFFER packet with the specified parameters.
      */
-    DhcpOfferPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+    DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
                     Inet4Address clientIp, byte[] clientMac) {
-        super(transId, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
         mSrcIp = serverAddress;
     }
 
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index a64ee6f..b923b1b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -226,6 +226,11 @@
     protected final int mTransId;
 
     /**
+     * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
+     */
+    protected final short mSecs;
+
+    /**
      * The IP address of the client host.  This address is typically
      * proposed by the client (from an earlier DHCP negotiation) or
      * supplied by the server.
@@ -258,10 +263,11 @@
      */
     abstract void finishPacket(ByteBuffer buffer);
 
-    protected DhcpPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+    protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
                          Inet4Address nextIp, Inet4Address relayIp,
                          byte[] clientMac, boolean broadcast) {
         mTransId = transId;
+        mSecs = secs;
         mClientIp = clientIp;
         mYourIp = yourIp;
         mNextIp = nextIp;
@@ -357,7 +363,7 @@
         buf.put((byte) mClientMac.length); // Hardware Address Length
         buf.put((byte) 0); // Hop Count
         buf.putInt(mTransId);  // Transaction ID
-        buf.putShort((short) 0); // Elapsed Seconds
+        buf.putShort(mSecs); // Elapsed Seconds
 
         if (broadcast) {
             buf.putShort((short) 0x8000); // Flags
@@ -652,6 +658,7 @@
     {
         // bootp parameters
         int transactionId;
+        short secs;
         Inet4Address clientIp;
         Inet4Address yourIp;
         Inet4Address nextIp;
@@ -759,7 +766,7 @@
         byte addrLen = packet.get();
         byte hops = packet.get();
         transactionId = packet.getInt();
-        short elapsed = packet.getShort();
+        secs = packet.getShort();
         short bootpFlags = packet.getShort();
         boolean broadcast = (bootpFlags & 0x8000) != 0;
         byte[] ipv4addr = new byte[4];
@@ -902,33 +909,33 @@
             case -1: return null;
             case DHCP_MESSAGE_TYPE_DISCOVER:
                 newPacket = new DhcpDiscoverPacket(
-                    transactionId, clientMac, broadcast);
+                    transactionId, secs, clientMac, broadcast);
                 break;
             case DHCP_MESSAGE_TYPE_OFFER:
                 newPacket = new DhcpOfferPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_REQUEST:
                 newPacket = new DhcpRequestPacket(
-                    transactionId, clientIp, clientMac, broadcast);
+                    transactionId, secs, clientIp, clientMac, broadcast);
                 break;
             case DHCP_MESSAGE_TYPE_DECLINE:
                 newPacket = new DhcpDeclinePacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_ACK:
                 newPacket = new DhcpAckPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                    transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_NAK:
                 newPacket = new DhcpNakPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             case DHCP_MESSAGE_TYPE_INFORM:
                 newPacket = new DhcpInformPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    transactionId, secs, clientIp, yourIp, nextIp, relayIp,
                     clientMac);
                 break;
             default:
@@ -1008,9 +1015,9 @@
      * parameters.
      */
     public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
-        byte[] clientMac, boolean broadcast, byte[] expectedParams) {
+        short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) {
         DhcpPacket pkt = new DhcpDiscoverPacket(
-            transactionId, clientMac, broadcast);
+            transactionId, secs, clientMac, broadcast);
         pkt.mRequestedParams = expectedParams;
         return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
     }
@@ -1025,7 +1032,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpOfferPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
@@ -1045,7 +1052,7 @@
         Inet4Address gateway, List<Inet4Address> dnsServers,
         Inet4Address dhcpServerIdentifier, String domainName) {
         DhcpPacket pkt = new DhcpAckPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+            transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
         pkt.mGateway = gateway;
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
@@ -1061,7 +1068,7 @@
      */
     public static ByteBuffer buildNakPacket(int encap, int transactionId,
         Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac) {
-        DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
+        DhcpPacket pkt = new DhcpNakPacket(transactionId, (short) 0, clientIpAddr,
             serverIpAddr, serverIpAddr, serverIpAddr, mac);
         pkt.mMessage = "requested address not available";
         pkt.mRequestedIp = clientIpAddr;
@@ -1072,10 +1079,10 @@
      * Builds a DHCP-REQUEST packet from the required specified parameters.
      */
     public static ByteBuffer buildRequestPacket(int encap,
-        int transactionId, Inet4Address clientIp, boolean broadcast,
+        int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
         byte[] clientMac, Inet4Address requestedIpAddress,
         Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
-        DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
+        DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
             clientMac, broadcast);
         pkt.mRequestedIp = requestedIpAddress;
         pkt.mServerIdentifier = serverIdentifier;
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
index 5d378b8..4f9aa01 100644
--- a/services/net/java/android/net/dhcp/DhcpRequestPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -28,9 +28,9 @@
     /**
      * Generates a REQUEST packet with the specified parameters.
      */
-    DhcpRequestPacket(int transId, Inet4Address clientIp, byte[] clientMac,
+    DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, byte[] clientMac,
                       boolean broadcast) {
-        super(transId, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+        super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 2658937..4f7c7ec 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -41,7 +41,8 @@
         private byte[] mDomainBytes, mVendorInfoBytes;
 
         public TestDhcpPacket(byte type, byte[] domainBytes, byte[] vendorInfoBytes) {
-            super(0xdeadbeef, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY, CLIENT_MAC, true);
+            super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+                  CLIENT_MAC, true);
             mType = type;
             mDomainBytes = domainBytes;
             mVendorInfoBytes = vendorInfoBytes;
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2a30384..d92c0c7 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -933,7 +933,8 @@
                     Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
         }
 
-        boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
+                !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
         if (videoCallChanged) {
             mVideoCall = parcelableCall.getVideoCall();
         }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index bab60fe..0424548 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -50,6 +50,7 @@
                 Conference conference, int connectionCapabilities) {}
         public void onVideoStateChanged(Conference c, int videoState) { }
         public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
+        public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
     }
 
     private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -67,6 +68,7 @@
     private int mConnectionCapabilities;
     private String mDisconnectMessage;
     private long mConnectTimeMillis = CONNECT_TIME_NOT_SPECIFIED;
+    private StatusHints mStatusHints;
 
     private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
         @Override
@@ -535,4 +537,23 @@
                 getVideoProvider(),
                 super.toString());
     }
+
+    /**
+     * Sets the label and icon status to display in the InCall UI.
+     *
+     * @param statusHints The status label and icon to set.
+     */
+    public final void setStatusHints(StatusHints statusHints) {
+        mStatusHints = statusHints;
+        for (Listener l : mListeners) {
+            l.onStatusHintsChanged(this, statusHints);
+        }
+    }
+
+    /**
+     * @return The status hints for this conference.
+     */
+    public final StatusHints getStatusHints() {
+        return mStatusHints;
+    }
 }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index e79584f..cd10050 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -28,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -402,7 +403,7 @@
          */
         public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
 
-        private static final int MSG_SET_VIDEO_CALLBACK = 1;
+        private static final int MSG_ADD_VIDEO_CALLBACK = 1;
         private static final int MSG_SET_CAMERA = 2;
         private static final int MSG_SET_PREVIEW_SURFACE = 3;
         private static final int MSG_SET_DISPLAY_SURFACE = 4;
@@ -413,11 +414,16 @@
         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
         private static final int MSG_SET_PAUSE_IMAGE = 11;
+        private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
 
         private final VideoProvider.VideoProviderHandler
                 mMessageHandler = new VideoProvider.VideoProviderHandler();
         private final VideoProvider.VideoProviderBinder mBinder;
-        private IVideoCallback mVideoCallback;
+
+        /**
+         * Stores a list of the video callbacks, keyed by IBinder.
+         */
+        private HashMap<IBinder, IVideoCallback> mVideoCallbacks = new HashMap<>();
 
         /**
          * Default handler used to consolidate binder method calls onto a single thread.
@@ -426,9 +432,29 @@
             @Override
             public void handleMessage(Message msg) {
                 switch (msg.what) {
-                    case MSG_SET_VIDEO_CALLBACK:
-                        mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+                    case MSG_ADD_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "addVideoProvider - skipped; already present.");
+                            break;
+                        }
+                        mVideoCallbacks.put(binder, callback);
+                        Log.i(this, "addVideoProvider  "+ mVideoCallbacks.size());
                         break;
+                    }
+                    case MSG_REMOVE_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (!mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "removeVideoProvider - skipped; not present.");
+                            break;
+                        }
+                        mVideoCallbacks.remove(binder);
+                        break;
+                    }
                     case MSG_SET_CAMERA:
                         onSetCamera((String) msg.obj);
                         break;
@@ -469,9 +495,14 @@
          * IVideoProvider stub implementation.
          */
         private final class VideoProviderBinder extends IVideoProvider.Stub {
-            public void setVideoCallback(IBinder videoCallbackBinder) {
+            public void addVideoCallback(IBinder videoCallbackBinder) {
                 mMessageHandler.obtainMessage(
-                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+                        MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+            }
+
+            public void removeVideoCallback(IBinder videoCallbackBinder) {
+                mMessageHandler.obtainMessage(
+                        MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
             }
 
             public void setCamera(String cameraId) {
@@ -609,21 +640,23 @@
         public abstract void onSetPauseImage(String uri);
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param videoProfile The requested video connection profile.
          */
         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyRequest(videoProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyRequest(videoProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param status Status of the session modify request.  Valid values are
          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
@@ -634,17 +667,19 @@
          */
         public void receiveSessionModifyResponse(int status,
                 VideoProfile requestedProfile, VideoProfile responseProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyResponse(
-                            status, requestedProfile, responseProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyResponse(status, requestedProfile,
+                                responseProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
          * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
@@ -654,66 +689,76 @@
          * @param event The event.
          */
         public void handleCallSessionEvent(int event) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.handleCallSessionEvent(event);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.handleCallSessionEvent(event);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param width  The updated peer video width.
          * @param height The updated peer video height.
          */
         public void changePeerDimensions(int width, int height) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changePeerDimensions(width, height);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changePeerDimensions(width, height);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param dataUsage The updated data usage.
          */
         public void changeCallDataUsage(long dataUsage) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCallDataUsage(dataUsage);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCallDataUsage(dataUsage);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param cameraCapabilities The changed camera capabilities.
          */
         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCameraCapabilities(cameraCapabilities);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCameraCapabilities(cameraCapabilities);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param videoQuality The updated video quality.
          */
         public void changeVideoQuality(int videoQuality) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeVideoQuality(videoQuality);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeVideoQuality(videoQuality);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 9812815..c039acf 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -427,6 +427,12 @@
                     videoProvider);
             mAdapter.setVideoProvider(id, videoProvider);
         }
+
+        @Override
+        public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
+            String id = mIdByConference.get(conference);
+            mAdapter.setStatusHints(id, statusHints);
+        }
     };
 
     private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -903,8 +909,9 @@
                     conference.getVideoProvider() == null ?
                             null : conference.getVideoProvider().getInterface(),
                     conference.getVideoState(),
-                    conference.getConnectTimeMillis()
-                    );
+                    conference.getConnectTimeMillis(),
+                    conference.getStatusHints());
+
             mAdapter.addConferenceCall(id, parcelableConference);
             mAdapter.setVideoProvider(id, conference.getVideoProvider());
             mAdapter.setVideoState(id, conference.getVideoState());
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 7cbc0fc..e5d6ae0 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -370,6 +370,11 @@
         public abstract void registerCallback(VideoCall.Callback callback);
 
         /**
+         * Clears the video call listener set via {@link #setVideoCallListener(Listener)}.
+         */
+        public abstract void unregisterCallback();
+
+        /**
          * Sets the camera to be used for video recording in a video call.
          *
          * @param cameraId The id of the camera.
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index c5c3d11..1a30910 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -46,6 +46,7 @@
     private final int mCallerDisplayNamePresentation;
     private final GatewayInfo mGatewayInfo;
     private final PhoneAccountHandle mAccountHandle;
+    private final boolean mIsVideoCallProviderChanged;
     private final IVideoProvider mVideoCallProvider;
     private InCallService.VideoCall mVideoCall;
     private final String mParentCallId;
@@ -69,6 +70,7 @@
             int callerDisplayNamePresentation,
             GatewayInfo gatewayInfo,
             PhoneAccountHandle accountHandle,
+            boolean isVideoCallProviderChanged,
             IVideoProvider videoCallProvider,
             String parentCallId,
             List<String> childCallIds,
@@ -89,6 +91,7 @@
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
         mGatewayInfo = gatewayInfo;
         mAccountHandle = accountHandle;
+        mIsVideoCallProviderChanged = isVideoCallProviderChanged;
         mVideoCallProvider = videoCallProvider;
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
@@ -232,6 +235,18 @@
         return mExtras;
     }
 
+    /**
+     * Indicates to the receiver of the {@link ParcelableCall} whether a change has occurred in the
+     * {@link android.telecom.InCallService.VideoCall} associated with this call.  Since
+     * {@link #getVideoCall()} creates a new {@link VideoCallImpl}, it is useful to know whether
+     * the provider has changed (which can influence whether it is accessed).
+     *
+     * @return {@code true} if the video call changed, {@code false} otherwise.
+     */
+    public boolean isVideoCallProviderChanged() {
+        return mIsVideoCallProviderChanged;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -252,6 +267,7 @@
             int callerDisplayNamePresentation = source.readInt();
             GatewayInfo gatewayInfo = source.readParcelable(classLoader);
             PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+            boolean isVideoCallProviderChanged = source.readByte() == 1;
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             String parentCallId = source.readString();
@@ -276,6 +292,7 @@
                     callerDisplayNamePresentation,
                     gatewayInfo,
                     accountHandle,
+                    isVideoCallProviderChanged,
                     videoCallProvider,
                     parentCallId,
                     childCallIds,
@@ -313,6 +330,7 @@
         destination.writeInt(mCallerDisplayNamePresentation);
         destination.writeParcelable(mGatewayInfo, 0);
         destination.writeParcelable(mAccountHandle, 0);
+        destination.writeByte((byte) (mIsVideoCallProviderChanged ? 1 : 0));
         destination.writeStrongBinder(
                 mVideoCallProvider != null ? mVideoCallProvider.asBinder() : null);
         destination.writeString(mParentCallId);
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ab82549..e54e79d 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -34,25 +34,10 @@
     private int mState;
     private int mConnectionCapabilities;
     private List<String> mConnectionIds;
-    private long mConnectTimeMillis;
+    private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
     private final IVideoProvider mVideoProvider;
     private final int mVideoState;
-
-    public ParcelableConference(
-            PhoneAccountHandle phoneAccount,
-            int state,
-            int connectionCapabilities,
-            List<String> connectionIds,
-            IVideoProvider videoProvider,
-            int videoState) {
-        mPhoneAccount = phoneAccount;
-        mState = state;
-        mConnectionCapabilities = connectionCapabilities;
-        mConnectionIds = connectionIds;
-        mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
-        mVideoProvider = videoProvider;
-        mVideoState = videoState;
-    }
+    private StatusHints mStatusHints;
 
     public ParcelableConference(
             PhoneAccountHandle phoneAccount,
@@ -61,9 +46,17 @@
             List<String> connectionIds,
             IVideoProvider videoProvider,
             int videoState,
-            long connectTimeMillis) {
-        this(phoneAccount, state, connectionCapabilities, connectionIds, videoProvider, videoState);
+            long connectTimeMillis,
+            StatusHints statusHints) {
+        mPhoneAccount = phoneAccount;
+        mState = state;
+        mConnectionCapabilities = connectionCapabilities;
+        mConnectionIds = connectionIds;
+        mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+        mVideoProvider = videoProvider;
+        mVideoState = videoState;
         mConnectTimeMillis = connectTimeMillis;
+        mStatusHints = statusHints;
     }
 
     @Override
@@ -113,6 +106,10 @@
         return mVideoState;
     }
 
+    public StatusHints getStatusHints() {
+        return mStatusHints;
+    }
+
     public static final Parcelable.Creator<ParcelableConference> CREATOR =
             new Parcelable.Creator<ParcelableConference> () {
         @Override
@@ -124,13 +121,14 @@
             List<String> connectionIds = new ArrayList<>(2);
             source.readList(connectionIds, classLoader);
             long connectTimeMillis = source.readLong();
+            StatusHints statusHints = source.readParcelable(classLoader);
 
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             int videoState = source.readInt();
 
             return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
-                    videoCallProvider, videoState);
+                    videoCallProvider, videoState, connectTimeMillis, statusHints);
         }
 
         @Override
@@ -156,5 +154,6 @@
         destination.writeStrongBinder(
                 mVideoProvider != null ? mVideoProvider.asBinder() : null);
         destination.writeInt(mVideoState);
+        destination.writeParcelable(mStatusHints, 0);
     }
 }
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index c1c1129..3d9acda 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -122,6 +122,11 @@
     final void internalRemoveCall(Call call) {
         mCallByTelecomCallId.remove(call.internalGetCallId());
         mCalls.remove(call);
+
+        InCallService.VideoCall videoCall = call.getVideoCall();
+        if (videoCall != null) {
+            videoCall.unregisterCallback();
+        }
         fireCallRemoved(call);
     }
 
@@ -167,6 +172,10 @@
      */
     final void destroy() {
         for (Call call : mCalls) {
+            InCallService.VideoCall videoCall = call.getVideoCall();
+            if (videoCall != null) {
+                videoCall.unregisterCallback();
+            }
             if (call.getState() != Call.STATE_DISCONNECTED) {
                 call.internalSetDisconnected();
             }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 4c423f2..4ecfd50 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -301,7 +301,7 @@
         public VideoProvider(IVideoProvider videoProviderBinder) {
             mVideoProviderBinder = videoProviderBinder;
             try {
-                mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+                mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
             } catch (RemoteException e) {
             }
         }
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 7bef688..3779d1a 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -166,7 +166,7 @@
         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
 
         mBinder = new VideoCallListenerBinder();
-        mVideoProvider.setVideoCallback(mBinder);
+        mVideoProvider.addVideoCallback(mBinder);
     }
 
     /** {@inheritDoc} */
@@ -175,6 +175,15 @@
     }
 
     /** {@inheritDoc} */
+    public void unregisterCallback() {
+        mCallback = null;
+        try {
+            mVideoProvider.removeVideoCallback(mBinder);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /** {@inheritDoc} */
     public void setCamera(String cameraId) {
         try {
             mVideoProvider.setCamera(cameraId);
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
index e96d9d3..bff3865 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -25,7 +25,9 @@
  * @hide
  */
 oneway interface IVideoProvider {
-    void setVideoCallback(IBinder videoCallbackBinder);
+    void addVideoCallback(IBinder videoCallbackBinder);
+
+    void removeVideoCallback(IBinder videoCallbackBinder);
 
     void setCamera(String cameraId);
 
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 84d1c545..0443c3e 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -115,4 +115,12 @@
      * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
      */
     void callSessionTtyModeReceived(in IImsCallSession session, in int mode);
+
+    /**
+     * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+     *
+     * @param session The call session.
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    void callSessionMultipartyStateChanged(in IImsCallSession session, in boolean isMultiParty);
 }
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index b156d0c..d2fb0dd 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -808,7 +808,7 @@
         if (mRttCapabilities == null) {
             if(getRttCapabilities() == null) {
                 Log.e(TAG, "Can not get RTT capabilities");
-                //throw new IllegalStateException("RTT chip is not working");
+                throw new IllegalStateException("RTT chip is not working");
             }
         }
 
@@ -866,6 +866,15 @@
         return true;
     }
 
+    /**
+     * Request to start an RTT ranging
+     *
+     * @param params  -- RTT request Parameters
+     * @param listener -- Call back to inform RTT result
+     * @exception throw IllegalArgumentException when params are illegal
+     *            throw IllegalStateException when RttCapabilities do not exist
+     */
+
     public void startRanging(RttParams[] params, RttListener listener) {
         int index  = 0;
         for(RttParams rttParam : params) {
@@ -874,9 +883,9 @@
             }
             index++;
         }
-
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
+        Log.i(TAG, "Send RTT request to RTT Service");
         sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
                 0, putListener(listener), parcelableParams);
     }
@@ -1024,6 +1033,7 @@
         }
         @Override
         public void handleMessage(Message msg) {
+            Log.i(TAG, "RTT manager get message: " + msg.what);
             switch (msg.what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
@@ -1049,10 +1059,10 @@
 
             Object listener = getListener(msg.arg2);
             if (listener == null) {
-                if (DBG) Log.d(TAG, "invalid listener key = " + msg.arg2);
+                Log.e(TAG, "invalid listener key = " + msg.arg2 );
                 return;
             } else {
-                if (DBG) Log.d(TAG, "listener key = " + msg.arg2);
+                Log.i(TAG, "listener key = " + msg.arg2);
             }
 
             switch (msg.what) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index c6f2991..b731316 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -243,6 +243,7 @@
      * The band which AP resides on
      * 0-2G  1-5G
      * By default, 2G is chosen
+     * @hide
      */
     public int apBand = 0;
 
@@ -251,6 +252,7 @@
      * 2G  1-11
      * 5G  36,40,44,48,149,153,157,161,165
      * 0 - find a random available channel according to the apBand
+     * @hide
      */
     public int apChannel = 0;
 
@@ -953,7 +955,7 @@
         if (!TextUtils.isEmpty(FQDN)) {
             /* this is passpoint configuration; it must not have an SSID */
             if (!TextUtils.isEmpty(SSID)) {
-                return "no SSID";
+                return "SSID not expected for Passpoint: '" + SSID + "'";
             }
             /* this is passpoint configuration; it must have a providerFriendlyName */
             if (TextUtils.isEmpty(providerFriendlyName)) {
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 6917971..3525ec2 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -56,6 +56,8 @@
     /** @hide */
     public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match";
     /** @hide */
+    public static final String DOM_SUFFIX_MATCH_KEY = "domain_suffix_match";
+    /** @hide */
     public static final String OPP_KEY_CACHING     = "proactive_key_caching";
     /**
      * String representing the keystore OpenSSL ENGINE's ID.
@@ -577,6 +579,36 @@
     }
 
     /**
+     * Set the domain_suffix_match directive on wpa_supplicant. This is the parameter to use
+     * for Hotspot 2.0 defined matching of AAA server certs per WFA HS2.0 spec, section 7.3.3.2,
+     * second paragraph.
+     *
+     * From wpa_supplicant documentation:
+     * Constraint for server domain name. If set, this FQDN is used as a suffix match requirement
+     * for the AAAserver certificate in SubjectAltName dNSName element(s). If a matching dNSName is
+     * found, this constraint is met. If no dNSName values are present, this constraint is matched
+     * against SubjectName CN using same suffix match comparison.
+     * Suffix match here means that the host/domain name is compared one label at a time starting
+     * from the top-level domain and all the labels in domain_suffix_match shall be included in the
+     * certificate. The certificate may include additional sub-level labels in addition to the
+     * required labels.
+     * For example, domain_suffix_match=example.com would match test.example.com but would not
+     * match test-example.com.
+     * @param domain The domain value
+     */
+    public void setDomainSuffixMatch(String domain) {
+        setFieldValue(DOM_SUFFIX_MATCH_KEY, domain);
+    }
+
+    /**
+     * Get the domain_suffix_match value. See setDomSuffixMatch.
+     * @return The domain value.
+     */
+    public String getDomainSubjectMatch() {
+        return getFieldValue(DOM_SUFFIX_MATCH_KEY, "");
+    }
+
+    /**
      * Set realm for passpoint credential
      * @param realm the realm
      */