Merge "Refactor wifi p2p's startDhcpServer function"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index fcaf03f..a7e03fc 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -743,7 +743,7 @@
      *        network type or {@code null} if the type is not
      *        supported by the device.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public NetworkInfo getNetworkInfo(int networkType) {
@@ -755,13 +755,34 @@
     }
 
     /**
+     * Returns connection status information about a particular
+     * Network.
+     *
+     * @param network {@link Network} specifying which network
+     *        in which you're interested.
+     * @return a {@link NetworkInfo} object for the requested
+     *        network or {@code null} if the {@code Network}
+     *        is not valid.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     */
+    public NetworkInfo getNetworkInfo(Network network) {
+        try {
+            return mService.getNetworkInfoForNetwork(network);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns connection status information about all network
      * types supported by the device.
      *
      * @return an array of {@link NetworkInfo} objects.  Check each
      * {@link NetworkInfo#getType} for which type each applies.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public NetworkInfo[] getAllNetworkInfo() {
@@ -773,6 +794,23 @@
     }
 
     /**
+     * Returns an array of all {@link Network} currently tracked by the
+     * framework.
+     *
+     * @return an array of {@link Network} objects.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     */
+    public Network[] getAllNetworks() {
+        try {
+            return mService.getAllNetworks();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns details about the Provisioning or currently active default data network. When
      * connected, this network is the default route for outgoing connections.
      * You should always check {@link NetworkInfo#isConnected()} before initiating
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 7d5dc41..b76fc38 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -48,7 +48,9 @@
     NetworkInfo getActiveNetworkInfo();
     NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
+    NetworkInfo getNetworkInfoForNetwork(in Network network);
     NetworkInfo[] getAllNetworkInfo();
+    Network[] getAllNetworks();
 
     NetworkInfo getProvisioningOrActiveNetworkInfo();
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0317138..6fc7c6b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1071,6 +1071,10 @@
      */
     private NetworkInfo getFilteredNetworkInfo(int networkType, int uid) {
         NetworkInfo info = getNetworkInfoForType(networkType);
+        return getFilteredNetworkInfo(info, networkType, uid);
+    }
+
+    private NetworkInfo getFilteredNetworkInfo(NetworkInfo info, int networkType, int uid) {
         if (isNetworkBlocked(networkType, uid)) {
             // network is blocked; clone and override state
             info = new NetworkInfo(info);
@@ -1176,6 +1180,24 @@
     }
 
     @Override
+    public NetworkInfo getNetworkInfoForNetwork(Network network) {
+        enforceAccessPermission();
+        if (network == null) return null;
+
+        final int uid = Binder.getCallingUid();
+        NetworkAgentInfo nai = null;
+        synchronized (mNetworkForNetId) {
+            nai = mNetworkForNetId.get(network.netId);
+        }
+        if (nai == null) return null;
+        synchronized (nai) {
+            if (nai.networkInfo == null) return null;
+
+            return getFilteredNetworkInfo(nai.networkInfo, nai.networkInfo.getType(), uid);
+        }
+    }
+
+    @Override
     public NetworkInfo[] getAllNetworkInfo() {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
@@ -1192,6 +1214,18 @@
     }
 
     @Override
+    public Network[] getAllNetworks() {
+        enforceAccessPermission();
+        final ArrayList<Network> result = new ArrayList();
+        synchronized (mNetworkForNetId) {
+            for (int i = 0; i < mNetworkForNetId.size(); i++) {
+                result.add(new Network(mNetworkForNetId.valueAt(i).network));
+            }
+        }
+        return result.toArray(new Network[result.size()]);
+    }
+
+    @Override
     public boolean isNetworkSupported(int networkType) {
         enforceAccessPermission();
         return (isNetworkTypeValid(networkType) && (getNetworkInfoForType(networkType) != null));
@@ -1223,16 +1257,31 @@
     @Override
     public LinkProperties getLinkProperties(Network network) {
         enforceAccessPermission();
-        NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
-        if (nai != null) return new LinkProperties(nai.linkProperties);
+        NetworkAgentInfo nai = null;
+        synchronized (mNetworkForNetId) {
+            nai = mNetworkForNetId.get(network.netId);
+        }
+
+        if (nai != null) {
+            synchronized (nai) {
+                return new LinkProperties(nai.linkProperties);
+            }
+        }
         return null;
     }
 
     @Override
     public NetworkCapabilities getNetworkCapabilities(Network network) {
         enforceAccessPermission();
-        NetworkAgentInfo nai = mNetworkForNetId.get(network.netId);
-        if (nai != null) return new NetworkCapabilities(nai.networkCapabilities);
+        NetworkAgentInfo nai = null;
+        synchronized (mNetworkForNetId) {
+            nai = mNetworkForNetId.get(network.netId);
+        }
+        if (nai != null) {
+            synchronized (nai) {
+                return new NetworkCapabilities(nai.networkCapabilities);
+            }
+        }
         return null;
     }
 
@@ -1777,8 +1826,10 @@
             }
             return false;
         }
-
-        DetailedState netState = nai.networkInfo.getDetailedState();
+        DetailedState netState;
+        synchronized (nai) {
+            netState = nai.networkInfo.getDetailedState();
+        }
 
         if ((netState != DetailedState.CONNECTED &&
                 netState != DetailedState.CAPTIVE_PORTAL_CHECK)) {
@@ -1792,9 +1843,13 @@
         final int uid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
         try {
-            LinkProperties lp = nai.linkProperties;
-            boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt,
-                    nai.network.netId, uid);
+            LinkProperties lp = null;
+            int netId = INVALID_NET_ID;
+            synchronized (nai) {
+                lp = nai.linkProperties;
+                netId = nai.network.netId;
+            }
+            boolean ok = modifyRouteToAddress(lp, addr, ADD, TO_DEFAULT_TABLE, exempt, netId, uid);
             if (DBG) log("requestRouteToHostAddress ok=" + ok);
             return ok;
         } finally {
@@ -3096,7 +3151,9 @@
                     } else {
                         if (VDBG) log("Update of Linkproperties for " + nai.name());
                         LinkProperties oldLp = nai.linkProperties;
-                        nai.linkProperties = (LinkProperties)msg.obj;
+                        synchronized (nai) {
+                            nai.linkProperties = (LinkProperties)msg.obj;
+                        }
                         updateLinkProperties(nai, oldLp);
                     }
                     break;
@@ -3131,6 +3188,16 @@
                     handleLingerComplete(nai);
                     break;
                 }
+                case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
+                    NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+                    if (nai == null) {
+                        loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
+                        break;
+                    }
+                    setProvNotificationVisibleIntent(msg.arg1 != 0, nai.networkInfo.getType(),
+                            nai.networkInfo.getExtraInfo(), (PendingIntent)msg.obj);
+                    break;
+                }
                 case NetworkStateTracker.EVENT_STATE_CHANGED: {
                     info = (NetworkInfo) msg.obj;
                     NetworkInfo.State state = info.getState();
@@ -3242,7 +3309,9 @@
                 loge("Error connecting NetworkAgent");
                 NetworkAgentInfo nai = mNetworkAgentInfos.remove(msg.replyTo);
                 if (nai != null) {
-                    mNetworkForNetId.remove(nai.network.netId);
+                    synchronized (mNetworkForNetId) {
+                        mNetworkForNetId.remove(nai.network.netId);
+                    }
                     mLegacyTypeTracker.remove(nai);
                 }
             }
@@ -3275,7 +3344,9 @@
             mNetworkAgentInfos.remove(msg.replyTo);
             updateClat(null, nai.linkProperties, nai);
             mLegacyTypeTracker.remove(nai);
-            mNetworkForNetId.remove(nai.network.netId);
+            synchronized (mNetworkForNetId) {
+                mNetworkForNetId.remove(nai.network.netId);
+            }
             // Since we've lost the network, go through all the requests that
             // it was satisfying and see if any other factory can satisfy them.
             final ArrayList<NetworkAgentInfo> toActivate = new ArrayList<NetworkAgentInfo>();
@@ -5044,6 +5115,40 @@
             log("setProvNotificationVisible: E visible=" + visible + " networkType=" + networkType
                 + " extraInfo=" + extraInfo + " url=" + url);
         }
+        Intent intent = null;
+        PendingIntent pendingIntent = null;
+        if (visible) {
+            switch (networkType) {
+                case ConnectivityManager.TYPE_WIFI:
+                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                            Intent.FLAG_ACTIVITY_NEW_TASK);
+                    pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+                    break;
+                case ConnectivityManager.TYPE_MOBILE:
+                case ConnectivityManager.TYPE_MOBILE_HIPRI:
+                    intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
+                    intent.putExtra("EXTRA_URL", url);
+                    intent.setFlags(0);
+                    pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+                    break;
+                default:
+                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
+                            Intent.FLAG_ACTIVITY_NEW_TASK);
+                    pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+                    break;
+            }
+        }
+        setProvNotificationVisibleIntent(visible, networkType, extraInfo, pendingIntent);
+    }
+
+    private void setProvNotificationVisibleIntent(boolean visible, int networkType,
+            String extraInfo, PendingIntent intent) {
+        if (DBG) {
+            log("setProvNotificationVisibleIntent: E visible=" + visible + " networkType=" +
+                networkType + " extraInfo=" + extraInfo);
+        }
 
         Resources r = Resources.getSystem();
         NotificationManager notificationManager = (NotificationManager) mContext
@@ -5053,7 +5158,6 @@
             CharSequence title;
             CharSequence details;
             int icon;
-            Intent intent;
             Notification notification = new Notification();
             switch (networkType) {
                 case ConnectivityManager.TYPE_WIFI:
@@ -5061,10 +5165,6 @@
                     details = r.getString(R.string.network_available_sign_in_detailed,
                             extraInfo);
                     icon = R.drawable.stat_notify_wifi_in_range;
-                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                            Intent.FLAG_ACTIVITY_NEW_TASK);
-                    notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
                     break;
                 case ConnectivityManager.TYPE_MOBILE:
                 case ConnectivityManager.TYPE_MOBILE_HIPRI:
@@ -5073,20 +5173,12 @@
                     // name has been added to it
                     details = mTelephonyManager.getNetworkOperatorName();
                     icon = R.drawable.stat_notify_rssi_in_range;
-                    intent = new Intent(CONNECTED_TO_PROVISIONING_NETWORK_ACTION);
-                    intent.putExtra("EXTRA_URL", url);
-                    intent.setFlags(0);
-                    notification.contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
                     break;
                 default:
                     title = r.getString(R.string.network_available_sign_in, 0);
                     details = r.getString(R.string.network_available_sign_in_detailed,
                             extraInfo);
                     icon = R.drawable.stat_notify_rssi_in_range;
-                    intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
-                    intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
-                            Intent.FLAG_ACTIVITY_NEW_TASK);
-                    notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
                     break;
             }
 
@@ -5095,6 +5187,7 @@
             notification.flags = Notification.FLAG_AUTO_CANCEL;
             notification.tickerText = title;
             notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
+            notification.contentIntent = intent;
 
             try {
                 notificationManager.notify(NOTIFICATION_ID, networkType, notification);
@@ -5570,7 +5663,9 @@
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        mNetworkForNetId.put(na.network.netId, na);
+        synchronized (mNetworkForNetId) {
+            mNetworkForNetId.put(na.network.netId, na);
+        }
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
@@ -5715,7 +5810,9 @@
             NetworkCapabilities networkCapabilities) {
         // TODO - what else here?  Verify still satisfies everybody?
         // Check if satisfies somebody new?  call callbacks?
-        networkAgent.networkCapabilities = networkCapabilities;
+        synchronized (networkAgent) {
+            networkAgent.networkCapabilities = networkCapabilities;
+        }
     }
 
     private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {
@@ -5929,8 +6026,11 @@
 
     private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
         NetworkInfo.State state = newInfo.getState();
-        NetworkInfo oldInfo = networkAgent.networkInfo;
-        networkAgent.networkInfo = newInfo;
+        NetworkInfo oldInfo = null;
+        synchronized (networkAgent) {
+            oldInfo = networkAgent.networkInfo;
+            networkAgent.networkInfo = newInfo;
+        }
 
         if (oldInfo != null && oldInfo.getState() == state) {
             if (VDBG) log("ignoring duplicate network state non-change");
@@ -6059,9 +6159,12 @@
 
     private LinkProperties getLinkPropertiesForTypeInternal(int networkType) {
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
-        return (nai != null) ?
-                new LinkProperties(nai.linkProperties) :
-                new LinkProperties();
+        if (nai != null) {
+            synchronized (nai) {
+                return new LinkProperties(nai.linkProperties);
+            }
+        }
+        return new LinkProperties();
     }
 
     private NetworkInfo getNetworkInfoForType(int networkType) {
@@ -6080,8 +6183,11 @@
 
     private NetworkCapabilities getNetworkCapabilitiesForType(int networkType) {
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
-        return (nai != null) ?
-                new NetworkCapabilities(nai.networkCapabilities) :
-                new NetworkCapabilities();
+        if (nai != null) {
+            synchronized (nai) {
+                return new NetworkCapabilities(nai.networkCapabilities);
+            }
+        }
+        return new NetworkCapabilities();
     }
 }