Allow apps with ACCESS_WIFI_STATE to listen for wifi networks

The compatibility measure introduced in bug 20081183 for apps
that connect to a Wi-Fi network without Internet access and then
expect to be able to use that network requires that such apps
register a NetworkCallback so that their WifiManager can pin them
to whatever wifi Network connects.

Currently, registering the callback requires ACCESS_NETWORK_STATE
and the app may not have that permission. Allow registering wifi
(only) callbacks if the app has ACCESS_WIFI_STATE.

If the app does not have ACCESS_WIFI_STATE (unlikely, since
CHANGE_WIFI_STATE is not very useful without ACCESS_WIFI_STATE),
then don't enable the compatibility measure.

Bug: 20081183
Bug: 20423580
Change-Id: Iad328d30c2d170dead883868fece3d922da68f6f
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9ec1c6a..7d8e9de 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3518,10 +3518,34 @@
                 getCallingUid(), 0, operation));
     }
 
+    // In order to implement the compatibility measure for pre-M apps that call
+    // WifiManager.enableNetwork(..., true) without also binding to that network explicitly,
+    // WifiManager registers a network listen for the purpose of calling setProcessDefaultNetwork.
+    // This ensures it has permission to do so.
+    private boolean hasWifiNetworkListenPermission(NetworkCapabilities nc) {
+        if (nc == null) {
+            return false;
+        }
+        int[] transportTypes = nc.getTransportTypes();
+        if (transportTypes.length != 1 || transportTypes[0] != NetworkCapabilities.TRANSPORT_WIFI) {
+            return false;
+        }
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ACCESS_WIFI_STATE,
+                    "ConnectivityService");
+        } catch (SecurityException e) {
+            return false;
+        }
+        return true;
+    }
+
     @Override
     public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, IBinder binder) {
-        enforceAccessPermission();
+        if (!hasWifiNetworkListenPermission(networkCapabilities)) {
+            enforceAccessPermission();
+        }
 
         NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
                 networkCapabilities), TYPE_NONE, nextNetworkRequestId());