Merge "Avoid reusing a currently active NetID." into lmp-dev
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 0814e0f..b2fc3be 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -108,6 +108,8 @@
 
     boolean prepareVpn(String oldPackage, String newPackage);
 
+    void setVpnPackageAuthorization(boolean authorized);
+
     ParcelFileDescriptor establishVpn(in VpnConfig config);
 
     VpnConfig getVpnConfig();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8354137..96f8324 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1743,6 +1743,19 @@
         }
     }
 
+    private boolean isLiveNetworkAgent(NetworkAgentInfo nai, String msg) {
+        final NetworkAgentInfo officialNai;
+        synchronized (mNetworkForNetId) {
+            officialNai = mNetworkForNetId.get(nai.network.netId);
+        }
+        if (officialNai != null && officialNai.equals(nai)) return true;
+        if (officialNai != null || VDBG) {
+            loge(msg + " - validateNetworkAgent found mismatched netId: " + officialNai +
+                " - " + nai);
+        }
+        return false;
+    }
+
     // must be stateless - things change under us.
     private class NetworkStateTrackerHandler extends Handler {
         public NetworkStateTrackerHandler(Looper looper) {
@@ -1872,23 +1885,30 @@
                 }
                 case NetworkMonitor.EVENT_NETWORK_VALIDATED: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    handleConnectionValidated(nai);
+                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
+                        handleConnectionValidated(nai);
+                    }
                     break;
                 }
                 case NetworkMonitor.EVENT_NETWORK_LINGER_COMPLETE: {
                     NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
-                    handleLingerComplete(nai);
+                    if (isLiveNetworkAgent(nai, "EVENT_NETWORK_LINGER_COMPLETE")) {
+                        handleLingerComplete(nai);
+                    }
                     break;
                 }
                 case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+                    NetworkAgentInfo nai = null;
+                    synchronized (mNetworkForNetId) {
+                        nai = mNetworkForNetId.get(msg.arg2);
+                    }
+                    if (nai == null) {
+                        loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+                        break;
+                    }
                     if (msg.arg1 == 0) {
                         setProvNotificationVisibleIntent(false, msg.arg2, 0, null, null);
                     } else {
-                        NetworkAgentInfo nai = mNetworkForNetId.get(msg.arg2);
-                        if (nai == null) {
-                            loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
-                            break;
-                        }
                         setProvNotificationVisibleIntent(true, msg.arg2, nai.networkInfo.getType(),
                                 nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
                     }
@@ -2099,13 +2119,21 @@
             if (newCap.satisfiedByNetworkCapabilities(network.networkCapabilities)) {
                 if (VDBG) log("apparently satisfied.  currentScore=" + network.currentScore);
                 if ((bestNetwork == null) || bestNetwork.currentScore < network.currentScore) {
-                    bestNetwork = network;
+                    if (!nri.isRequest) {
+                        // Not setting bestNetwork here as a listening NetworkRequest may be
+                        // satisfied by multiple Networks.  Instead the request is added to
+                        // each satisfying Network and notified about each.
+                        network.addRequest(nri.request);
+                        notifyNetworkCallback(network, nri);
+                    } else {
+                        bestNetwork = network;
+                    }
                 }
             }
         }
         if (bestNetwork != null) {
             if (VDBG) log("using " + bestNetwork.name());
-            if (nri.isRequest && bestNetwork.networkInfo.isConnected()) {
+            if (bestNetwork.networkInfo.isConnected()) {
                 // Cancel any lingering so the linger timeout doesn't teardown this network
                 // even though we have a request for it.
                 bestNetwork.networkLingered.clear();
@@ -2115,7 +2143,7 @@
             mNetworkForRequestId.put(nri.request.requestId, bestNetwork);
             notifyNetworkCallback(bestNetwork, nri);
             score = bestNetwork.currentScore;
-            if (nri.isRequest && nri.request.legacyType != TYPE_NONE) {
+            if (nri.request.legacyType != TYPE_NONE) {
                 mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
             }
         }
@@ -2673,6 +2701,20 @@
     }
 
     /**
+     * Set whether the current VPN package has the ability to launch VPNs without
+     * user intervention. This method is used by system UIs and not available
+     * in ConnectivityManager. Permissions are checked in Vpn class.
+     * @hide
+     */
+    @Override
+    public void setVpnPackageAuthorization(boolean authorized) {
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            mVpns.get(user).setPackageAuthorization(authorized);
+        }
+    }
+
+    /**
      * Configure a TUN interface and return its file descriptor. Parameters
      * are encoded and opaque to this class. This method is used by VpnBuilder
      * and not available in ConnectivityManager. Permissions are checked in
@@ -3500,10 +3542,13 @@
             mIsProvisioningNetwork.set(false);
             // Check for  apps that can handle provisioning first
             Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
-            provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
-                    + mTelephonyManager.getSimOperator());
-            if (mContext.getPackageManager().resolveActivity(provisioningIntent, 0 /* flags */)
-                    != null) {
+            List<String> carrierPackages =
+                    mTelephonyManager.getCarrierPackageNamesForBroadcastIntent(provisioningIntent);
+            if (carrierPackages != null && !carrierPackages.isEmpty()) {
+                if (carrierPackages.size() != 1) {
+                    if (DBG) log("Multiple matching carrier apps found, launching the first.");
+                }
+                provisioningIntent.setPackage(carrierPackages.get(0));
                 provisioningIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
                         Intent.FLAG_ACTIVITY_NEW_TASK);
                 mContext.startActivity(provisioningIntent);
@@ -4394,6 +4439,10 @@
             if (VDBG) log("  checking if request is satisfied: " + nri.request);
             if (nri.request.networkCapabilities.satisfiedByNetworkCapabilities(
                     newNetwork.networkCapabilities)) {
+                if (!nri.isRequest) {
+                    newNetwork.addRequest(nri.request);
+                    continue;
+                }
                 // next check if it's better than any current network we're using for
                 // this request
                 if (VDBG) {