1/*
2 * Copyright (C) 2017 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.settings.wifi.tether;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.ConnectivityManager;
24import android.net.wifi.WifiConfiguration;
25import android.net.wifi.WifiManager;
26import android.provider.Settings;
27import android.support.annotation.VisibleForTesting;
28import android.support.v7.preference.Preference;
29import android.support.v7.preference.PreferenceScreen;
30import android.text.BidiFormatter;
31
32import com.android.settings.R;
33import com.android.settings.Utils;
34import com.android.settings.core.PreferenceControllerMixin;
35import com.android.settingslib.core.AbstractPreferenceController;
36import com.android.settingslib.core.lifecycle.Lifecycle;
37import com.android.settingslib.core.lifecycle.LifecycleObserver;
38import com.android.settingslib.core.lifecycle.events.OnStart;
39import com.android.settingslib.core.lifecycle.events.OnStop;
40
41public class WifiTetherPreferenceController extends AbstractPreferenceController
42        implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
43
44    private static final String WIFI_TETHER_SETTINGS = "wifi_tether";
45    private static final IntentFilter AIRPLANE_INTENT_FILTER = new IntentFilter(
46            Intent.ACTION_AIRPLANE_MODE_CHANGED);
47    private static final int ID_NULL = -1;
48
49    private final ConnectivityManager mConnectivityManager;
50    private final String[] mWifiRegexs;
51    private final WifiManager mWifiManager;
52    private final Lifecycle mLifecycle;
53    private int mSoftApState;
54    @VisibleForTesting
55    Preference mPreference;
56    @VisibleForTesting
57    WifiTetherSoftApManager mWifiTetherSoftApManager;
58
59    public WifiTetherPreferenceController(Context context, Lifecycle lifecycle) {
60        this(context, lifecycle, true /* initSoftApManager */);
61    }
62
63    @VisibleForTesting
64    WifiTetherPreferenceController(Context context, Lifecycle lifecycle,
65            boolean initSoftApManager) {
66        super(context);
67        mConnectivityManager =
68                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
69        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
70        mWifiRegexs = mConnectivityManager.getTetherableWifiRegexs();
71        mLifecycle = lifecycle;
72        if (lifecycle != null) {
73            lifecycle.addObserver(this);
74        }
75        if (initSoftApManager) {
76            initWifiTetherSoftApManager();
77        }
78    }
79
80    @Override
81    public boolean isAvailable() {
82        return mWifiRegexs != null
83                && mWifiRegexs.length != 0
84                && !Utils.isMonkeyRunning();
85    }
86
87    @Override
88    public void displayPreference(PreferenceScreen screen) {
89        super.displayPreference(screen);
90        mPreference = screen.findPreference(WIFI_TETHER_SETTINGS);
91        if (mPreference == null) {
92            // unavailable
93            return;
94        }
95    }
96
97    @Override
98    public String getPreferenceKey() {
99        return WIFI_TETHER_SETTINGS;
100    }
101
102    @Override
103    public void onStart() {
104        if (mPreference != null) {
105            mContext.registerReceiver(mReceiver, AIRPLANE_INTENT_FILTER);
106            clearSummaryForAirplaneMode();
107            if (mWifiTetherSoftApManager != null) {
108                mWifiTetherSoftApManager.registerSoftApCallback();
109            }
110        }
111    }
112
113    @Override
114    public void onStop() {
115        if (mPreference != null) {
116            mContext.unregisterReceiver(mReceiver);
117            if (mWifiTetherSoftApManager != null) {
118                mWifiTetherSoftApManager.unRegisterSoftApCallback();
119            }
120        }
121    }
122
123    @VisibleForTesting
124    void initWifiTetherSoftApManager() {
125        // This manager only handles the number of connected devices, other parts are handled by
126        // normal BroadcastReceiver in this controller
127        mWifiTetherSoftApManager = new WifiTetherSoftApManager(mWifiManager,
128                new WifiTetherSoftApManager.WifiTetherSoftApCallback() {
129                    @Override
130                    public void onStateChanged(int state, int failureReason) {
131                        mSoftApState = state;
132                        handleWifiApStateChanged(state, failureReason);
133                    }
134
135                    @Override
136                    public void onNumClientsChanged(int numClients) {
137                        if (mPreference != null
138                                && mSoftApState == WifiManager.WIFI_AP_STATE_ENABLED) {
139                            // Only show the number of clients when state is on
140                            mPreference.setSummary(mContext.getResources().getQuantityString(
141                                    R.plurals.wifi_tether_connected_summary, numClients,
142                                    numClients));
143                        }
144                    }
145                });
146    }
147
148    //
149    // Everything below is copied from WifiApEnabler
150    //
151    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
152        @Override
153        public void onReceive(Context context, Intent intent) {
154            String action = intent.getAction();
155            if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
156                clearSummaryForAirplaneMode(R.string.wifi_hotspot_off_subtext);
157            }
158        }
159    };
160
161    @VisibleForTesting
162    void handleWifiApStateChanged(int state, int reason) {
163        switch (state) {
164            case WifiManager.WIFI_AP_STATE_ENABLING:
165                mPreference.setSummary(R.string.wifi_tether_starting);
166                break;
167            case WifiManager.WIFI_AP_STATE_ENABLED:
168                WifiConfiguration wifiConfig = mWifiManager.getWifiApConfiguration();
169                updateConfigSummary(wifiConfig);
170                break;
171            case WifiManager.WIFI_AP_STATE_DISABLING:
172                mPreference.setSummary(R.string.wifi_tether_stopping);
173                break;
174            case WifiManager.WIFI_AP_STATE_DISABLED:
175                mPreference.setSummary(R.string.wifi_hotspot_off_subtext);
176                clearSummaryForAirplaneMode();
177                break;
178            default:
179                if (reason == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
180                    mPreference.setSummary(R.string.wifi_sap_no_channel_error);
181                } else {
182                    mPreference.setSummary(R.string.wifi_error);
183                }
184                clearSummaryForAirplaneMode();
185        }
186    }
187
188    private void updateConfigSummary(WifiConfiguration wifiConfig) {
189        final String s = mContext.getString(
190                com.android.internal.R.string.wifi_tether_configure_ssid_default);
191
192        mPreference.setSummary(mContext.getString(R.string.wifi_tether_enabled_subtext,
193                BidiFormatter.getInstance().unicodeWrap(
194                        (wifiConfig == null) ? s : wifiConfig.SSID)));
195    }
196
197    private void clearSummaryForAirplaneMode() {
198        clearSummaryForAirplaneMode(ID_NULL);
199    }
200
201    private void clearSummaryForAirplaneMode(int defaultId) {
202        boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
203                Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
204        if (isAirplaneMode) {
205            mPreference.setSummary(R.string.wifi_tether_disabled_by_airplane);
206        } else if (defaultId != ID_NULL){
207            mPreference.setSummary(defaultId);
208        }
209    }
210    //
211    // Everything above is copied from WifiApEnabler
212    //
213}
214