SoftApManager.java revision 4e0a1bafa7a86e2840c855846f3fdaee9cd00424
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.Locale;
41
42/**
43 * Manage WiFi in AP mode.
44 * The internal state machine runs under "WifiStateMachine" thread context.
45 */
46public 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