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 
17 package com.android.settings.wifi.tether;
18 
19 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
20 import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION;
21 
22 import android.app.settings.SettingsEnums;
23 import android.content.BroadcastReceiver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.net.wifi.SoftApConfiguration;
28 import android.net.wifi.WifiManager;
29 import android.os.Bundle;
30 import android.os.UserManager;
31 import android.util.FeatureFlagUtils;
32 import android.util.Log;
33 
34 import androidx.annotation.VisibleForTesting;
35 import androidx.preference.PreferenceGroup;
36 
37 import com.android.settings.R;
38 import com.android.settings.SettingsActivity;
39 import com.android.settings.core.FeatureFlags;
40 import com.android.settings.dashboard.RestrictedDashboardFragment;
41 import com.android.settings.search.BaseSearchIndexProvider;
42 import com.android.settings.widget.SwitchBar;
43 import com.android.settingslib.TetherUtil;
44 import com.android.settingslib.core.AbstractPreferenceController;
45 import com.android.settingslib.search.SearchIndexable;
46 
47 import java.util.ArrayList;
48 import java.util.List;
49 
50 @SearchIndexable
51 public class WifiTetherSettings extends RestrictedDashboardFragment
52         implements WifiTetherBasePreferenceController.OnTetherConfigUpdateListener {
53 
54     private static final String TAG = "WifiTetherSettings";
55     private static final IntentFilter TETHER_STATE_CHANGE_FILTER;
56     private static final String KEY_WIFI_TETHER_SCREEN = "wifi_tether_settings_screen";
57     private static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3;
58     private static final int EXPANDED_CHILD_COUNT_DEFAULT = 4;
59 
60     @VisibleForTesting
61     static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name";
62     @VisibleForTesting
63     static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password";
64     @VisibleForTesting
65     static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off";
66     @VisibleForTesting
67     static final String KEY_WIFI_TETHER_NETWORK_AP_BAND = "wifi_tether_network_ap_band";
68 
69     private WifiTetherSwitchBarController mSwitchBarController;
70     private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
71     private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
72     private WifiTetherApBandPreferenceController mApBandPreferenceController;
73     private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
74 
75     private WifiManager mWifiManager;
76     private boolean mRestartWifiApAfterConfigChange;
77     private boolean mUnavailable;
78 
79     @VisibleForTesting
80     TetherChangeReceiver mTetherChangeReceiver;
81 
82     static {
83         TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
84         TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION);
85     }
86 
WifiTetherSettings()87     public WifiTetherSettings() {
88         super(UserManager.DISALLOW_CONFIG_TETHERING);
89     }
90 
91     @Override
getMetricsCategory()92     public int getMetricsCategory() {
93         return SettingsEnums.WIFI_TETHER_SETTINGS;
94     }
95 
96     @Override
getLogTag()97     protected String getLogTag() {
98         return "WifiTetherSettings";
99     }
100 
101     @Override
onCreate(Bundle icicle)102     public void onCreate(Bundle icicle) {
103         super.onCreate(icicle);
104         setIfOnlyAvailableForAdmins(true);
105         if (isUiRestricted()) {
106             mUnavailable = true;
107         }
108     }
109 
110     @Override
onAttach(Context context)111     public void onAttach(Context context) {
112         super.onAttach(context);
113         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
114         mTetherChangeReceiver = new TetherChangeReceiver();
115 
116         mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class);
117         mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class);
118         mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class);
119         mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class);
120     }
121 
122     @Override
onActivityCreated(Bundle savedInstanceState)123     public void onActivityCreated(Bundle savedInstanceState) {
124         super.onActivityCreated(savedInstanceState);
125         if (mUnavailable) {
126             return;
127         }
128         // Assume we are in a SettingsActivity. This is only safe because we currently use
129         // SettingsActivity as base for all preference fragments.
130         final SettingsActivity activity = (SettingsActivity) getActivity();
131         final SwitchBar switchBar = activity.getSwitchBar();
132         mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar);
133         getSettingsLifecycle().addObserver(mSwitchBarController);
134         switchBar.show();
135     }
136 
137     @Override
onStart()138     public void onStart() {
139         super.onStart();
140         if (mUnavailable) {
141             if (!isUiRestrictedByOnlyAdmin()) {
142                 getEmptyTextView().setText(R.string.tethering_settings_not_available);
143             }
144             getPreferenceScreen().removeAll();
145             return;
146         }
147         final Context context = getContext();
148         if (context != null) {
149             context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER);
150         }
151     }
152 
153     @Override
onStop()154     public void onStop() {
155         super.onStop();
156         if (mUnavailable) {
157             return;
158         }
159         final Context context = getContext();
160         if (context != null) {
161             context.unregisterReceiver(mTetherChangeReceiver);
162         }
163     }
164 
165 
166     @Override
getPreferenceScreenResId()167     protected int getPreferenceScreenResId() {
168         return R.xml.wifi_tether_settings;
169     }
170 
171     @Override
createPreferenceControllers(Context context)172     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
173         return buildPreferenceControllers(context, this::onTetherConfigUpdated);
174     }
175 
buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)176     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
177             WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
178         final List<AbstractPreferenceController> controllers = new ArrayList<>();
179         controllers.add(new WifiTetherSSIDPreferenceController(context, listener));
180         controllers.add(new WifiTetherSecurityPreferenceController(context, listener));
181         controllers.add(new WifiTetherPasswordPreferenceController(context, listener));
182         controllers.add(new WifiTetherApBandPreferenceController(context, listener));
183         controllers.add(
184                 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF));
185 
186         return controllers;
187     }
188 
189     @Override
onTetherConfigUpdated(AbstractPreferenceController context)190     public void onTetherConfigUpdated(AbstractPreferenceController context) {
191         final SoftApConfiguration config = buildNewConfig();
192         mPasswordPreferenceController.updateVisibility(config.getSecurityType());
193 
194         /**
195          * if soft AP is stopped, bring up
196          * else restart with new config
197          * TODO: update config on a running access point when framework support is added
198          */
199         if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
200             Log.d("TetheringSettings",
201                     "Wifi AP config changed while enabled, stop and restart");
202             mRestartWifiApAfterConfigChange = true;
203             mSwitchBarController.stopTether();
204         }
205         mWifiManager.setSoftApConfiguration(config);
206 
207         if (context instanceof WifiTetherSecurityPreferenceController) {
208             reConfigInitialExpandedChildCount();
209         }
210     }
211 
buildNewConfig()212     private SoftApConfiguration buildNewConfig() {
213         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
214         final int securityType = mSecurityPreferenceController.getSecurityType();
215         configBuilder.setSsid(mSSIDPreferenceController.getSSID());
216         if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
217             configBuilder.setPassphrase(
218                     mPasswordPreferenceController.getPasswordValidated(securityType),
219                     SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
220         }
221         configBuilder.setBand(mApBandPreferenceController.getBandIndex());
222         return configBuilder.build();
223     }
224 
startTether()225     private void startTether() {
226         mRestartWifiApAfterConfigChange = false;
227         mSwitchBarController.startTether();
228     }
229 
updateDisplayWithNewConfig()230     private void updateDisplayWithNewConfig() {
231         use(WifiTetherSSIDPreferenceController.class)
232                 .updateDisplay();
233         use(WifiTetherSecurityPreferenceController.class)
234                 .updateDisplay();
235         use(WifiTetherPasswordPreferenceController.class)
236                 .updateDisplay();
237         use(WifiTetherApBandPreferenceController.class)
238                 .updateDisplay();
239     }
240 
241     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
242             new BaseSearchIndexProvider(R.xml.wifi_tether_settings) {
243 
244                 @Override
245                 public List<String> getNonIndexableKeys(Context context) {
246                     final List<String> keys = super.getNonIndexableKeys(context);
247 
248                     if (!TetherUtil.isTetherAvailable(context)) {
249                         keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
250                         keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
251                         keys.add(KEY_WIFI_TETHER_AUTO_OFF);
252                         keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND);
253                     }
254 
255                     // Remove duplicate
256                     keys.add(KEY_WIFI_TETHER_SCREEN);
257                     return keys;
258                 }
259 
260                 @Override
261                 protected boolean isPageSearchEnabled(Context context) {
262                     return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
263                 }
264 
265                 @Override
266                 public List<AbstractPreferenceController> createPreferenceControllers(
267                         Context context) {
268                     return buildPreferenceControllers(context, null /* listener */);
269                 }
270             };
271 
272     @VisibleForTesting
273     class TetherChangeReceiver extends BroadcastReceiver {
274         @Override
onReceive(Context content, Intent intent)275         public void onReceive(Context content, Intent intent) {
276             String action = intent.getAction();
277             Log.d(TAG, "updating display config due to receiving broadcast action " + action);
278             updateDisplayWithNewConfig();
279             if (action.equals(ACTION_TETHER_STATE_CHANGED)) {
280                 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED
281                         && mRestartWifiApAfterConfigChange) {
282                     startTether();
283                 }
284             } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) {
285                 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
286                 if (state == WifiManager.WIFI_AP_STATE_DISABLED
287                         && mRestartWifiApAfterConfigChange) {
288                     startTether();
289                 }
290             }
291         }
292     }
293 
reConfigInitialExpandedChildCount()294     private void reConfigInitialExpandedChildCount() {
295         final PreferenceGroup screen = getPreferenceScreen();
296         if (mSecurityPreferenceController.getSecurityType()
297                 == SoftApConfiguration.SECURITY_TYPE_OPEN) {
298             screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_WITH_SECURITY_NON);
299             return;
300         }
301         screen.setInitialExpandedChildrenCount(EXPANDED_CHILD_COUNT_DEFAULT);
302     }
303 
304     @Override
getInitialExpandedChildCount()305     public int getInitialExpandedChildCount() {
306         if (mSecurityPreferenceController == null) {
307             return EXPANDED_CHILD_COUNT_DEFAULT;
308         }
309 
310         return (mSecurityPreferenceController.getSecurityType()
311                 == SoftApConfiguration.SECURITY_TYPE_OPEN)
312             ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT;
313     }
314 }
315