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