Home | History | Annotate | Download | only in wifi
      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