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.wifi; 18 19 import static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC; 20 import static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL; 21 import static com.android.server.wifi.util.ApConfigUtil.SUCCESS; 22 23 import android.net.InterfaceConfiguration; 24 import android.net.wifi.IApInterface; 25 import android.net.wifi.WifiConfiguration; 26 import android.net.wifi.WifiConfiguration.KeyMgmt; 27 import android.net.wifi.WifiManager; 28 import android.os.INetworkManagementService; 29 import android.os.Looper; 30 import android.os.Message; 31 import android.os.RemoteException; 32 import android.util.Log; 33 34 import com.android.internal.util.State; 35 import com.android.internal.util.StateMachine; 36 import com.android.server.net.BaseNetworkObserver; 37 import com.android.server.wifi.util.ApConfigUtil; 38 39 import java.nio.charset.StandardCharsets; 40 import java.util.Locale; 41 42 /** 43 * Manage WiFi in AP mode. 44 * The internal state machine runs under "WifiStateMachine" thread context. 45 */ 46 public class SoftApManager implements ActiveModeManager { 47 private static final String TAG = "SoftApManager"; 48 49 private final WifiNative mWifiNative; 50 51 private final String mCountryCode; 52 53 private final SoftApStateMachine mStateMachine; 54 55 private final Listener mListener; 56 57 private final IApInterface mApInterface; 58 59 private final INetworkManagementService mNwService; 60 private final WifiApConfigStore mWifiApConfigStore; 61 62 private final WifiMetrics mWifiMetrics; 63 64 private WifiConfiguration mApConfig; 65 66 /** 67 * Listener for soft AP state changes. 68 */ 69 public interface Listener { 70 /** 71 * Invoke when AP state changed. 72 * @param state new AP state 73 * @param failureReason reason when in failed state 74 */ 75 void onStateChanged(int state, int failureReason); 76 } 77 78 public SoftApManager(Looper looper, 79 WifiNative wifiNative, 80 String countryCode, 81 Listener listener, 82 IApInterface apInterface, 83 INetworkManagementService nms, 84 WifiApConfigStore wifiApConfigStore, 85 WifiConfiguration config, 86 WifiMetrics wifiMetrics) { 87 mStateMachine = new SoftApStateMachine(looper); 88 89 mWifiNative = wifiNative; 90 mCountryCode = countryCode; 91 mListener = listener; 92 mApInterface = apInterface; 93 mNwService = nms; 94 mWifiApConfigStore = wifiApConfigStore; 95 if (config == null) { 96 mApConfig = mWifiApConfigStore.getApConfiguration(); 97 } else { 98 mApConfig = config; 99 } 100 mWifiMetrics = wifiMetrics; 101 } 102 103 /** 104 * Start soft AP with the supplied config. 105 */ 106 public void start() { 107 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig); 108 } 109 110 /** 111 * Stop soft AP. 112 */ 113 public void stop() { 114 mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); 115 } 116 117 /** 118 * Update AP state. 119 * @param state new AP state 120 * @param reason Failure reason if the new AP state is in failure state 121 */ 122 private void updateApState(int state, int reason) { 123 if (mListener != null) { 124 mListener.onStateChanged(state, reason); 125 } 126 } 127 128 /** 129 * Start a soft AP instance with the given configuration. 130 * @param config AP configuration 131 * @return integer result code 132 */ 133 private int startSoftAp(WifiConfiguration config) { 134 if (config == null || config.SSID == null) { 135 Log.e(TAG, "Unable to start soft AP without valid configuration"); 136 return ERROR_GENERIC; 137 } 138 139 // Make a copy of configuration for updating AP band and channel. 140 WifiConfiguration localConfig = new WifiConfiguration(config); 141 142 int result = ApConfigUtil.updateApChannelConfig( 143 mWifiNative, mCountryCode, 144 mWifiApConfigStore.getAllowed2GChannel(), localConfig); 145 if (result != SUCCESS) { 146 Log.e(TAG, "Failed to update AP band and channel"); 147 return result; 148 } 149 150 // Setup country code if it is provided. 151 if (mCountryCode != null) { 152 // Country code is mandatory for 5GHz band, return an error if failed to set 153 // country code when AP is configured for 5GHz band. 154 if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT)) 155 && config.apBand == WifiConfiguration.AP_BAND_5GHZ) { 156 Log.e(TAG, "Failed to set country code, required for setting up " 157 + "soft ap in 5GHz"); 158 return ERROR_GENERIC; 159 } 160 } 161 162 int encryptionType = getIApInterfaceEncryptionType(localConfig); 163 164 try { 165 // Note that localConfig.SSID is intended to be either a hex string or "double quoted". 166 // However, it seems that whatever is handing us these configurations does not obey 167 // this convention. 168 boolean success = mApInterface.writeHostapdConfig( 169 localConfig.SSID.getBytes(StandardCharsets.UTF_8), false, 170 localConfig.apChannel, encryptionType, 171 (localConfig.preSharedKey != null) 172 ? localConfig.preSharedKey.getBytes(StandardCharsets.UTF_8) 173 : new byte[0]); 174 if (!success) { 175 Log.e(TAG, "Failed to write hostapd configuration"); 176 return ERROR_GENERIC; 177 } 178 179 success = mApInterface.startHostapd(); 180 if (!success) { 181 Log.e(TAG, "Failed to start hostapd."); 182 return ERROR_GENERIC; 183 } 184 } catch (RemoteException e) { 185 Log.e(TAG, "Exception in starting soft AP: " + e); 186 } 187 188 Log.d(TAG, "Soft AP is started"); 189 190 return SUCCESS; 191 } 192 193 private static int getIApInterfaceEncryptionType(WifiConfiguration localConfig) { 194 int encryptionType; 195 switch (localConfig.getAuthType()) { 196 case KeyMgmt.NONE: 197 encryptionType = IApInterface.ENCRYPTION_TYPE_NONE; 198 break; 199 case KeyMgmt.WPA_PSK: 200 encryptionType = IApInterface.ENCRYPTION_TYPE_WPA; 201 break; 202 case KeyMgmt.WPA2_PSK: 203 encryptionType = IApInterface.ENCRYPTION_TYPE_WPA2; 204 break; 205 default: 206 // We really shouldn't default to None, but this was how NetworkManagementService 207 // used to do this. 208 encryptionType = IApInterface.ENCRYPTION_TYPE_NONE; 209 break; 210 } 211 return encryptionType; 212 } 213 214 /** 215 * Teardown soft AP. 216 */ 217 private void stopSoftAp() { 218 try { 219 mApInterface.stopHostapd(); 220 } catch (RemoteException e) { 221 Log.e(TAG, "Exception in stopping soft AP: " + e); 222 return; 223 } 224 Log.d(TAG, "Soft AP is stopped"); 225 } 226 227 private class SoftApStateMachine extends StateMachine { 228 // Commands for the state machine. 229 public static final int CMD_START = 0; 230 public static final int CMD_STOP = 1; 231 public static final int CMD_AP_INTERFACE_BINDER_DEATH = 2; 232 public static final int CMD_INTERFACE_STATUS_CHANGED = 3; 233 234 private final State mIdleState = new IdleState(); 235 private final State mStartedState = new StartedState(); 236 237 private final StateMachineDeathRecipient mDeathRecipient = 238 new StateMachineDeathRecipient(this, CMD_AP_INTERFACE_BINDER_DEATH); 239 240 private NetworkObserver mNetworkObserver; 241 242 private class NetworkObserver extends BaseNetworkObserver { 243 private final String mIfaceName; 244 245 NetworkObserver(String ifaceName) { 246 mIfaceName = ifaceName; 247 } 248 249 @Override 250 public void interfaceLinkStateChanged(String iface, boolean up) { 251 if (mIfaceName.equals(iface)) { 252 SoftApStateMachine.this.sendMessage( 253 CMD_INTERFACE_STATUS_CHANGED, up ? 1 : 0, 0, this); 254 } 255 } 256 } 257 258 SoftApStateMachine(Looper looper) { 259 super(TAG, looper); 260 261 addState(mIdleState); 262 addState(mStartedState); 263 264 setInitialState(mIdleState); 265 start(); 266 } 267 268 private class IdleState extends State { 269 @Override 270 public void enter() { 271 mDeathRecipient.unlinkToDeath(); 272 unregisterObserver(); 273 } 274 275 @Override 276 public boolean processMessage(Message message) { 277 switch (message.what) { 278 case CMD_START: 279 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0); 280 if (!mDeathRecipient.linkToDeath(mApInterface.asBinder())) { 281 mDeathRecipient.unlinkToDeath(); 282 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 283 WifiManager.SAP_START_FAILURE_GENERAL); 284 mWifiMetrics.incrementSoftApStartResult( 285 false, WifiManager.SAP_START_FAILURE_GENERAL); 286 break; 287 } 288 289 try { 290 mNetworkObserver = new NetworkObserver(mApInterface.getInterfaceName()); 291 mNwService.registerObserver(mNetworkObserver); 292 } catch (RemoteException e) { 293 mDeathRecipient.unlinkToDeath(); 294 unregisterObserver(); 295 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 296 WifiManager.SAP_START_FAILURE_GENERAL); 297 mWifiMetrics.incrementSoftApStartResult( 298 false, WifiManager.SAP_START_FAILURE_GENERAL); 299 break; 300 } 301 302 int result = startSoftAp((WifiConfiguration) message.obj); 303 if (result != SUCCESS) { 304 int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; 305 if (result == ERROR_NO_CHANNEL) { 306 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 307 } 308 mDeathRecipient.unlinkToDeath(); 309 unregisterObserver(); 310 updateApState(WifiManager.WIFI_AP_STATE_FAILED, failureReason); 311 mWifiMetrics.incrementSoftApStartResult(false, failureReason); 312 break; 313 } 314 315 transitionTo(mStartedState); 316 break; 317 default: 318 // Ignore all other commands. 319 break; 320 } 321 322 return HANDLED; 323 } 324 325 private void unregisterObserver() { 326 if (mNetworkObserver == null) { 327 return; 328 } 329 try { 330 mNwService.unregisterObserver(mNetworkObserver); 331 } catch (RemoteException e) { } 332 mNetworkObserver = null; 333 } 334 } 335 336 private class StartedState extends State { 337 private boolean mIfaceIsUp; 338 339 private void onUpChanged(boolean isUp) { 340 if (isUp == mIfaceIsUp) { 341 return; // no change 342 } 343 mIfaceIsUp = isUp; 344 if (isUp) { 345 Log.d(TAG, "SoftAp is ready for use"); 346 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0); 347 mWifiMetrics.incrementSoftApStartResult(true, 0); 348 } else { 349 // TODO: handle the case where the interface was up, but goes down 350 } 351 } 352 353 @Override 354 public void enter() { 355 mIfaceIsUp = false; 356 InterfaceConfiguration config = null; 357 try { 358 config = mNwService.getInterfaceConfig(mApInterface.getInterfaceName()); 359 } catch (RemoteException e) { 360 } 361 if (config != null) { 362 onUpChanged(config.isUp()); 363 } 364 } 365 366 @Override 367 public boolean processMessage(Message message) { 368 switch (message.what) { 369 case CMD_INTERFACE_STATUS_CHANGED: 370 if (message.obj != mNetworkObserver) { 371 // This is from some time before the most recent configuration. 372 break; 373 } 374 boolean isUp = message.arg1 == 1; 375 onUpChanged(isUp); 376 break; 377 case CMD_START: 378 // Already started, ignore this command. 379 break; 380 case CMD_AP_INTERFACE_BINDER_DEATH: 381 case CMD_STOP: 382 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); 383 stopSoftAp(); 384 if (message.what == CMD_AP_INTERFACE_BINDER_DEATH) { 385 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 386 WifiManager.SAP_START_FAILURE_GENERAL); 387 } else { 388 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); 389 } 390 transitionTo(mIdleState); 391 break; 392 default: 393 return NOT_HANDLED; 394 } 395 return HANDLED; 396 } 397 } 398 399 } 400 } 401