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