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