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.content.Context; 24 import android.net.ConnectivityManager; 25 import android.net.wifi.WifiConfiguration; 26 import android.net.wifi.WifiManager; 27 import android.os.INetworkManagementService; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.util.Log; 31 32 import com.android.internal.util.State; 33 import com.android.internal.util.StateMachine; 34 import com.android.server.wifi.util.ApConfigUtil; 35 36 import java.util.ArrayList; 37 import java.util.Locale; 38 39 /** 40 * Manage WiFi in AP mode. 41 * The internal state machine runs under "WifiStateMachine" thread context. 42 */ 43 public class SoftApManager { 44 private static final String TAG = "SoftApManager"; 45 46 private final INetworkManagementService mNmService; 47 private final WifiNative mWifiNative; 48 private final ArrayList<Integer> mAllowed2GChannels; 49 50 private final String mCountryCode; 51 52 private final String mInterfaceName; 53 54 private final SoftApStateMachine mStateMachine; 55 56 private final Listener mListener; 57 58 /** 59 * Listener for soft AP state changes. 60 */ 61 public interface Listener { 62 /** 63 * Invoke when AP state changed. 64 * @param state new AP state 65 * @param failureReason reason when in failed state 66 */ 67 void onStateChanged(int state, int failureReason); 68 } 69 70 public SoftApManager(Looper looper, 71 WifiNative wifiNative, 72 INetworkManagementService nmService, 73 String countryCode, 74 ArrayList<Integer> allowed2GChannels, 75 Listener listener) { 76 mStateMachine = new SoftApStateMachine(looper); 77 78 mNmService = nmService; 79 mWifiNative = wifiNative; 80 mCountryCode = countryCode; 81 mAllowed2GChannels = allowed2GChannels; 82 mListener = listener; 83 84 mInterfaceName = mWifiNative.getInterfaceName(); 85 } 86 87 /** 88 * Start soft AP with given configuration. 89 * @param config AP configuration 90 */ 91 public void start(WifiConfiguration config) { 92 mStateMachine.sendMessage(SoftApStateMachine.CMD_START, config); 93 } 94 95 /** 96 * Stop soft AP. 97 */ 98 public void stop() { 99 mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP); 100 } 101 102 /** 103 * Update AP state. 104 * @param state new AP state 105 * @param reason Failure reason if the new AP state is in failure state 106 */ 107 private void updateApState(int state, int reason) { 108 if (mListener != null) { 109 mListener.onStateChanged(state, reason); 110 } 111 } 112 113 /** 114 * Start a soft AP instance with the given configuration. 115 * @param config AP configuration 116 * @return integer result code 117 */ 118 private int startSoftAp(WifiConfiguration config) { 119 if (config == null) { 120 Log.e(TAG, "Unable to start soft AP without configuration"); 121 return ERROR_GENERIC; 122 } 123 124 /* Make a copy of configuration for updating AP band and channel. */ 125 WifiConfiguration localConfig = new WifiConfiguration(config); 126 127 int result = ApConfigUtil.updateApChannelConfig( 128 mWifiNative, mCountryCode, mAllowed2GChannels, localConfig); 129 if (result != SUCCESS) { 130 Log.e(TAG, "Failed to update AP band and channel"); 131 return result; 132 } 133 134 /* Setup country code if it is provide. */ 135 if (mCountryCode != null) { 136 /** 137 * Country code is mandatory for 5GHz band, return an error if failed to set 138 * country code when AP is configured for 5GHz band. 139 */ 140 if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT)) 141 && config.apBand == WifiConfiguration.AP_BAND_5GHZ) { 142 Log.e(TAG, "Failed to set country code, required for setting up " 143 + "soft ap in 5GHz"); 144 return ERROR_GENERIC; 145 } 146 } 147 148 try { 149 mNmService.startAccessPoint(localConfig, mInterfaceName); 150 } catch (Exception e) { 151 Log.e(TAG, "Exception in starting soft AP: " + e); 152 return ERROR_GENERIC; 153 } 154 155 Log.d(TAG, "Soft AP is started"); 156 157 return SUCCESS; 158 } 159 160 /** 161 * Teardown soft AP. 162 */ 163 private void stopSoftAp() { 164 try { 165 mNmService.stopAccessPoint(mInterfaceName); 166 } catch (Exception e) { 167 Log.e(TAG, "Exception in stopping soft AP: " + e); 168 return; 169 } 170 Log.d(TAG, "Soft AP is stopped"); 171 } 172 173 private class SoftApStateMachine extends StateMachine { 174 /* Commands for the state machine. */ 175 public static final int CMD_START = 0; 176 public static final int CMD_STOP = 1; 177 178 private final State mIdleState = new IdleState(); 179 private final State mStartedState = new StartedState(); 180 181 SoftApStateMachine(Looper looper) { 182 super(TAG, looper); 183 184 addState(mIdleState); 185 addState(mStartedState, mIdleState); 186 187 setInitialState(mIdleState); 188 start(); 189 } 190 191 private class IdleState extends State { 192 @Override 193 public boolean processMessage(Message message) { 194 switch (message.what) { 195 case CMD_START: 196 updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0); 197 int result = startSoftAp((WifiConfiguration) message.obj); 198 if (result == SUCCESS) { 199 updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0); 200 transitionTo(mStartedState); 201 } else { 202 int reason = WifiManager.SAP_START_FAILURE_GENERAL; 203 if (result == ERROR_NO_CHANNEL) { 204 reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL; 205 } 206 updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason); 207 } 208 break; 209 default: 210 /* Ignore all other commands. */ 211 break; 212 } 213 return HANDLED; 214 } 215 } 216 217 private class StartedState extends State { 218 @Override 219 public boolean processMessage(Message message) { 220 switch (message.what) { 221 case CMD_START: 222 /* Already started, ignore this command. */ 223 break; 224 case CMD_STOP: 225 updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0); 226 stopSoftAp(); 227 updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0); 228 transitionTo(mIdleState); 229 break; 230 default: 231 return NOT_HANDLED; 232 } 233 return HANDLED; 234 } 235 } 236 237 } 238 } 239