1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.net; 18 19 import android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.ConnectivityManager.NetworkCallback; 22 import android.net.Network; 23 import android.net.NetworkRequest; 24 import android.util.Log; 25 26 import com.android.internal.annotations.GuardedBy; 27 import com.android.internal.annotations.VisibleForTesting; 28 29 /** 30 * A class that pins a process to the first network that satisfies a particular NetworkRequest. 31 * 32 * We use this to maintain compatibility with pre-M apps that call WifiManager.enableNetwork() 33 * to connect to a Wi-Fi network that has no Internet access, and then assume that they will be 34 * able to use that network because it's the system default. 35 * 36 * In order to maintain compatibility with apps that call setProcessDefaultNetwork themselves, 37 * we try not to set the default network unless they have already done so, and we try not to 38 * clear the default network unless we set it ourselves. 39 * 40 * This should maintain behaviour that's compatible with L, which would pin the whole system to 41 * any wifi network that was created via enableNetwork(..., true) until that network 42 * disconnected. 43 * 44 * Note that while this hack allows network traffic to flow, it is quite limited. For example: 45 * 46 * 1. setProcessDefaultNetwork only affects this process, so: 47 * - Any subprocesses spawned by this process will not be pinned to Wi-Fi. 48 * - If this app relies on any other apps on the device also being on Wi-Fi, that won't work 49 * either, because other apps on the device will not be pinned. 50 * 2. The behaviour of other APIs is not modified. For example: 51 * - getActiveNetworkInfo will return the system default network, not Wi-Fi. 52 * - There will be no CONNECTIVITY_ACTION broadcasts about TYPE_WIFI. 53 * - getProcessDefaultNetwork will not return null, so if any apps are relying on that, they 54 * will be surprised as well. 55 * 56 * This class is a per-process singleton because the process default network is a per-process 57 * singleton. 58 * 59 */ 60 public class NetworkPinner extends NetworkCallback { 61 62 private static final String TAG = NetworkPinner.class.getSimpleName(); 63 64 @VisibleForTesting 65 protected static final Object sLock = new Object(); 66 67 @GuardedBy("sLock") 68 private static ConnectivityManager sCM; 69 @GuardedBy("sLock") 70 private static Callback sCallback; 71 @VisibleForTesting 72 @GuardedBy("sLock") 73 protected static Network sNetwork; 74 maybeInitConnectivityManager(Context context)75 private static void maybeInitConnectivityManager(Context context) { 76 // TODO: what happens if an app calls a WifiManager API before ConnectivityManager is 77 // registered? Can we fix this by starting ConnectivityService before WifiService? 78 if (sCM == null) { 79 // Getting a ConnectivityManager does not leak the calling context, because it stores 80 // the application context and not the calling context. 81 sCM = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 82 if (sCM == null) { 83 throw new IllegalStateException("Bad luck, ConnectivityService not started."); 84 } 85 } 86 } 87 88 private static class Callback extends NetworkCallback { 89 @Override onAvailable(Network network)90 public void onAvailable(Network network) { 91 synchronized(sLock) { 92 if (this != sCallback) return; 93 94 if (sCM.getBoundNetworkForProcess() == null && sNetwork == null) { 95 sCM.bindProcessToNetwork(network); 96 sNetwork = network; 97 Log.d(TAG, "Wifi alternate reality enabled on network " + network); 98 } 99 sLock.notify(); 100 } 101 } 102 103 @Override onLost(Network network)104 public void onLost(Network network) { 105 synchronized (sLock) { 106 if (this != sCallback) return; 107 108 if (network.equals(sNetwork) && network.equals(sCM.getBoundNetworkForProcess())) { 109 unpin(); 110 Log.d(TAG, "Wifi alternate reality disabled on network " + network); 111 } 112 sLock.notify(); 113 } 114 } 115 } 116 pin(Context context, NetworkRequest request)117 public static void pin(Context context, NetworkRequest request) { 118 synchronized (sLock) { 119 if (sCallback == null) { 120 maybeInitConnectivityManager(context); 121 sCallback = new Callback(); 122 try { 123 sCM.registerNetworkCallback(request, sCallback); 124 } catch (SecurityException e) { 125 Log.d(TAG, "Failed to register network callback", e); 126 sCallback = null; 127 } 128 } 129 } 130 } 131 unpin()132 public static void unpin() { 133 synchronized (sLock) { 134 if (sCallback != null) { 135 try { 136 sCM.bindProcessToNetwork(null); 137 sCM.unregisterNetworkCallback(sCallback); 138 } catch (SecurityException e) { 139 Log.d(TAG, "Failed to unregister network callback", e); 140 } 141 sCallback = null; 142 sNetwork = null; 143 } 144 } 145 } 146 } 147