1 /*
2  * Copyright (C) 2020 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;
18 
19 import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
20 import static android.net.ConnectivityManager.TETHERING_WIFI;
21 import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION;
22 
23 import android.app.settings.SettingsEnums;
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothPan;
26 import android.bluetooth.BluetoothProfile;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.net.wifi.SoftApConfiguration;
32 import android.net.wifi.WifiManager;
33 import android.os.Bundle;
34 import android.os.UserManager;
35 import android.text.TextUtils;
36 import android.util.FeatureFlagUtils;
37 import android.util.Log;
38 
39 import androidx.annotation.VisibleForTesting;
40 import androidx.preference.Preference;
41 import androidx.preference.PreferenceGroup;
42 
43 import com.android.settings.core.FeatureFlags;
44 import com.android.settings.dashboard.RestrictedDashboardFragment;
45 import com.android.settings.datausage.DataSaverBackend;
46 import com.android.settings.network.BluetoothTetherPreferenceController;
47 import com.android.settings.network.EthernetTetherPreferenceController;
48 import com.android.settings.network.TetherEnabler;
49 import com.android.settings.network.UsbTetherPreferenceController;
50 import com.android.settings.network.WifiTetherDisablePreferenceController;
51 import com.android.settings.search.BaseSearchIndexProvider;
52 import com.android.settings.widget.SwitchBar;
53 import com.android.settings.widget.SwitchBarController;
54 import com.android.settings.wifi.tether.WifiTetherApBandPreferenceController;
55 import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController;
56 import com.android.settings.wifi.tether.WifiTetherBasePreferenceController;
57 import com.android.settings.wifi.tether.WifiTetherFooterPreferenceController;
58 import com.android.settings.wifi.tether.WifiTetherPasswordPreferenceController;
59 import com.android.settings.wifi.tether.WifiTetherSSIDPreferenceController;
60 import com.android.settings.wifi.tether.WifiTetherSecurityPreferenceController;
61 import com.android.settingslib.TetherUtil;
62 import com.android.settingslib.core.AbstractPreferenceController;
63 import com.android.settingslib.search.SearchIndexable;
64 
65 import java.util.ArrayList;
66 import java.util.List;
67 import java.util.concurrent.atomic.AtomicReference;
68 
69 /**
70  * Displays preferences for all Tethering options.
71  */
72 @SearchIndexable
73 public class AllInOneTetherSettings extends RestrictedDashboardFragment
74         implements DataSaverBackend.Listener,
75         WifiTetherBasePreferenceController.OnTetherConfigUpdateListener {
76 
77     // TODO(b/148622133): Should clean up the postfix once this fragment replaced TetherSettings.
78     public static final String DEDUP_POSTFIX = "_2";
79 
80     @VisibleForTesting
81     static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name" + DEDUP_POSTFIX;
82     @VisibleForTesting
83     static final String KEY_WIFI_TETHER_NETWORK_PASSWORD =
84             "wifi_tether_network_password" + DEDUP_POSTFIX;
85     @VisibleForTesting
86     static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off" + DEDUP_POSTFIX;
87     @VisibleForTesting
88     static final String KEY_WIFI_TETHER_NETWORK_AP_BAND =
89             "wifi_tether_network_ap_band" + DEDUP_POSTFIX;
90     @VisibleForTesting
91     static final String KEY_WIFI_TETHER_SECURITY = "wifi_tether_security" + DEDUP_POSTFIX;
92 
93     private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver" + DEDUP_POSTFIX;
94     private static final String KEY_WIFI_TETHER_GROUP = "wifi_tether_settings_group";
95     public static final String WIFI_TETHER_DISABLE_KEY = "disable_wifi_tethering";
96     public static final String USB_TETHER_KEY = "enable_usb_tethering";
97     public static final String BLUETOOTH_TETHER_KEY = "enable_bluetooth_tethering" + DEDUP_POSTFIX;
98     public static final String ETHERNET_TETHER_KEY = "enable_ethernet_tethering" + DEDUP_POSTFIX;
99 
100     @VisibleForTesting
101     static final int EXPANDED_CHILD_COUNT_DEFAULT = 4;
102     @VisibleForTesting
103     static final int EXPANDED_CHILD_COUNT_WITH_SECURITY_NON = 3;
104     @VisibleForTesting
105     static final int EXPANDED_CHILD_COUNT_MAX = Integer.MAX_VALUE;
106     private static final String TAG = "AllInOneTetherSettings";
107 
108     private boolean mUnavailable;
109 
110     private DataSaverBackend mDataSaverBackend;
111     private boolean mDataSaverEnabled;
112     private Preference mDataSaverFooter;
113 
114     private WifiManager mWifiManager;
115     private boolean mRestartWifiApAfterConfigChange;
116     private final AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
117 
118     private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
119     private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
120     private WifiTetherApBandPreferenceController mApBandPreferenceController;
121     private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
122     private PreferenceGroup mWifiTetherGroup;
123     private boolean mShouldShowWifiConfig = true;
124     private boolean mHasShownAdvance;
125     private TetherEnabler mTetherEnabler;
126     @VisibleForTesting
127     final TetherEnabler.OnTetherStateUpdateListener mStateUpdateListener =
128             state -> {
129                 mShouldShowWifiConfig = TetherEnabler.isTethering(state, TETHERING_WIFI)
130                         || state == TetherEnabler.TETHERING_OFF;
131                 getPreferenceScreen().setInitialExpandedChildrenCount(
132                         getInitialExpandedChildCount());
133                 mWifiTetherGroup.setVisible(mShouldShowWifiConfig);
134             };
135 
136     private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() {
137         @Override
138         public void onReceive(Context content, Intent intent) {
139             String action = intent.getAction();
140             if (Log.isLoggable(TAG, Log.DEBUG)) {
141                 Log.d(TAG,
142                         "updating display config due to receiving broadcast action " + action);
143             }
144             updateDisplayWithNewConfig();
145             if (TextUtils.equals(action, ACTION_TETHER_STATE_CHANGED)) {
146                 restartWifiTetherIfNeed(mWifiManager.getWifiApState());
147             } else if (TextUtils.equals(action, WIFI_AP_STATE_CHANGED_ACTION)) {
148                 restartWifiTetherIfNeed(intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0));
149             }
150         }
151 
152         private void restartWifiTetherIfNeed(int state) {
153             if (state == WifiManager.WIFI_AP_STATE_DISABLED
154                     && mRestartWifiApAfterConfigChange) {
155                 mRestartWifiApAfterConfigChange = false;
156                 mTetherEnabler.startTethering(TETHERING_WIFI);
157             }
158         }
159     };
160 
161     private final BluetoothProfile.ServiceListener mProfileServiceListener =
162             new BluetoothProfile.ServiceListener() {
163                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
164                     mBluetoothPan.set((BluetoothPan) proxy);
165                 }
166 
167                 public void onServiceDisconnected(int profile) {
168                     mBluetoothPan.set(null);
169                 }
170             };
171 
172     @Override
getMetricsCategory()173     public int getMetricsCategory() {
174         return SettingsEnums.TETHER;
175     }
176 
AllInOneTetherSettings()177     public AllInOneTetherSettings() {
178         super(UserManager.DISALLOW_CONFIG_TETHERING);
179     }
180 
181     @Override
onAttach(Context context)182     public void onAttach(Context context) {
183         super.onAttach(context);
184         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
185 
186         mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class);
187         mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class);
188         mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class);
189         mApBandPreferenceController = use(WifiTetherApBandPreferenceController.class);
190         getSettingsLifecycle().addObserver(use(UsbTetherPreferenceController.class));
191         getSettingsLifecycle().addObserver(use(BluetoothTetherPreferenceController.class));
192         getSettingsLifecycle().addObserver(use(EthernetTetherPreferenceController.class));
193         getSettingsLifecycle().addObserver(use(WifiTetherDisablePreferenceController.class));
194     }
195 
196     @Override
onCreate(Bundle icicle)197     public void onCreate(Bundle icicle) {
198         super.onCreate(icicle);
199         mDataSaverBackend = new DataSaverBackend(getContext());
200         mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
201         mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER);
202         mWifiTetherGroup = findPreference(KEY_WIFI_TETHER_GROUP);
203 
204         setIfOnlyAvailableForAdmins(true);
205         if (isUiRestricted()) {
206             mUnavailable = true;
207             return;
208         }
209 
210         mDataSaverBackend.addListener(this);
211 
212         // Set initial state based on Data Saver mode.
213         onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
214     }
215 
216     @Override
onActivityCreated(Bundle savedInstanceState)217     public void onActivityCreated(Bundle savedInstanceState) {
218         super.onActivityCreated(savedInstanceState);
219         if (mUnavailable) {
220             return;
221         }
222         // Assume we are in a SettingsActivity. This is only safe because we currently use
223         // SettingsActivity as base for all preference fragments.
224         final SettingsActivity activity = (SettingsActivity) getActivity();
225         final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
226         if (adapter != null) {
227             adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
228                     BluetoothProfile.PAN);
229         }
230         final SwitchBar switchBar = activity.getSwitchBar();
231         mTetherEnabler = new TetherEnabler(activity,
232                 new SwitchBarController(switchBar), mBluetoothPan);
233         getSettingsLifecycle().addObserver(mTetherEnabler);
234         use(UsbTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
235         use(BluetoothTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
236         use(EthernetTetherPreferenceController.class).setTetherEnabler(mTetherEnabler);
237         use(WifiTetherDisablePreferenceController.class).setTetherEnabler(mTetherEnabler);
238         switchBar.show();
239     }
240 
241     @Override
onStart()242     public void onStart() {
243         super.onStart();
244 
245         if (mUnavailable) {
246             if (!isUiRestrictedByOnlyAdmin()) {
247                 getEmptyTextView().setText(R.string.tethering_settings_not_available);
248             }
249             getPreferenceScreen().removeAll();
250             return;
251         }
252         final Context context = getContext();
253         if (context != null) {
254             IntentFilter filter = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
255             filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
256             context.registerReceiver(mTetherChangeReceiver, filter);
257         }
258     }
259 
260     @Override
onResume()261     public void onResume() {
262         super.onResume();
263         if (mUnavailable) {
264             return;
265         }
266         if (mTetherEnabler != null) {
267             mTetherEnabler.addListener(mStateUpdateListener);
268         }
269     }
270 
271     @Override
onPause()272     public void onPause() {
273         super.onPause();
274         if (mUnavailable) {
275             return;
276         }
277         if (mTetherEnabler != null) {
278             mTetherEnabler.removeListener(mStateUpdateListener);
279         }
280     }
281 
282     @Override
onStop()283     public void onStop() {
284         super.onStop();
285         if (mUnavailable) {
286             return;
287         }
288         final Context context = getContext();
289         if (context != null) {
290             context.unregisterReceiver(mTetherChangeReceiver);
291         }
292     }
293 
294     @Override
onDestroy()295     public void onDestroy() {
296         mDataSaverBackend.remListener(this);
297         super.onDestroy();
298     }
299 
300     @Override
onDataSaverChanged(boolean isDataSaving)301     public void onDataSaverChanged(boolean isDataSaving) {
302         mDataSaverEnabled = isDataSaving;
303         mDataSaverFooter.setVisible(mDataSaverEnabled);
304     }
305 
306     @Override
onWhitelistStatusChanged(int uid, boolean isWhitelisted)307     public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
308         // Do nothing
309     }
310 
311     @Override
onBlacklistStatusChanged(int uid, boolean isBlacklisted)312     public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
313         // Do nothing
314     }
315 
316     @Override
createPreferenceControllers(Context context)317     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
318         return buildPreferenceControllers(context, this);
319     }
320 
buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)321     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
322             WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
323         final List<AbstractPreferenceController> controllers = new ArrayList<>();
324         controllers.add(
325                 new WifiTetherSSIDPreferenceController(context, listener));
326         controllers.add(
327                 new WifiTetherPasswordPreferenceController(context, listener));
328         controllers.add(
329                 new WifiTetherApBandPreferenceController(context, listener));
330         controllers.add(
331                 new WifiTetherSecurityPreferenceController(context, listener));
332         controllers.add(
333                 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF));
334         controllers.add(
335                 new WifiTetherFooterPreferenceController(context));
336 
337         return controllers;
338     }
339 
340     @Override
getPreferenceScreenResId()341     protected int getPreferenceScreenResId() {
342         return R.xml.all_tether_prefs;
343     }
344 
345     @Override
getLogTag()346     protected String getLogTag() {
347         return TAG;
348     }
349 
350     @Override
getHelpResource()351     public int getHelpResource() {
352         return R.string.help_url_tether;
353     }
354 
355     @Override
onTetherConfigUpdated(AbstractPreferenceController controller)356     public void onTetherConfigUpdated(AbstractPreferenceController controller) {
357         final SoftApConfiguration config = buildNewConfig();
358         mPasswordPreferenceController.updateVisibility(config.getSecurityType());
359         mWifiManager.setSoftApConfiguration(config);
360 
361         if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
362             if (Log.isLoggable(TAG, Log.DEBUG)) {
363                 Log.d(TAG, "Wifi AP config changed while enabled, stop and restart");
364             }
365             mRestartWifiApAfterConfigChange = true;
366             mTetherEnabler.stopTethering(TETHERING_WIFI);
367         }
368     }
369 
buildNewConfig()370     private SoftApConfiguration buildNewConfig() {
371         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
372         final int securityType = mSecurityPreferenceController.getSecurityType();
373         configBuilder.setSsid(mSSIDPreferenceController.getSSID());
374         if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK) {
375             configBuilder.setPassphrase(
376                     mPasswordPreferenceController.getPasswordValidated(securityType),
377                     SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
378         }
379         configBuilder.setBand(mApBandPreferenceController.getBandIndex());
380         return configBuilder.build();
381     }
382 
updateDisplayWithNewConfig()383     private void updateDisplayWithNewConfig() {
384         mSSIDPreferenceController.updateDisplay();
385         mSecurityPreferenceController.updateDisplay();
386         mPasswordPreferenceController.updateDisplay();
387         mApBandPreferenceController.updateDisplay();
388     }
389 
390     @Override
getInitialExpandedChildCount()391     public int getInitialExpandedChildCount() {
392         if (mHasShownAdvance || !mShouldShowWifiConfig) {
393             mHasShownAdvance = true;
394             return EXPANDED_CHILD_COUNT_MAX;
395         }
396 
397         if (mSecurityPreferenceController == null) {
398             return EXPANDED_CHILD_COUNT_DEFAULT;
399         }
400 
401         return (mSecurityPreferenceController.getSecurityType()
402                 == SoftApConfiguration.SECURITY_TYPE_OPEN)
403                 ? EXPANDED_CHILD_COUNT_WITH_SECURITY_NON : EXPANDED_CHILD_COUNT_DEFAULT;
404     }
405 
406     @Override
onExpandButtonClick()407     public void onExpandButtonClick() {
408         super.onExpandButtonClick();
409         mHasShownAdvance = true;
410     }
411 
412     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
413             new BaseSearchIndexProvider(R.xml.all_tether_prefs) {
414 
415                 @Override
416                 public List<String> getNonIndexableKeys(Context context) {
417                     final List<String> keys = super.getNonIndexableKeys(context);
418 
419                     if (!TetherUtil.isTetherAvailable(context)) {
420                         keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
421                         keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
422                         keys.add(KEY_WIFI_TETHER_AUTO_OFF);
423                         keys.add(KEY_WIFI_TETHER_NETWORK_AP_BAND);
424                         keys.add(KEY_WIFI_TETHER_SECURITY);
425                     }
426                     return keys;
427                 }
428 
429                 @Override
430                 protected boolean isPageSearchEnabled(Context context) {
431                     return FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
432                 }
433 
434                 @Override
435                 public List<AbstractPreferenceController> createPreferenceControllers(
436                         Context context) {
437                     return buildPreferenceControllers(context, null /*listener*/);
438                 }
439             };
440 }
441