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 17package com.android.server.wifi; 18 19import android.net.wifi.IApInterface; 20import android.net.wifi.IWificond; 21import android.net.wifi.WifiConfiguration; 22import android.net.wifi.WifiManager; 23import android.os.INetworkManagementService; 24import android.os.Looper; 25import android.os.Message; 26import android.os.RemoteException; 27import android.util.Log; 28 29import com.android.internal.util.Protocol; 30import com.android.internal.util.State; 31import com.android.internal.util.StateMachine; 32 33import java.util.Queue; 34import java.util.concurrent.ConcurrentLinkedQueue; 35 36/** 37 * This class provides the implementation for different WiFi operating modes. 38 * 39 * NOTE: The class is a WIP and is in active development. It is intended to replace the existing 40 * WifiStateMachine.java class when the rearchitecture is complete. 41 */ 42public class WifiStateMachinePrime { 43 private static final String TAG = "WifiStateMachinePrime"; 44 45 private ModeStateMachine mModeStateMachine; 46 47 private final WifiInjector mWifiInjector; 48 private final Looper mLooper; 49 private final INetworkManagementService mNMService; 50 51 private IWificond mWificond; 52 53 private Queue<WifiConfiguration> mApConfigQueue = new ConcurrentLinkedQueue<>(); 54 55 /* The base for wifi message types */ 56 static final int BASE = Protocol.BASE_WIFI; 57 58 /* Start the soft access point */ 59 static final int CMD_START_AP = BASE + 21; 60 /* Indicates soft ap start failed */ 61 static final int CMD_START_AP_FAILURE = BASE + 22; 62 /* Stop the soft access point */ 63 static final int CMD_STOP_AP = BASE + 23; 64 /* Soft access point teardown is completed. */ 65 static final int CMD_AP_STOPPED = BASE + 24; 66 67 WifiStateMachinePrime(WifiInjector wifiInjector, 68 Looper looper, 69 INetworkManagementService nmService) { 70 mWifiInjector = wifiInjector; 71 mLooper = looper; 72 mNMService = nmService; 73 74 // Clean up existing interfaces in wificond. 75 // This ensures that the framework and wificond are in a consistent state after a framework 76 // restart. 77 try { 78 mWificond = mWifiInjector.makeWificond(); 79 if (mWificond != null) { 80 mWificond.tearDownInterfaces(); 81 } 82 } catch (RemoteException e) { 83 Log.e(TAG, "wificond died during framework startup"); 84 } 85 } 86 87 /** 88 * Method to switch wifi into client mode where connections to configured networks will be 89 * attempted. 90 */ 91 public void enterClientMode() { 92 changeMode(ModeStateMachine.CMD_START_CLIENT_MODE); 93 } 94 95 /** 96 * Method to switch wifi into scan only mode where network connection attempts will not be made. 97 * 98 * This mode is utilized by location scans. If wifi is disabled by a user, but they have 99 * previously configured their device to perform location scans, this mode allows wifi to 100 * fulfill the location scan requests but will not be used for connectivity. 101 */ 102 public void enterScanOnlyMode() { 103 changeMode(ModeStateMachine.CMD_START_SCAN_ONLY_MODE); 104 } 105 106 /** 107 * Method to enable soft ap for wifi hotspot. 108 * 109 * The WifiConfiguration is generally going to be null to indicate that the 110 * currently saved config in WifiApConfigManager should be used. When the config is 111 * not null, it will be saved in the WifiApConfigManager. This save is performed in the 112 * constructor of SoftApManager. 113 * 114 * @param wifiConfig WifiConfiguration for the hostapd softap 115 */ 116 public void enterSoftAPMode(WifiConfiguration wifiConfig) { 117 if (wifiConfig == null) { 118 wifiConfig = new WifiConfiguration(); 119 } 120 mApConfigQueue.offer(wifiConfig); 121 changeMode(ModeStateMachine.CMD_START_SOFT_AP_MODE); 122 } 123 124 /** 125 * Method to fully disable wifi. 126 * 127 * This mode will completely shut down wifi and will not perform any network scans. 128 */ 129 public void disableWifi() { 130 changeMode(ModeStateMachine.CMD_DISABLE_WIFI); 131 } 132 133 protected String getCurrentMode() { 134 if (mModeStateMachine != null) { 135 return mModeStateMachine.getCurrentMode(); 136 } 137 return "WifiDisabledState"; 138 } 139 140 private void changeMode(int newMode) { 141 if (mModeStateMachine == null) { 142 if (newMode == ModeStateMachine.CMD_DISABLE_WIFI) { 143 // command is to disable wifi, but it is already disabled. 144 Log.e(TAG, "Received call to disable wifi when it is already disabled."); 145 return; 146 } 147 // state machine was not initialized yet, we must be starting up. 148 mModeStateMachine = new ModeStateMachine(); 149 } 150 mModeStateMachine.sendMessage(newMode); 151 } 152 153 private class ModeStateMachine extends StateMachine { 154 // Commands for the state machine. 155 public static final int CMD_START_CLIENT_MODE = 0; 156 public static final int CMD_START_SCAN_ONLY_MODE = 1; 157 public static final int CMD_START_SOFT_AP_MODE = 2; 158 public static final int CMD_DISABLE_WIFI = 3; 159 160 // Create the base modes for WSM. 161 private final State mClientModeState = new ClientModeState(); 162 private final State mScanOnlyModeState = new ScanOnlyModeState(); 163 private final State mSoftAPModeState = new SoftAPModeState(); 164 private final State mWifiDisabledState = new WifiDisabledState(); 165 166 // Create the active versions of the modes for WSM. 167 private final State mClientModeActiveState = new ClientModeActiveState(); 168 private final State mScanOnlyModeActiveState = new ScanOnlyModeActiveState(); 169 private final State mSoftAPModeActiveState = new SoftAPModeActiveState(); 170 171 ModeStateMachine() { 172 super(TAG, mLooper); 173 174 // CHECKSTYLE:OFF IndentationCheck 175 addState(mClientModeState); 176 addState(mClientModeActiveState, mClientModeState); 177 addState(mScanOnlyModeState); 178 addState(mScanOnlyModeActiveState, mScanOnlyModeState); 179 addState(mSoftAPModeState); 180 addState(mSoftAPModeActiveState, mSoftAPModeState); 181 addState(mWifiDisabledState); 182 // CHECKSTYLE:ON IndentationCheck 183 184 Log.d(TAG, "Starting Wifi in WifiDisabledState"); 185 setInitialState(mWifiDisabledState); 186 start(); 187 } 188 189 private String getCurrentMode() { 190 return getCurrentState().getName(); 191 } 192 193 private boolean checkForAndHandleModeChange(Message message) { 194 switch(message.what) { 195 case ModeStateMachine.CMD_START_CLIENT_MODE: 196 Log.d(TAG, "Switching from " + getCurrentMode() + " to ClientMode"); 197 mModeStateMachine.transitionTo(mClientModeState); 198 break; 199 case ModeStateMachine.CMD_START_SCAN_ONLY_MODE: 200 Log.d(TAG, "Switching from " + getCurrentMode() + " to ScanOnlyMode"); 201 mModeStateMachine.transitionTo(mScanOnlyModeState); 202 break; 203 case ModeStateMachine.CMD_START_SOFT_AP_MODE: 204 Log.d(TAG, "Switching from " + getCurrentMode() + " to SoftApMode"); 205 mModeStateMachine.transitionTo(mSoftAPModeState); 206 break; 207 case ModeStateMachine.CMD_DISABLE_WIFI: 208 Log.d(TAG, "Switching from " + getCurrentMode() + " to WifiDisabled"); 209 mModeStateMachine.transitionTo(mWifiDisabledState); 210 break; 211 default: 212 return NOT_HANDLED; 213 } 214 return HANDLED; 215 } 216 217 private void tearDownInterfaces() { 218 if (mWificond != null) { 219 try { 220 mWificond.tearDownInterfaces(); 221 } catch (RemoteException e) { 222 // There is very little we can do here 223 Log.e(TAG, "Failed to tear down interfaces via wificond"); 224 } 225 mWificond = null; 226 } 227 return; 228 } 229 230 class ClientModeState extends State { 231 @Override 232 public void enter() { 233 mWificond = mWifiInjector.makeWificond(); 234 } 235 236 @Override 237 public boolean processMessage(Message message) { 238 if (checkForAndHandleModeChange(message)) { 239 return HANDLED; 240 } 241 return NOT_HANDLED; 242 } 243 244 @Override 245 public void exit() { 246 tearDownInterfaces(); 247 } 248 } 249 250 class ScanOnlyModeState extends State { 251 @Override 252 public void enter() { 253 } 254 255 @Override 256 public boolean processMessage(Message message) { 257 if (checkForAndHandleModeChange(message)) { 258 return HANDLED; 259 } 260 return NOT_HANDLED; 261 } 262 263 @Override 264 public void exit() { 265 // Do not tear down interfaces yet since this mode is not actively controlled or 266 // used in tests at this time. 267 // tearDownInterfaces(); 268 } 269 } 270 271 class SoftAPModeState extends State { 272 IApInterface mApInterface = null; 273 274 @Override 275 public void enter() { 276 final Message message = mModeStateMachine.getCurrentMessage(); 277 if (message.what != ModeStateMachine.CMD_START_SOFT_AP_MODE) { 278 Log.d(TAG, "Entering SoftAPMode (idle)"); 279 return; 280 } 281 282 // Continue with setup since we are changing modes 283 mApInterface = null; 284 mWificond = mWifiInjector.makeWificond(); 285 if (mWificond == null) { 286 Log.e(TAG, "Failed to get reference to wificond"); 287 writeApConfigDueToStartFailure(); 288 mModeStateMachine.sendMessage(CMD_START_AP_FAILURE); 289 return; 290 } 291 292 try { 293 mApInterface = mWificond.createApInterface(); 294 } catch (RemoteException e1) { } 295 296 if (mApInterface == null) { 297 Log.e(TAG, "Could not get IApInterface instance from wificond"); 298 writeApConfigDueToStartFailure(); 299 mModeStateMachine.sendMessage(CMD_START_AP_FAILURE); 300 return; 301 } 302 mModeStateMachine.transitionTo(mSoftAPModeActiveState); 303 } 304 305 @Override 306 public boolean processMessage(Message message) { 307 if (checkForAndHandleModeChange(message)) { 308 return HANDLED; 309 } 310 311 switch(message.what) { 312 case CMD_START_AP: 313 Log.d(TAG, "Received CMD_START_AP (now invalid message) - dropping"); 314 break; 315 case CMD_STOP_AP: 316 // not in active state, nothing to stop. 317 break; 318 case CMD_START_AP_FAILURE: 319 Log.e(TAG, "Failed to start SoftApMode. Wait for next mode command."); 320 break; 321 case CMD_AP_STOPPED: 322 Log.d(TAG, "SoftApModeActiveState stopped. Wait for next mode command."); 323 break; 324 default: 325 return NOT_HANDLED; 326 } 327 return HANDLED; 328 } 329 330 @Override 331 public void exit() { 332 tearDownInterfaces(); 333 } 334 335 protected IApInterface getInterface() { 336 return mApInterface; 337 } 338 339 private void writeApConfigDueToStartFailure() { 340 WifiConfiguration config = mApConfigQueue.poll(); 341 if (config != null && config.SSID != null) { 342 // Save valid configs for future calls. 343 mWifiInjector.getWifiApConfigStore().setApConfiguration(config); 344 } 345 } 346 } 347 348 class WifiDisabledState extends State { 349 @Override 350 public void enter() { 351 // make sure everything is torn down 352 Log.d(TAG, "Entering WifiDisabledState"); 353 } 354 355 @Override 356 public boolean processMessage(Message message) { 357 Log.d(TAG, "received a message in WifiDisabledState: " + message); 358 if (checkForAndHandleModeChange(message)) { 359 return HANDLED; 360 } 361 return NOT_HANDLED; 362 } 363 364 } 365 366 class ModeActiveState extends State { 367 ActiveModeManager mActiveModeManager; 368 369 @Override 370 public boolean processMessage(Message message) { 371 // handle messages for changing modes here 372 return NOT_HANDLED; 373 } 374 375 @Override 376 public void exit() { 377 // clean up objects from an active state - check with mode handlers to make sure 378 // they are stopping properly. 379 mActiveModeManager.stop(); 380 } 381 } 382 383 class ClientModeActiveState extends ModeActiveState { 384 @Override 385 public void enter() { 386 this.mActiveModeManager = new ClientModeManager(); 387 } 388 } 389 390 class ScanOnlyModeActiveState extends ModeActiveState { 391 @Override 392 public void enter() { 393 this.mActiveModeManager = new ScanOnlyModeManager(); 394 } 395 } 396 397 class SoftAPModeActiveState extends ModeActiveState { 398 private class SoftApListener implements SoftApManager.Listener { 399 @Override 400 public void onStateChanged(int state, int reason) { 401 if (state == WifiManager.WIFI_AP_STATE_DISABLED) { 402 mModeStateMachine.sendMessage(CMD_AP_STOPPED); 403 } else if (state == WifiManager.WIFI_AP_STATE_FAILED) { 404 mModeStateMachine.sendMessage(CMD_START_AP_FAILURE); 405 } 406 } 407 } 408 409 @Override 410 public void enter() { 411 Log.d(TAG, "Entering SoftApModeActiveState"); 412 WifiConfiguration config = mApConfigQueue.poll(); 413 if (config != null && config.SSID != null) { 414 Log.d(TAG, "Passing config to SoftApManager! " + config); 415 } else { 416 config = null; 417 } 418 419 this.mActiveModeManager = mWifiInjector.makeSoftApManager(mNMService, 420 new SoftApListener(), ((SoftAPModeState) mSoftAPModeState).getInterface(), 421 config); 422 mActiveModeManager.start(); 423 } 424 425 @Override 426 public boolean processMessage(Message message) { 427 switch(message.what) { 428 case CMD_START_AP: 429 Log.d(TAG, "Received CMD_START_AP when active - invalid message - drop"); 430 break; 431 case CMD_STOP_AP: 432 mActiveModeManager.stop(); 433 break; 434 case CMD_START_AP_FAILURE: 435 Log.d(TAG, "Failed to start SoftApMode. Return to SoftApMode (inactive)."); 436 mModeStateMachine.transitionTo(mSoftAPModeState); 437 break; 438 case CMD_AP_STOPPED: 439 Log.d(TAG, "SoftApModeActiveState stopped." 440 + " Return to SoftApMode (inactive)."); 441 mModeStateMachine.transitionTo(mSoftAPModeState); 442 break; 443 default: 444 return NOT_HANDLED; 445 } 446 return HANDLED; 447 } 448 } 449 } // class ModeStateMachine 450} 451