[go: nahoru, domu]

1e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu/*
2e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * Copyright (C) 2016 The Android Open Source Project
3e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu *
4e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * Licensed under the Apache License, Version 2.0 (the "License");
5e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * you may not use this file except in compliance with the License.
6e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * You may obtain a copy of the License at
7e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu *
8e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu *      http://www.apache.org/licenses/LICENSE-2.0
9e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu *
10e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * Unless required by applicable law or agreed to in writing, software
11e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * distributed under the License is distributed on an "AS IS" BASIS,
12e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * See the License for the specific language governing permissions and
14e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * limitations under the License.
15e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu */
16e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
17e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiupackage com.android.server.wifi;
18e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
19e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport static com.android.server.wifi.util.ApConfigUtil.ERROR_GENERIC;
20e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport static com.android.server.wifi.util.ApConfigUtil.ERROR_NO_CHANNEL;
21e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport static com.android.server.wifi.util.ApConfigUtil.SUCCESS;
22e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
23e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.content.BroadcastReceiver;
24e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.content.Context;
25e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.content.Intent;
26e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.content.IntentFilter;
27e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.ConnectivityManager;
28e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.InterfaceConfiguration;
29e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.LinkAddress;
30e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.NetworkUtils;
31e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.wifi.WifiConfiguration;
32e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.net.wifi.WifiManager;
33e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.os.INetworkManagementService;
34e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.os.Looper;
35e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.os.Message;
36e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport android.util.Log;
37e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
38e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport com.android.internal.util.State;
39e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport com.android.internal.util.StateMachine;
40e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport com.android.server.wifi.util.ApConfigUtil;
41e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
42e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport java.util.ArrayList;
43e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiuimport java.util.Locale;
44e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
45e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu/**
46e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * Manage WiFi in AP mode.
47e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu * The internal state machine runs under "WifiStateMachine" thread context.
48e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu */
49e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiupublic class SoftApManager {
50e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private static final String TAG = "SoftApManager";
51e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
52e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final Context mContext;
53e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final INetworkManagementService mNmService;
54e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final WifiNative mWifiNative;
55e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final ConnectivityManager mConnectivityManager;
56e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final ArrayList<Integer> mAllowed2GChannels;
57e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
58e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final String mCountryCode;
59e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
60e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final String mInterfaceName;
61e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private String mTetherInterfaceName;
62e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
63e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final SoftApStateMachine mStateMachine;
64e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
65e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private final Listener mListener;
66e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
67e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private static class TetherStateChange {
68e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public ArrayList<String> available;
69e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public ArrayList<String> active;
70e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
71e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        TetherStateChange(ArrayList<String> av, ArrayList<String> ac) {
72e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            available = av;
73e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            active = ac;
74e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
75e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
76e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
77e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
78e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Listener for soft AP state changes.
79e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
80e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    public interface Listener {
81e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /**
82e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * Invoke when AP state changed.
83e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * @param state new AP state
84e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * @param failureReason reason when in failed state
85e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         */
86e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        void onStateChanged(int state, int failureReason);
87e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
88e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
89e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    public SoftApManager(Context context,
90e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         Looper looper,
91e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         WifiNative wifiNative,
92e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         INetworkManagementService nmService,
93e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         ConnectivityManager connectivityManager,
94e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         String countryCode,
95e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         ArrayList<Integer> allowed2GChannels,
96e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                         Listener listener) {
97e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mStateMachine = new SoftApStateMachine(looper);
98e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
99e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mContext = context;
100e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mNmService = nmService;
101e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mWifiNative = wifiNative;
102e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mConnectivityManager = connectivityManager;
103e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mCountryCode = countryCode;
104e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mAllowed2GChannels = allowed2GChannels;
105e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mListener = listener;
106e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
107e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mInterfaceName = mWifiNative.getInterfaceName();
108e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
109e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* Register receiver for tether state changes. */
110e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mContext.registerReceiver(
111e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                new BroadcastReceiver() {
112e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    @Override
113e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    public void onReceive(Context context, Intent intent) {
114e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        ArrayList<String> available = intent.getStringArrayListExtra(
115e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                ConnectivityManager.EXTRA_AVAILABLE_TETHER);
116e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        ArrayList<String> active = intent.getStringArrayListExtra(
117e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                ConnectivityManager.EXTRA_ACTIVE_TETHER);
118e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        mStateMachine.sendMessage(
119e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                SoftApStateMachine.CMD_TETHER_STATE_CHANGE,
120e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                new TetherStateChange(available, active));
121e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    }
122e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
123e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
124e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
125e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
126e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Start soft AP with given configuration.
127e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * @param config AP configuration
128e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
129e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    public void start(WifiConfiguration config) {
130e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mStateMachine.sendMessage(SoftApStateMachine.CMD_START, config);
131e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
132e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
133e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
134e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Stop soft AP.
135e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
136e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    public void stop() {
137e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        mStateMachine.sendMessage(SoftApStateMachine.CMD_STOP);
138e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
139e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
140e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
141e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Update AP state.
142e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * @param state new AP state
143e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * @param reason Failure reason if the new AP state is in failure state
144e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
145e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private void updateApState(int state, int reason) {
146e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        if (mListener != null) {
147e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            mListener.onStateChanged(state, reason);
148e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
149e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
150e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
151e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
152e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Start a soft AP instance with the given configuration.
153e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * @param config AP configuration
154e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * @return integer result code
155e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
156e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private int startSoftAp(WifiConfiguration config) {
157e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        if (config == null) {
158e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Unable to start soft AP without configuration");
159e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            return ERROR_GENERIC;
160e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
161e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
162e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* Make a copy of configuration for updating AP band and channel. */
163e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        WifiConfiguration localConfig = new WifiConfiguration(config);
164e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
165e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        int result = ApConfigUtil.updateApChannelConfig(
166e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                mWifiNative, mCountryCode, mAllowed2GChannels, localConfig);
167e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        if (result != SUCCESS) {
168e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Failed to update AP band and channel");
169e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            return result;
170e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
171e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
172e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* Setup country code if it is provide. */
173e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        if (mCountryCode != null) {
174e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            /**
175e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu             * Country code is mandatory for 5GHz band, return an error if failed to set
176e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu             * country code when AP is configured for 5GHz band.
177e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu             */
178e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            if (!mWifiNative.setCountryCodeHal(mCountryCode.toUpperCase(Locale.ROOT))
179e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    && config.apBand == WifiConfiguration.AP_BAND_5GHZ) {
180e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                Log.e(TAG, "Failed to set country code, required for setting up "
181e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        + "soft ap in 5GHz");
182e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return ERROR_GENERIC;
183e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
184e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
185e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
186e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        try {
187e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            mNmService.startAccessPoint(localConfig, mInterfaceName);
188e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        } catch (Exception e) {
189e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Exception in starting soft AP: " + e);
190e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            return ERROR_GENERIC;
191e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
192e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
193e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        Log.d(TAG, "Soft AP is started");
194e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
195e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        return SUCCESS;
196e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
197e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
198e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    /**
199e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     * Teardown soft AP.
200e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu     */
201e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private void stopSoftAp() {
202e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        try {
203e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            mNmService.stopAccessPoint(mInterfaceName);
204e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        } catch (Exception e) {
205e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Exception in stopping soft AP: " + e);
206e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            return;
207e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
208e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        Log.d(TAG, "Soft AP is stopped");
209e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
210e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
211e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private boolean startTethering(ArrayList<String> available) {
212e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        String[] wifiRegexs = mConnectivityManager.getTetherableWifiRegexs();
213e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
214e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        for (String intf : available) {
215e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            for (String regex : wifiRegexs) {
216e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                if (intf.matches(regex)) {
217e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    try {
218e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        InterfaceConfiguration ifcg =
219e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                mNmService.getInterfaceConfig(intf);
220e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (ifcg != null) {
221e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            /* IP/netmask: 192.168.43.1/255.255.255.0 */
222e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            ifcg.setLinkAddress(new LinkAddress(
223e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                    NetworkUtils.numericToInetAddress("192.168.43.1"), 24));
224e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            ifcg.setInterfaceUp();
225e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
226e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            mNmService.setInterfaceConfig(intf, ifcg);
227e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
228e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    } catch (Exception e) {
229e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        Log.e(TAG, "Error configuring interface " + intf + ", :" + e);
230e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        return false;
231e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    }
232e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
233e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    if (mConnectivityManager.tether(intf)
234e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
235e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        Log.e(TAG, "Error tethering on " + intf);
236e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        return false;
237e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    }
238e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    mTetherInterfaceName = intf;
239e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    return true;
240e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
241e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
242e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
243e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* We found no interfaces to tether. */
244e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        return false;
245e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
246e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
247e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private void stopTethering() {
248e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        try {
249e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            /* Clear the interface address. */
250e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            InterfaceConfiguration ifcg =
251e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    mNmService.getInterfaceConfig(mTetherInterfaceName);
252e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            if (ifcg != null) {
253e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                ifcg.setLinkAddress(
254e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        new LinkAddress(
255e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                NetworkUtils.numericToInetAddress("0.0.0.0"), 0));
256e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                mNmService.setInterfaceConfig(mTetherInterfaceName, ifcg);
257e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
258e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        } catch (Exception e) {
259e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Error resetting interface " + mTetherInterfaceName + ", :" + e);
260e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
261e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
262e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        if (mConnectivityManager.untether(mTetherInterfaceName)
263e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
264e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            Log.e(TAG, "Untether initiate failed!");
265e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
266e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
267e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
268e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private boolean isWifiTethered(ArrayList<String> active) {
269e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        String[] wifiRegexs = mConnectivityManager.getTetherableWifiRegexs();
270e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        for (String intf : active) {
271e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            for (String regex : wifiRegexs) {
272e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                if (intf.matches(regex)) {
273e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    return true;
274e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
275e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
276e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
277e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* No tethered interface. */
278e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        return false;
279e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
280e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
281e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    private class SoftApStateMachine extends StateMachine {
282e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* Commands for the state machine. */
283e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public static final int CMD_START = 0;
284e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public static final int CMD_STOP = 1;
285e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public static final int CMD_TETHER_STATE_CHANGE = 2;
286e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        public static final int CMD_TETHER_NOTIFICATION_TIMEOUT = 3;
287e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
288e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000;
289e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
290e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /* Sequence number used to track tether notification timeout. */
291e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private int mTetherToken = 0;
292e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
293e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private final State mIdleState = new IdleState();
294e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private final State mStartedState = new StartedState();
295e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private final State mTetheringState = new TetheringState();
296e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private final State mTetheredState = new TetheredState();
297e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private final State mUntetheringState = new UntetheringState();
298e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
299e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        SoftApStateMachine(Looper looper) {
300e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            super(TAG, looper);
301e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
302e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            // CHECKSTYLE:OFF IndentationCheck
303e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            addState(mIdleState);
304e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                addState(mStartedState, mIdleState);
305e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    addState(mTetheringState, mStartedState);
306e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    addState(mTetheredState, mStartedState);
307e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    addState(mUntetheringState, mStartedState);
308e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            // CHECKSTYLE:ON IndentationCheck
309e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
310e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            setInitialState(mIdleState);
311e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            start();
312e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
313e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
314e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private class IdleState extends State {
315e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
316e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public boolean processMessage(Message message) {
317e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                switch (message.what) {
318e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_START:
319e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
320e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        int result = startSoftAp((WifiConfiguration) message.obj);
321e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (result == SUCCESS) {
322e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
323e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mStartedState);
324e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        } else {
325e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            int reason = WifiManager.SAP_START_FAILURE_GENERAL;
326e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            if (result == ERROR_NO_CHANNEL) {
327e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
328e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            }
329e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason);
330e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
331e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
332e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    default:
333e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        /* Ignore all other commands. */
334e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
335e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
336e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return HANDLED;
337e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
338e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
339e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
340e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private class StartedState extends State {
341e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
342e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public boolean processMessage(Message message) {
343e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                switch (message.what) {
344e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_START:
345e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        /* Already started, ignore this command. */
346e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
347e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_STOP:
348e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        updateApState(WifiManager.WIFI_AP_STATE_DISABLING, 0);
349e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        stopSoftAp();
350e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        updateApState(WifiManager.WIFI_AP_STATE_DISABLED, 0);
351e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        transitionTo(mIdleState);
352e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
353e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_STATE_CHANGE:
354e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TetherStateChange stateChange = (TetherStateChange) message.obj;
355e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (startTethering(stateChange.available)) {
356e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mTetheringState);
357e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
358e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
359e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    default:
360e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        return NOT_HANDLED;
361e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
362e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return HANDLED;
363e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
364e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
365e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
366e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /**
367e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * This is a transient state. We will transition out of this state when
368e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * we receive a notification that WiFi is tethered (TetheredState) or
369e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * we timed out waiting for that notification (StartedState).
370e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         */
371e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private class TetheringState extends State {
372e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
373e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public void enter() {
374e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                /* Send a delayed message to terminate if tethering fails to notify. */
375e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                sendMessageDelayed(
376e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        obtainMessage(CMD_TETHER_NOTIFICATION_TIMEOUT, ++mTetherToken),
377e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TETHER_NOTIFICATION_TIME_OUT_MSECS);
378e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
379e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
380e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
381e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public boolean processMessage(Message message) {
382e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                switch (message.what) {
383e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_STATE_CHANGE:
384e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TetherStateChange stateChange = (TetherStateChange) message.obj;
385e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (isWifiTethered(stateChange.active)) {
386e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mTetheredState);
387e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
388e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
389e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_NOTIFICATION_TIMEOUT:
390e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (message.arg1 == mTetherToken) {
391e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            Log.e(TAG, "Failed to get tether update, "
392e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                    + "shutdown soft access point");
393e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mStartedState);
394e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            /* Needs to be first thing handled. */
395e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            sendMessageAtFrontOfQueue(CMD_STOP);
396e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
397e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
398e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    default:
399e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        return NOT_HANDLED;
400e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
401e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return HANDLED;
402e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
403e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
404e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
405e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private class TetheredState extends State {
406e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
407e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public boolean processMessage(Message message) {
408e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                switch (message.what) {
409e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_STATE_CHANGE:
410e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TetherStateChange stateChange = (TetherStateChange) message.obj;
411e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (!isWifiTethered(stateChange.active)) {
412e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            Log.e(TAG, "Tethering reports wifi as untethered!, "
413e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                    + "shut down soft Ap");
414e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            sendMessage(CMD_STOP);
415e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
416e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
417e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_STOP:
418e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        Log.d(TAG, "Untethering before stopping AP");
419e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        stopTethering();
420e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        transitionTo(mUntetheringState);
421e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
422e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
423e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    default:
424e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        return NOT_HANDLED;
425e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
426e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return HANDLED;
427e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
428e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
429e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
430e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        /**
431e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * This is a transient state, will transition out of this state to StartedState
432e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * when we receive a notification that WiFi is untethered or we timed out waiting
433e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         * for that notification.
434e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu         */
435e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        private class UntetheringState extends State {
436e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
437e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public void enter() {
438e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                /* Send a delayed message to terminate if tethering fails to notify. */
439e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                sendMessageDelayed(
440e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        obtainMessage(CMD_TETHER_NOTIFICATION_TIMEOUT, ++mTetherToken),
441e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TETHER_NOTIFICATION_TIME_OUT_MSECS);
442e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
443e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu
444e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            @Override
445e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            public boolean processMessage(Message message) {
446e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                switch (message.what) {
447e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_STATE_CHANGE:
448e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        TetherStateChange stateChange = (TetherStateChange) message.obj;
449e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        /* Transition back to StartedState when WiFi is untethered. */
450e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (!isWifiTethered(stateChange.active)) {
451e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mStartedState);
452e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            /* Needs to be first thing handled */
453e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            sendMessageAtFrontOfQueue(CMD_STOP);
454e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
455e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
456e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    case CMD_TETHER_NOTIFICATION_TIMEOUT:
457e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        if (message.arg1 == mTetherToken) {
458e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            Log.e(TAG, "Failed to get tether update, "
459e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                                    + "force stop access point");
460e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            transitionTo(mStartedState);
461e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            /* Needs to be first thing handled. */
462e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                            sendMessageAtFrontOfQueue(CMD_STOP);
463e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        }
464e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
465e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                    default:
466e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        /* Defer handling of this message until untethering is completed. */
467e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        deferMessage(message);
468e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                        break;
469e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                }
470e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu                return HANDLED;
471e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu            }
472e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu        }
473e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu    }
474e5b93fbfe451bc57c07b3f72191b52b6bd237d5bPeter Qiu}
475