Merge "Add subId as parameter for get/setDataEnabled." into lmp-mr1-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7d5db85..fa87c80 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -101,7 +101,7 @@
 
     /**
      * Identical to {@link #CONNECTIVITY_ACTION} broadcast, but sent without any
-     * historic {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
+     * applicable {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY}.
      *
      * @hide
      */
@@ -429,6 +429,18 @@
     public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
 
     /**
+     * Default value for {@link Settings.Global#CONNECTIVITY_CHANGE_DELAY} in
+     * milliseconds.  This was introduced because IPv6 routes seem to take a
+     * moment to settle - trying network activity before the routes are adjusted
+     * can lead to packets using the wrong interface or having the wrong IP address.
+     * This delay is a bit crude, but in the future hopefully we will have kernel
+     * notifications letting us know when it's safe to use the new network.
+     *
+     * @hide
+     */
+    public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+
+    /**
      * @hide
      */
     public final static int REQUEST_ID_UNSET = 0;
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 8021210..d9921a6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -68,9 +68,6 @@
 
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
 
-    /** Policy control over specific {@link NetworkStateTracker}. */
-    void setPolicyDataEnable(int networkType, boolean enabled);
-
     int tether(String iface);
 
     int untether(String iface);
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 5d2a43d..b92c9e3 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -20,15 +20,18 @@
 import android.os.Parcelable;
 
 /**
- * A grab-bag of information (metadata, policies, properties, etc) about a {@link Network}.
+ * A grab-bag of information (metadata, policies, properties, etc) about a
+ * {@link Network}. Since this contains PII, it should not be sent outside the
+ * system.
  *
  * @hide
  */
 public class NetworkMisc implements Parcelable {
 
     /**
-     * If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
-     * a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
+     * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+     * VPN. This is set by a {@link VpnService} and used by
+     * {@link ConnectivityManager} when creating a VPN.
      */
     public boolean allowBypass;
 
@@ -41,6 +44,11 @@
      */
     public boolean explicitlySelected;
 
+    /**
+     * For mobile networks, this is the subscriber ID (such as IMSI).
+     */
+    public String subscriberId;
+
     public NetworkMisc() {
     }
 
@@ -48,6 +56,7 @@
         if (nm != null) {
             allowBypass = nm.allowBypass;
             explicitlySelected = nm.explicitlySelected;
+            subscriberId = nm.subscriberId;
         }
     }
 
@@ -60,6 +69,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(allowBypass ? 1 : 0);
         out.writeInt(explicitlySelected ? 1 : 0);
+        out.writeString(subscriberId);
     }
 
     public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -68,6 +78,7 @@
             NetworkMisc networkMisc = new NetworkMisc();
             networkMisc.allowBypass = in.readInt() != 0;
             networkMisc.explicitlySelected = in.readInt() != 0;
+            networkMisc.subscriberId = in.readString();
             return networkMisc;
         }
 
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index d26c70d..933287f 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -30,16 +30,10 @@
     public final LinkProperties linkProperties;
     public final NetworkCapabilities networkCapabilities;
     public final Network network;
-    /** Currently only used by testing. */
     public final String subscriberId;
     public final String networkId;
 
     public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
-            NetworkCapabilities networkCapabilities, Network network) {
-        this(networkInfo, linkProperties, networkCapabilities, network, null, null);
-    }
-
-    public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
             NetworkCapabilities networkCapabilities, Network network, String subscriberId,
             String networkId) {
         this.networkInfo = networkInfo;
@@ -85,5 +79,4 @@
             return new NetworkState[size];
         }
     };
-
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0f1ed0a..a86d564 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,22 +20,8 @@
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
-import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
-import static android.net.ConnectivityManager.TYPE_DUMMY;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_MOBILE_CBS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
-import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IA;
-import static android.net.ConnectivityManager.TYPE_MOBILE_IMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
-import static android.net.ConnectivityManager.TYPE_MOBILE_SUPL;
 import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_PROXY;
 import static android.net.ConnectivityManager.TYPE_VPN;
-import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -45,11 +31,9 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
@@ -62,7 +46,6 @@
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
-import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
 import android.net.MobileDataStateTracker;
@@ -72,7 +55,6 @@
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkFactory;
 import android.net.NetworkMisc;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
@@ -80,16 +62,12 @@
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
-import android.net.ProxyDataTracker;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.SamplingDataTracker;
 import android.net.UidRange;
 import android.net.Uri;
-import android.net.wimax.WimaxManagerConstants;
-import android.os.AsyncTask;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
@@ -103,7 +81,6 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -126,9 +103,6 @@
 import com.android.internal.net.VpnConfig;
 import com.android.internal.net.VpnProfile;
 import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
@@ -146,8 +120,6 @@
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
-import dalvik.system.DexClassLoader;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -157,30 +129,20 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.reflect.Constructor;
-import java.net.HttpURLConnection;
 import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.URL;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
-import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSession;
-
 /**
  * @hide
  */
@@ -215,6 +177,10 @@
 
     private static final int SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE = 0;
 
+    // How long to delay to removal of a pending intent based request.
+    // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
+    private final int mReleasePendingIntentDelayMs;
+
     private PendingIntent mSampleIntervalElapsedIntent;
 
     // Set network sampling interval at 12 minutes, this way, even if the timers get
@@ -265,6 +231,7 @@
     private static ConnectivityService sServiceInstance;
 
     private INetworkManagementService mNetd;
+    private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
 
     private String mCurrentTcpBufferSizes;
@@ -272,6 +239,26 @@
     private static final int ENABLED  = 1;
     private static final int DISABLED = 0;
 
+    // Arguments to rematchNetworkAndRequests()
+    private enum NascentState {
+        // Indicates a network was just validated for the first time.  If the network is found to
+        // be unwanted (i.e. not satisfy any NetworkRequests) it is torn down.
+        JUST_VALIDATED,
+        // Indicates a network was not validated for the first time immediately prior to this call.
+        NOT_JUST_VALIDATED
+    };
+    private enum ReapUnvalidatedNetworks {
+        // Tear down unvalidated networks that have no chance (i.e. even if validated) of becoming
+        // the highest scoring network satisfying a NetworkRequest.  This should be passed when it's
+        // known that there may be unvalidated networks that could potentially be reaped, and when
+        // all networks have been rematched against all NetworkRequests.
+        REAP,
+        // Don't reap unvalidated networks.  This should be passed when it's known that there are
+        // no unvalidated networks that could potentially be reaped, and when some networks have
+        // not yet been rematched against all NetworkRequests.
+        DONT_REAP
+    };
+
     /**
      * used internally to change our mobile data enabled flag
      */
@@ -303,12 +290,6 @@
     private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
 
     /**
-     * Used internally to
-     * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
-     */
-    private static final int EVENT_SET_POLICY_DATA_ENABLE = 12;
-
-    /**
      * Used internally to disable fail fast of mobile data
      */
     private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
@@ -645,8 +626,12 @@
             loge("Error setting defaultDns using " + dns);
         }
 
+        mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
+
         mContext = checkNotNull(context, "missing Context");
         mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+        mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
         mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
         mKeyStore = KeyStore.getInstance();
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -797,6 +782,17 @@
         throw new IllegalStateException("No free netIds");
     }
 
+    private int getConnectivityChangeDelay() {
+        final ContentResolver cr = mContext.getContentResolver();
+
+        /** Check system properties for the default value then use secure settings value, if any. */
+        int defaultDelay = SystemProperties.getInt(
+                "conn." + Settings.Global.CONNECTIVITY_CHANGE_DELAY,
+                ConnectivityManager.CONNECTIVITY_CHANGE_DELAY_DEFAULT);
+        return Settings.Global.getInt(cr, Settings.Global.CONNECTIVITY_CHANGE_DELAY,
+                defaultDelay);
+    }
+
     private boolean teardown(NetworkStateTracker netTracker) {
         if (netTracker.teardown()) {
             netTracker.setTeardownRequested(true);
@@ -811,6 +807,7 @@
         LinkProperties lp = null;
         NetworkCapabilities nc = null;
         Network network = null;
+        String subscriberId = null;
 
         if (mLegacyTypeTracker.isTypeSupported(networkType)) {
             NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
@@ -820,6 +817,7 @@
                     lp = new LinkProperties(nai.linkProperties);
                     nc = new NetworkCapabilities(nai.networkCapabilities);
                     network = new Network(nai.network);
+                    subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
                 }
                 info.setType(networkType);
             } else {
@@ -833,7 +831,7 @@
             info = getFilteredNetworkInfo(info, lp, uid);
         }
 
-        return new NetworkState(info, lp, nc, network);
+        return new NetworkState(info, lp, nc, network, subscriberId, null);
     }
 
     private NetworkAgentInfo getNetworkAgentInfoForNetwork(Network network) {
@@ -850,6 +848,7 @@
         LinkProperties lp = null;
         NetworkCapabilities nc = null;
         Network network = null;
+        String subscriberId = null;
 
         NetworkAgentInfo nai = mNetworkForRequestId.get(mDefaultRequest.requestId);
 
@@ -881,10 +880,11 @@
                 lp = new LinkProperties(nai.linkProperties);
                 nc = new NetworkCapabilities(nai.networkCapabilities);
                 network = new Network(nai.network);
+                subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
             }
         }
 
-        return new NetworkState(info, lp, nc, network);
+        return new NetworkState(info, lp, nc, network, subscriberId, null);
     }
 
     /**
@@ -1181,14 +1181,19 @@
 
     @Override
     public NetworkState[] getAllNetworkState() {
-        enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        // Require internal since we're handing out IMSI details
+        enforceConnectivityInternalPermission();
+
         final ArrayList<NetworkState> result = Lists.newArrayList();
-        for (int networkType = 0; networkType <= ConnectivityManager.MAX_NETWORK_TYPE;
-                networkType++) {
-            NetworkState state = getFilteredNetworkState(networkType, uid);
-            if (state.networkInfo != null) {
-                result.add(state);
+        for (Network network : getAllNetworks()) {
+            final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+            if (nai != null) {
+                synchronized (nai) {
+                    final String subscriberId = (nai.networkMisc != null)
+                            ? nai.networkMisc.subscriberId : null;
+                    result.add(new NetworkState(nai.networkInfo, nai.linkProperties,
+                            nai.networkCapabilities, network, subscriberId, null));
+                }
             }
         }
         return result.toArray(new NetworkState[result.size()]);
@@ -1413,25 +1418,6 @@
         }
     };
 
-    @Override
-    public void setPolicyDataEnable(int networkType, boolean enabled) {
-        // only someone like NPMS should only be calling us
-        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-
-        mHandler.sendMessage(mHandler.obtainMessage(
-                EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
-    }
-
-    private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
-   // TODO - handle this passing to factories
-//        if (isNetworkTypeValid(networkType)) {
-//            final NetworkStateTracker tracker = mNetTrackers[networkType];
-//            if (tracker != null) {
-//                tracker.setPolicyDataEnable(enabled);
-//            }
-//        }
-    }
-
     private void enforceInternetPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.INTERNET,
@@ -1468,6 +1454,11 @@
         sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
     }
 
+    private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
+        sendGeneralBroadcast(info, CONNECTIVITY_ACTION_IMMEDIATE);
+        sendGeneralBroadcastDelayed(info, CONNECTIVITY_ACTION, delayMs);
+    }
+
     private void sendInetConditionBroadcast(NetworkInfo info) {
         sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
     }
@@ -1499,6 +1490,10 @@
         sendStickyBroadcast(makeGeneralIntent(info, bcastType));
     }
 
+    private void sendGeneralBroadcastDelayed(NetworkInfo info, String bcastType, int delayMs) {
+        sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
+    }
+
     private void sendDataActivityBroadcast(int deviceType, boolean active, long tsNanos) {
         Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
         intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
@@ -1524,6 +1519,17 @@
             }
 
             final long ident = Binder.clearCallingIdentity();
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+                final IBatteryStats bs = BatteryStatsService.getService();
+                try {
+                    NetworkInfo ni = intent.getParcelableExtra(
+                            ConnectivityManager.EXTRA_NETWORK_INFO);
+                    bs.noteConnectivityChanged(intent.getIntExtra(
+                            ConnectivityManager.EXTRA_NETWORK_TYPE, ConnectivityManager.TYPE_NONE),
+                            ni != null ? ni.getState().toString() : "?");
+                } catch (RemoteException e) {
+                }
+            }
             try {
                 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
             } finally {
@@ -1532,6 +1538,19 @@
         }
     }
 
+    private void sendStickyBroadcastDelayed(Intent intent, int delayMs) {
+        if (delayMs <= 0) {
+            sendStickyBroadcast(intent);
+        } else {
+            if (VDBG) {
+                log("sendStickyBroadcastDelayed: delayMs=" + delayMs + ", action="
+                        + intent.getAction());
+            }
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                    EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
+        }
+    }
+
     void systemReady() {
         // start network sampling ..
         Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
@@ -1970,12 +1989,11 @@
                         boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                         if (valid) {
                             if (DBG) log("Validated " + nai.name());
-                            final boolean previouslyValidated = nai.validated;
-                            final int previousScore = nai.getCurrentScore();
-                            nai.validated = true;
-                            rematchNetworkAndRequests(nai, !previouslyValidated);
-                            // If score has changed, rebroadcast to NetworkFactories. b/17726566
-                            if (nai.getCurrentScore() != previousScore) {
+                            if (!nai.validated) {
+                                nai.validated = true;
+                                rematchNetworkAndRequests(nai, NascentState.JUST_VALIDATED,
+                                    ReapUnvalidatedNetworks.REAP);
+                                // If score has changed, rebroadcast to NetworkFactories. b/17726566
                                 sendUpdatedScoreToFactories(nai);
                             }
                         }
@@ -2150,6 +2168,7 @@
             if (isDefaultNetwork(nai)) {
                 mDefaultInetConditionPublished = 0;
             }
+            notifyIfacesChanged();
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
@@ -2194,7 +2213,8 @@
             }
             for (NetworkAgentInfo networkToActivate : toActivate) {
                 unlinger(networkToActivate);
-                rematchNetworkAndRequests(networkToActivate, false);
+                rematchNetworkAndRequests(networkToActivate, NascentState.NOT_JUST_VALIDATED,
+                        ReapUnvalidatedNetworks.DONT_REAP);
             }
         }
     }
@@ -2385,12 +2405,6 @@
                     sendStickyBroadcast(intent);
                     break;
                 }
-                case EVENT_SET_POLICY_DATA_ENABLE: {
-                    final int networkType = msg.arg1;
-                    final boolean enabled = msg.arg2 == ENABLED;
-                    handleSetPolicyDataEnable(networkType, enabled);
-                    break;
-                }
                 case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
                     int tag = mEnableFailFastMobileDataTag.get();
                     if (msg.arg1 == tag) {
@@ -3376,6 +3390,7 @@
 
         final NetworkRequest request;
         final PendingIntent mPendingIntent;
+        boolean mPendingIntentSent;
         private final IBinder mBinder;
         final int mPid;
         final int mUid;
@@ -3497,6 +3512,12 @@
         return networkRequest;
     }
 
+    private void releasePendingNetworkRequestWithDelay(PendingIntent operation) {
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
+                getCallingUid(), 0, operation), mReleasePendingIntentDelayMs);
+    }
+
     @Override
     public void releasePendingNetworkRequest(PendingIntent operation) {
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
@@ -3642,6 +3663,7 @@
         if (isDefaultNetwork(networkAgent)) handleApplyDefaultProxy(newLp.getHttpProxy());
         // TODO - move this check to cover the whole function
         if (!Objects.equals(newLp, oldLp)) {
+            notifyIfacesChanged();
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
         }
     }
@@ -3779,7 +3801,6 @@
         mNumDnsEntries = last;
     }
 
-
     private void updateCapabilities(NetworkAgentInfo networkAgent,
             NetworkCapabilities networkCapabilities) {
         if (!Objects.equals(networkAgent.networkCapabilities, networkCapabilities)) {
@@ -3810,10 +3831,11 @@
 
     private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
             int notificationType) {
-        if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
+        if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
             Intent intent = new Intent();
             intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
             intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+            nri.mPendingIntentSent = true;
             sendIntent(nri.mPendingIntent, intent);
         }
         // else not handled
@@ -3837,7 +3859,9 @@
             String resultData, Bundle resultExtras) {
         if (DBG) log("Finished sending " + pendingIntent);
         mPendingIntentWakeLock.release();
-        releasePendingNetworkRequest(pendingIntent);
+        // Release with a delay so the receiving client has an opportunity to put in its
+        // own request.
+        releasePendingNetworkRequestWithDelay(pendingIntent);
     }
 
     private void callCallbackForRequest(NetworkRequestInfo nri,
@@ -3920,15 +3944,16 @@
     //   satisfied by newNetwork, and reassigns to newNetwork
     //   any such requests for which newNetwork is the best.
     //
-    // - Lingers any Networks that as a result are no longer
+    // - Lingers any validated Networks that as a result are no longer
     //   needed. A network is needed if it is the best network for
     //   one or more NetworkRequests, or if it is a VPN.
     //
     // - Tears down newNetwork if it just became validated
-    //   (i.e. nascent==true) but turns out to be unneeded.
-    //   Does not tear down newNetwork if it is unvalidated,
-    //   because future validation may improve newNetwork's
-    //   score enough that it is needed.
+    //   (i.e. nascent==JUST_VALIDATED) but turns out to be unneeded.
+    //
+    // - If reapUnvalidatedNetworks==REAP, tears down unvalidated
+    //   networks that have no chance (i.e. even if validated)
+    //   of becoming the highest scoring network.
     //
     // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy,
     // it does not remove NetworkRequests that other Networks could better satisfy.
@@ -3936,16 +3961,18 @@
     // This function should be used when possible instead of {@code rematchAllNetworksAndRequests}
     // as it performs better by a factor of the number of Networks.
     //
+    // @param newNetwork is the network to be matched against NetworkRequests.
     // @param nascent indicates if newNetwork just became validated, in which case it should be
-    //               torn down if unneeded.  If nascent is false, no action is taken if newNetwork
-    //               is found to be unneeded by this call.  Presumably, in this case, either:
-    //               - newNetwork is unvalidated (and left alive), or
-    //               - the NetworkRequests keeping newNetwork alive have been transitioned to
-    //                 another higher scoring network by another call to rematchNetworkAndRequests()
-    //                 and this other call also lingered newNetwork.
-    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, boolean nascent) {
+    //               torn down if unneeded.
+    // @param reapUnvalidatedNetworks indicates if an additional pass over all networks should be
+    //               performed to tear down unvalidated networks that have no chance (i.e. even if
+    //               validated) of becoming the highest scoring network.
+    private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, NascentState nascent,
+            ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
         if (!newNetwork.created) return;
-        if (nascent && !newNetwork.validated) loge("ERROR: nascent network not validated.");
+        if (nascent == NascentState.JUST_VALIDATED && !newNetwork.validated) {
+            loge("ERROR: nascent network not validated.");
+        }
         boolean keep = newNetwork.isVPN();
         boolean isNewDefault = false;
         if (DBG) log("rematching " + newNetwork.name());
@@ -4092,17 +4119,44 @@
             if (newNetwork.isVPN()) {
                 mLegacyTypeTracker.add(TYPE_VPN, newNetwork);
             }
-        } else if (nascent) {
+        } else if (nascent == NascentState.JUST_VALIDATED) {
             // Only tear down newly validated networks here.  Leave unvalidated to either become
-            // validated (and get evaluated against peers, one losing here) or
-            // NetworkMonitor reports a bad network and we tear it down then.
-            // Networks that have been up for a while and are validated should be torn down via
-            // the lingering process so communication on that network is given time to wrap up.
-            // TODO: Could teardown unvalidated networks when their NetworkCapabilities
-            // satisfy no NetworkRequests.
+            // validated (and get evaluated against peers, one losing here), or get reaped (see
+            // reapUnvalidatedNetworks) if they have no chance of becoming the highest scoring
+            // network.  Networks that have been up for a while and are validated should be torn
+            // down via the lingering process so communication on that network is given time to
+            // wrap up.
             if (DBG) log("Validated network turns out to be unwanted.  Tear it down.");
             teardownUnneededNetwork(newNetwork);
         }
+        if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
+            for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+                if (!nai.created || nai.validated || nai.isVPN()) continue;
+                boolean reap = true;
+                for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+                    // If this Network is already the highest scoring Network for a request, or if
+                    // there is hope for it to become one if it validated, then don't reap it.
+                    if (nri.isRequest && nai.satisfies(nri.request) &&
+                            (nai.networkRequests.get(nri.request.requestId) != null ||
+                            // Note that this catches two important cases:
+                            // 1. Unvalidated cellular will not be reaped when unvalidated WiFi
+                            //    is currently satisfying the request.  This is desirable when
+                            //    cellular ends up validating but WiFi does not.
+                            // 2. Unvalidated WiFi will not be reaped when validated cellular
+                            //    is currently satsifying the request.  This is desirable when
+                            //    WiFi ends up validating and out scoring cellular.
+                            mNetworkForRequestId.get(nri.request.requestId).getCurrentScore() <
+                                    nai.getCurrentScoreAsValidated())) {
+                        reap = false;
+                        break;
+                    }
+                }
+                if (reap) {
+                    if (DBG) log("Reaping " + nai.name());
+                    teardownUnneededNetwork(nai);
+                }
+            }
+        }
     }
 
     // Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
@@ -4123,10 +4177,17 @@
         // can only add more NetworkRequests satisfied by "changed", and this is exactly what
         // rematchNetworkAndRequests() handles.
         if (changed != null && oldScore < changed.getCurrentScore()) {
-            rematchNetworkAndRequests(changed, false);
+            rematchNetworkAndRequests(changed, NascentState.NOT_JUST_VALIDATED,
+                    ReapUnvalidatedNetworks.REAP);
         } else {
-            for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-                rematchNetworkAndRequests(nai, false);
+            for (Iterator i = mNetworkAgentInfos.values().iterator(); i.hasNext(); ) {
+                rematchNetworkAndRequests((NetworkAgentInfo)i.next(),
+                        NascentState.NOT_JUST_VALIDATED,
+                        // Only reap the last time through the loop.  Reaping before all rematching
+                        // is complete could incorrectly teardown a network that hasn't yet been
+                        // rematched.
+                        i.hasNext() ? ReapUnvalidatedNetworks.DONT_REAP
+                                : ReapUnvalidatedNetworks.REAP);
             }
         }
     }
@@ -4193,6 +4254,7 @@
             }
             networkAgent.created = true;
             updateLinkProperties(networkAgent, null);
+            notifyIfacesChanged();
             notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
             networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
             if (networkAgent.isVPN()) {
@@ -4208,7 +4270,8 @@
                 // TODO: support proxy per network.
             }
             // Consider network even though it is not yet validated.
-            rematchNetworkAndRequests(networkAgent, false);
+            rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
+                    ReapUnvalidatedNetworks.REAP);
         } else if (state == NetworkInfo.State.DISCONNECTED ||
                 state == NetworkInfo.State.SUSPENDED) {
             networkAgent.asyncChannel.disconnect();
@@ -4268,7 +4331,7 @@
         info.setType(type);
         if (connected) {
             info.setDetailedState(DetailedState.CONNECTED, null, info.getExtraInfo());
-            sendConnectedBroadcast(info);
+            sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
         } else {
             info.setDetailedState(DetailedState.DISCONNECTED, null, info.getExtraInfo());
             Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
@@ -4299,9 +4362,10 @@
             final Intent immediateIntent = new Intent(intent);
             immediateIntent.setAction(CONNECTIVITY_ACTION_IMMEDIATE);
             sendStickyBroadcast(immediateIntent);
-            sendStickyBroadcast(intent);
+            sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
             if (newDefaultAgent != null) {
-                sendConnectedBroadcast(newDefaultAgent.networkInfo);
+                sendConnectedBroadcastDelayed(newDefaultAgent.networkInfo,
+                getConnectivityChangeDelay());
             }
         }
     }
@@ -4334,6 +4398,16 @@
         return "UNKNOWN";
     }
 
+    /**
+     * Notify other system services that set of active ifaces has changed.
+     */
+    private void notifyIfacesChanged() {
+        try {
+            mStatsService.forceUpdateIfaces();
+        } catch (Exception ignored) {
+        }
+    }
+
     @Override
     public boolean addVpnAddress(String address, int prefixLength) {
         throwIfLockdownEnabled();
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 6feeb7c..779a834 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -106,9 +106,7 @@
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
     }
 
-    // Get the current score for this Network.  This may be modified from what the
-    // NetworkAgent sent, as it has modifiers applied to it.
-    public int getCurrentScore() {
+    private int getCurrentScore(boolean pretendValidated) {
         // TODO: We may want to refactor this into a NetworkScore class that takes a base score from
         // the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
         // score.  The NetworkScore class would provide a nice place to centralize score constants
@@ -116,7 +114,7 @@
 
         int score = currentScore;
 
-        if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
+        if (!validated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
         if (score < 0) score = 0;
 
         if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
@@ -124,6 +122,18 @@
         return score;
     }
 
+    // Get the current score for this Network.  This may be modified from what the
+    // NetworkAgent sent, as it has modifiers applied to it.
+    public int getCurrentScore() {
+        return getCurrentScore(false);
+    }
+
+    // Get the current score for this Network as if it was validated.  This may be modified from
+    // what the NetworkAgent sent, as it has modifiers applied to it.
+    public int getCurrentScoreAsValidated() {
+        return getCurrentScore(true);
+    }
+
     public void setCurrentScore(int newScore) {
         currentScore = newScore;
     }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index f9a03fc..7383478 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -1023,7 +1023,7 @@
         info.setDetailedState(DetailedState.CONNECTED, null, null);
         final LinkProperties prop = new LinkProperties();
         prop.setInterfaceName(iface);
-        return new NetworkState(info, prop, null, null);
+        return new NetworkState(info, prop, null, null, null, null);
     }
 
     private NetworkStats buildEmptyStats() {