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.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