1 /* 2 * Copyright 2017 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.wifi.hotspot2; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; 20 21 import android.content.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.net.ConnectivityManager; 26 import android.net.LinkProperties; 27 import android.net.Network; 28 import android.net.NetworkCapabilities; 29 import android.net.NetworkRequest; 30 import android.net.wifi.WifiConfiguration; 31 import android.net.wifi.WifiInfo; 32 import android.net.wifi.WifiManager; 33 import android.net.wifi.WifiSsid; 34 import android.os.Handler; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 /** 39 * Responsible for setup/monitor on Wi-Fi state and connection to the OSU AP. 40 */ 41 public class OsuNetworkConnection { 42 private static final String TAG = "PasspointOsuNetworkConnection"; 43 private static final int TIMEOUT_MS = 10000; 44 45 private final Context mContext; 46 47 private boolean mVerboseLoggingEnabled = false; 48 private WifiManager mWifiManager; 49 private ConnectivityManager mConnectivityManager; 50 private ConnectivityCallbacks mConnectivityCallbacks; 51 private Callbacks mCallbacks; 52 private Handler mHandler; 53 private Network mNetwork = null; 54 private boolean mConnected = false; 55 private int mNetworkId = -1; 56 private boolean mWifiEnabled = false; 57 58 /** 59 * Callbacks on Wi-Fi connection state changes. 60 */ 61 public interface Callbacks { 62 /** 63 * Invoked when network connection is established with IP connectivity. 64 * 65 * @param network {@link Network} associated with the connected network. 66 */ onConnected(Network network)67 void onConnected(Network network); 68 69 /** 70 * Invoked when the targeted network is disconnected. 71 */ onDisconnected()72 void onDisconnected(); 73 74 /** 75 * Invoked when a timer tracking connection request is not reset by successful connection. 76 */ onTimeOut()77 void onTimeOut(); 78 79 /** 80 * Invoked when Wifi is enabled. 81 */ onWifiEnabled()82 void onWifiEnabled(); 83 84 /** 85 * Invoked when Wifi is disabled. 86 */ onWifiDisabled()87 void onWifiDisabled(); 88 } 89 OsuNetworkConnection(Context context)90 public OsuNetworkConnection(Context context) { 91 mContext = context; 92 } 93 94 /** 95 * Called to initialize tracking of wifi state and network events by registering for the 96 * corresponding intents. 97 */ init(Handler handler)98 public void init(Handler handler) { 99 IntentFilter filter = new IntentFilter(); 100 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 101 BroadcastReceiver receiver = new BroadcastReceiver() { 102 @Override 103 public void onReceive(Context context, Intent intent) { 104 String action = intent.getAction(); 105 if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 106 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 107 WifiManager.WIFI_STATE_UNKNOWN); 108 if (state == WifiManager.WIFI_STATE_DISABLED && mWifiEnabled) { 109 mWifiEnabled = false; 110 if (mCallbacks != null) mCallbacks.onWifiDisabled(); 111 } 112 if (state == WifiManager.WIFI_STATE_ENABLED && !mWifiEnabled) { 113 mWifiEnabled = true; 114 if (mCallbacks != null) mCallbacks.onWifiEnabled(); 115 } 116 } 117 } 118 }; 119 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 120 mContext.registerReceiver(receiver, filter, null, handler); 121 mWifiEnabled = mWifiManager.isWifiEnabled(); 122 mConnectivityManager = 123 (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 124 mConnectivityCallbacks = new ConnectivityCallbacks(); 125 mHandler = handler; 126 } 127 128 /** 129 * Disconnect, if required in the two cases 130 * - still connected to the OSU AP 131 * - connection to OSU AP was requested and in progress 132 */ disconnectIfNeeded()133 public void disconnectIfNeeded() { 134 if (mNetworkId < 0) { 135 if (mVerboseLoggingEnabled) { 136 Log.v(TAG, "No connection to tear down"); 137 } 138 return; 139 } 140 mConnectivityManager.unregisterNetworkCallback(mConnectivityCallbacks); 141 mWifiManager.removeNetwork(mNetworkId); 142 mNetworkId = -1; 143 mNetwork = null; 144 mConnected = false; 145 } 146 147 /** 148 * Register for network and Wifi state events 149 * 150 * @param callbacks The callbacks to be invoked on network change events 151 */ setEventCallback(Callbacks callbacks)152 public void setEventCallback(Callbacks callbacks) { 153 mCallbacks = callbacks; 154 } 155 156 /** 157 * Connect to a OSU Wi-Fi network specified by the given SSID. The security type of the Wi-Fi 158 * network is either open or OSEN (OSU Server-only authenticated layer 2 Encryption Network). 159 * When network access identifier is provided, OSEN is used. 160 * 161 * @param ssid The SSID to connect to 162 * @param nai Network access identifier of the network 163 * @param friendlyName a friendly name of service provider 164 * 165 * @return boolean true if connection was successfully initiated 166 */ connect(WifiSsid ssid, String nai, String friendlyName)167 public boolean connect(WifiSsid ssid, String nai, String friendlyName) { 168 if (mConnected) { 169 if (mVerboseLoggingEnabled) { 170 // Already connected 171 Log.v(TAG, "Connect called twice"); 172 } 173 return true; 174 } 175 if (!mWifiEnabled) { 176 Log.w(TAG, "Wifi is not enabled"); 177 return false; 178 } 179 WifiConfiguration config = new WifiConfiguration(); 180 config.SSID = "\"" + ssid.toString() + "\""; 181 182 // To suppress Wi-Fi has no internet access notification. 183 config.noInternetAccessExpected = true; 184 185 // To suppress Wi-Fi Sign-in notification for captive portal. 186 config.osu = true; 187 188 // Do not save this network 189 config.ephemeral = true; 190 config.providerFriendlyName = friendlyName; 191 192 if (TextUtils.isEmpty(nai)) { 193 config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); 194 } else { 195 // TODO: Handle OSEN. 196 Log.w(TAG, "OSEN not supported"); 197 return false; 198 } 199 mNetworkId = mWifiManager.addNetwork(config); 200 if (mNetworkId < 0) { 201 Log.e(TAG, "Unable to add network"); 202 return false; 203 } 204 205 // NET_CAPABILITY_TRUSTED is added by builder by default. 206 // But for ephemeral network, the capability needs to be removed 207 // as wifi stack creates network agent without the capability. 208 // That could cause connectivity service not to find the matching agent. 209 NetworkRequest networkRequest = new NetworkRequest.Builder() 210 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 211 .removeCapability(NET_CAPABILITY_TRUSTED) 212 .build(); 213 mConnectivityManager.requestNetwork(networkRequest, mConnectivityCallbacks, mHandler, 214 TIMEOUT_MS); 215 216 // TODO(b/112195429): replace it with new connectivity API. 217 if (!mWifiManager.enableNetwork(mNetworkId, true)) { 218 Log.e(TAG, "Unable to enable network " + mNetworkId); 219 disconnectIfNeeded(); 220 return false; 221 } 222 223 if (mVerboseLoggingEnabled) { 224 Log.v(TAG, "Current network ID " + mNetworkId); 225 } 226 return true; 227 } 228 229 /** 230 * Method to update logging level in this class 231 * 232 * @param verbose more than 0 enables verbose logging 233 */ enableVerboseLogging(int verbose)234 public void enableVerboseLogging(int verbose) { 235 mVerboseLoggingEnabled = verbose > 0 ? true : false; 236 } 237 238 private class ConnectivityCallbacks extends ConnectivityManager.NetworkCallback { 239 @Override onAvailable(Network network)240 public void onAvailable(Network network) { 241 WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); 242 if (wifiInfo == null) { 243 Log.w(TAG, "wifiInfo is not valid"); 244 return; 245 } 246 if (mNetworkId < 0 || mNetworkId != wifiInfo.getNetworkId()) { 247 Log.w(TAG, "Irrelevant network available notification for netId: " 248 + wifiInfo.getNetworkId()); 249 return; 250 } 251 mNetwork = network; 252 mConnected = true; 253 } 254 255 @Override onLinkPropertiesChanged(Network network, LinkProperties linkProperties)256 public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) { 257 if (mVerboseLoggingEnabled) { 258 Log.v(TAG, "onLinkPropertiesChanged for network=" + network 259 + " isProvisioned?" + linkProperties.isProvisioned()); 260 } 261 if (mNetwork == null) { 262 Log.w(TAG, "ignore onLinkPropertyChanged event for null network"); 263 return; 264 } 265 if (linkProperties.isProvisioned()) { 266 if (mCallbacks != null) { 267 mCallbacks.onConnected(network); 268 } 269 } 270 } 271 272 @Override onUnavailable()273 public void onUnavailable() { 274 if (mVerboseLoggingEnabled) { 275 Log.v(TAG, "onUnvailable "); 276 } 277 if (mCallbacks != null) { 278 mCallbacks.onTimeOut(); 279 } 280 } 281 282 @Override onLost(Network network)283 public void onLost(Network network) { 284 if (mVerboseLoggingEnabled) { 285 Log.v(TAG, "onLost " + network); 286 } 287 if (network != mNetwork) { 288 Log.w(TAG, "Irrelevant network lost notification"); 289 return; 290 } 291 if (mCallbacks != null) { 292 mCallbacks.onDisconnected(); 293 } 294 } 295 } 296 } 297 298