SoftApManager.java revision ef8f064681d7e3b92a0c3f2fdaa55a656e68486d
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.wifi.IApInterface; 24import android.net.wifi.WifiConfiguration; 25import android.net.wifi.WifiManager; 26import android.net.wifi.WifiConfiguration.KeyMgmt; 27import android.os.IBinder.DeathRecipient; 28import android.os.Looper; 29import android.os.Message; 30import android.os.RemoteException; 31import android.util.Log; 32 33import com.android.internal.util.State; 34import com.android.internal.util.StateMachine; 35import com.android.server.wifi.util.ApConfigUtil; 36 37import java.nio.charset.StandardCharsets; 38import java.util.ArrayList; 39import java.util.Locale; 40 41/** 42 * Manage WiFi in AP mode. 43 * The internal state machine runs under "WifiStateMachine" thread context. 44 */ 45public class SoftApManager { 46 private static final String TAG = "SoftApManager"; 47 48 private final WifiNative mWifiNative; 49 private final ArrayList<Integer> mAllowed2GChannels; 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 /** 60 * Listener for soft AP state changes. 61 */ 62 public interface Listener { 63 /** 64 * Invoke when AP state changed. 65 * @param state new AP state 66 * @param failureReason reason when in failed state 67 */ 68 void onStateChanged(int state, int failureReason); 69 } 70 71 public SoftApManager(Looper looper, 72 WifiNative wifiNative, 73 String countryCode, 74 ArrayList<Integer> allowed2GChannels, 75 Listener listener, 76 IApInterface apInterface) { 77 mStateMachine = new SoftApStateMachine(looper); 78 79 mWifiNative = wifiNative; 80 mCountryCode = countryCode; 81 mAllowed2GChannels = allowed2GChannels; 82 mListener = listener; 83 mApInterface = apInterface; 84 } 85 86 /** 87 * Start soft AP with given configuration. 88 * @param config AP configuration 89 */ 90 public void start(WifiConfiguration config) { 91 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, config); 92 } 93 94 /** 95 * Stop soft AP. 96 */ 97 public void stop() { 98 mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); 99 } 100 101 /** 102 * Update AP state. 103 * @param state new AP state 104 * @param reason Failure reason if the new AP state is in failure state 105 */ 106 private void updateApState(int state, int reason) { 107 if (mListener != null) { 108 mListener.onStateChanged(state, reason); 109 } 110 } 111 112 /** 113 * Start a soft AP instance with the given configuration. 114 * @param config AP configuration 115 * @return integer result code 116 */ 117 private int startSoftAp(WifiConfiguration config) { 118 if (config == null || config.SSID == null) { 119 Log.e(TAG, "Unable to start soft AP without valid configuration"); 120 return ERROR_GENERIC; 121 } 122 123 /* Make a copy of configuration for updating AP band and channel. */ 124 WifiConfiguration localConfig = new WifiConfiguration(config); 125 126 int result = ApConfigUtil.updateApChannelConfig( 127 mWifiNative, mCountryCode, mAllowed2GChannels, localConfig); 128 if (result != SUCCESS) { 129 Log.e(TAG, "Failed to update AP band and channel"); 130 return result; 131 } 132 133 /* Setup country code if it is provide. */ 134 if (mCountryCode != null) { 135 /** 136 * Country code is mandatory for 5GHz band, return an error if failed to set 137 * country code when AP is configured for 5GHz band. 138 */ 139 if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT)) 140 && config.apBand == WifiConfiguration.AP_BAND_5GHZ) { 141 Log.e(TAG, "Failed to set country code, required for setting up " 142 + "soft ap in 5GHz"); 143 return ERROR_GENERIC; 144 } 145 } 146 147 int encryptionType = getIApInterfaceEncryptionType(localConfig); 148 149 try { 150 // Note that localConfig.SSID is intended to be either a hex string or "double quoted". 151 // However, it seems that whatever is handing us these configurations does not obey 152 // this convention. 153 boolean success = mApInterface.writeHostapdConfig( 154 localConfig.SSID.getBytes(StandardCharsets.UTF_8), false, 155 localConfig.apChannel, encryptionType, 156 (localConfig.preSharedKey != null) 157 ? localConfig.preSharedKey.getBytes(StandardCharsets.UTF_8) 158 : new byte[0]); 159 if (!success) { 160 Log.e(TAG, "Failed to write hostapd configuration"); 161 return ERROR_GENERIC; 162 } 163 164 success = mApInterface.startHostapd(); 165 if (!success) { 166 Log.e(TAG, "Failed to start hostapd."); 167 return ERROR_GENERIC; 168 } 169 } catch (RemoteException e) { 170 Log.e(TAG, "Exception in starting soft AP: " + e); 171 } 172 173 Log.d(TAG, "Soft AP is started"); 174 175 return SUCCESS; 176 } 177 178 private static int getIApInterfaceEncryptionType(WifiConfiguration localConfig) { 179 int encryptionType; 180 switch (localConfig.getAuthType()) { 181 case KeyMgmt.NONE: 182 encryptionType = IApInterface.ENCRYPTION_TYPE_NONE; 183 break; 184 case KeyMgmt.WPA_PSK: 185 encryptionType = IApInterface.ENCRYPTION_TYPE_WPA; 186 break; 187 case KeyMgmt.WPA2_PSK: 188 encryptionType = IApInterface.ENCRYPTION_TYPE_WPA2; 189 break; 190 default: 191 // We really shouldn't default to None, but this was how NetworkManagementService 192 // used to do this. 193 encryptionType = IApInterface.ENCRYPTION_TYPE_NONE; 194 break; 195 } 196 return encryptionType; 197 } 198 199 /** 200 * Teardown soft AP. 201 */ 202 private void stopSoftAp() { 203 try { 204 mApInterface.stopHostapd(); 205 } catch (RemoteException e) { 206 Log.e(TAG, "Exception in stopping soft AP: " + e); 207 return; 208 } 209 Log.d(TAG, "Soft AP is stopped"); 210 } 211 212 private class SoftApStateMachine extends StateMachine { 213 /* Commands for the state machine. */ 214 public static final int CMD_START = 0; 215 public static final int CMD_STOP = 1; 216 public static final int CMD_AP_INTERFACE_BINDER_DEATH = 2; 217 218 private final State mIdleState = new IdleState(); 219 private final State mStartedState = new StartedState(); 220 221 private final StateMachineDeathRecipient mDeathRecipient = 222 new StateMachineDeathRecipient(this, CMD_AP_INTERFACE_BINDER_DEATH); 223 224 SoftApStateMachine(Looper looper) { 225 super(TAG, looper); 226 227 addState(mIdleState); 228 addState(mStartedState); 229 230 setInitialState(mIdleState); 231 start(); 232 } 233 234 private class IdleState extends State { 235 @Override 236 public void enter() { 237 mDeathRecipient.unlinkToDeath(); 238 } 239 240 @Override 241 public boolean processMessage(Message message) { 242 switch (message.what) { 243 case CMD_START: 244 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0); 245 if (!mDeathRecipient.linkToDeath(mApInterface.asBinder())) { 246 mDeathRecipient.unlinkToDeath(); 247 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 248 WifiManager.SAP_START_FAILURE_GENERAL); 249 break; 250 } 251 252 int result = startSoftAp((WifiConfiguration) message.obj); 253 if (result != SUCCESS) { 254 int failureReason = WifiManager.SAP_START_FAILURE_GENERAL; 255 if (result == ERROR_NO_CHANNEL) { 256 failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 257 } 258 mDeathRecipient.unlinkToDeath(); 259 updateApState(WifiManager.WIFI_AP_STATE_FAILED, failureReason); 260 break; 261 } 262 263 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0); 264 transitionTo(mStartedState); 265 break; 266 default: 267 /* Ignore all other commands. */ 268 break; 269 } 270 271 return HANDLED; 272 } 273 } 274 275 private class StartedState extends State { 276 @Override 277 public boolean processMessage(Message message) { 278 switch (message.what) { 279 case CMD_START: 280 /* Already started, ignore this command. */ 281 break; 282 case CMD_AP_INTERFACE_BINDER_DEATH: 283 case CMD_STOP: 284 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); 285 stopSoftAp(); 286 if (message.what == CMD_AP_INTERFACE_BINDER_DEATH) { 287 updateApState(WifiManager.WIFI_AP_STATE_FAILED, 288 WifiManager.SAP_START_FAILURE_GENERAL); 289 } else { 290 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); 291 } 292 transitionTo(mIdleState); 293 break; 294 default: 295 return NOT_HANDLED; 296 } 297 return HANDLED; 298 } 299 } 300 301 } 302} 303