Merge "Fix findConnectionTypeForIface." 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 4d7b2ef..54afe99 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -756,10 +756,20 @@
         return mNextNetworkRequestId++;
     }
 
-    private synchronized int nextNetId() {
-        int netId = mNextNetId;
-        if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
-        return netId;
+    private void assignNextNetId(NetworkAgentInfo nai) {
+        synchronized (mNetworkForNetId) {
+            for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
+                int netId = mNextNetId;
+                if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+                // Make sure NetID unused.  http://b/16815182
+                if (mNetworkForNetId.get(netId) == null) {
+                    nai.network = new Network(netId);
+                    mNetworkForNetId.put(netId, nai);
+                    return;
+                }
+            }
+        }
+        throw new IllegalStateException("No free netIds");
     }
 
     private int getConnectivityChangeDelay() {
@@ -1733,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) {
@@ -1862,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);
                     }
@@ -2089,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();
@@ -2105,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);
             }
         }
@@ -2663,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
@@ -3492,10 +3544,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);
@@ -4106,7 +4161,7 @@
             int currentScore, NetworkMisc networkMisc) {
         enforceConnectivityInternalPermission();
 
-        NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(), nextNetId(),
+        NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
             networkMisc);
@@ -4120,9 +4175,7 @@
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        synchronized (mNetworkForNetId) {
-            mNetworkForNetId.put(na.network.netId, na);
-        }
+        assignNextNetId(na);
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
@@ -4388,6 +4441,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) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 4eb2ef1..5a97aee 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,7 @@
  */
 public class NetworkAgentInfo {
     public NetworkInfo networkInfo;
-    public final Network network;
+    public Network network;
     public LinkProperties linkProperties;
     public NetworkCapabilities networkCapabilities;
     public int currentScore;
@@ -55,12 +55,12 @@
     public final Messenger messenger;
     public final AsyncChannel asyncChannel;
 
-    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, int netId, NetworkInfo info,
+    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc) {
         this.messenger = messenger;
         asyncChannel = ac;
-        network = new Network(netId);
+        network = null;
         networkInfo = info;
         linkProperties = lp;
         networkCapabilities = nc;
@@ -87,6 +87,7 @@
 
     public String name() {
         return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ") - " + network.toString() + "]";
+                networkInfo.getSubtypeName() + ") - " +
+                (network == null ? "null" : network.toString()) + "]";
     }
 }