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.TetheringManager.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.SettingsMainSwitchBar;
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 
58     @VisibleForTesting
59     static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name";
60     @VisibleForTesting
61     static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password";
62     @VisibleForTesting
63     static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off";
64     @VisibleForTesting
65     static final String KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY =
66             WifiTetherMaximizeCompatibilityPreferenceController.PREF_KEY;
67 
68     private WifiTetherSwitchBarController mSwitchBarController;
69     private WifiTetherSSIDPreferenceController mSSIDPreferenceController;
70     private WifiTetherPasswordPreferenceController mPasswordPreferenceController;
71     private WifiTetherSecurityPreferenceController mSecurityPreferenceController;
72     private WifiTetherMaximizeCompatibilityPreferenceController mMaxCompatibilityPrefController;
73 
74     private WifiManager mWifiManager;
75     private boolean mRestartWifiApAfterConfigChange;
76     private boolean mUnavailable;
77 
78     @VisibleForTesting
79     TetherChangeReceiver mTetherChangeReceiver;
80 
81     static {
82         TETHER_STATE_CHANGE_FILTER = new IntentFilter(ACTION_TETHER_STATE_CHANGED);
83         TETHER_STATE_CHANGE_FILTER.addAction(WIFI_AP_STATE_CHANGED_ACTION);
84     }
85 
WifiTetherSettings()86     public WifiTetherSettings() {
87         super(UserManager.DISALLOW_CONFIG_TETHERING);
88     }
89 
90     @Override
getMetricsCategory()91     public int getMetricsCategory() {
92         return SettingsEnums.WIFI_TETHER_SETTINGS;
93     }
94 
95     @Override
getLogTag()96     protected String getLogTag() {
97         return "WifiTetherSettings";
98     }
99 
100     @Override
onCreate(Bundle icicle)101     public void onCreate(Bundle icicle) {
102         super.onCreate(icicle);
103         setIfOnlyAvailableForAdmins(true);
104         if (isUiRestricted()) {
105             mUnavailable = true;
106         }
107     }
108 
109     @Override
onAttach(Context context)110     public void onAttach(Context context) {
111         super.onAttach(context);
112         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
113         mTetherChangeReceiver = new TetherChangeReceiver();
114 
115         mSSIDPreferenceController = use(WifiTetherSSIDPreferenceController.class);
116         mSecurityPreferenceController = use(WifiTetherSecurityPreferenceController.class);
117         mPasswordPreferenceController = use(WifiTetherPasswordPreferenceController.class);
118         mMaxCompatibilityPrefController =
119                 use(WifiTetherMaximizeCompatibilityPreferenceController.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 SettingsMainSwitchBar switchBar = activity.getSwitchBar();
132         switchBar.setTitle(getContext().getString(R.string.use_wifi_hotsopt_main_switch_title));
133         mSwitchBarController = new WifiTetherSwitchBarController(activity, switchBar);
134         getSettingsLifecycle().addObserver(mSwitchBarController);
135         switchBar.show();
136     }
137 
138     @Override
onStart()139     public void onStart() {
140         super.onStart();
141         if (mUnavailable) {
142             if (!isUiRestrictedByOnlyAdmin()) {
143                 getEmptyTextView().setText(R.string.tethering_settings_not_available);
144             }
145             getPreferenceScreen().removeAll();
146             return;
147         }
148         final Context context = getContext();
149         if (context != null) {
150             context.registerReceiver(mTetherChangeReceiver, TETHER_STATE_CHANGE_FILTER);
151         }
152     }
153 
154     @Override
onStop()155     public void onStop() {
156         super.onStop();
157         if (mUnavailable) {
158             return;
159         }
160         final Context context = getContext();
161         if (context != null) {
162             context.unregisterReceiver(mTetherChangeReceiver);
163         }
164     }
165 
166 
167     @Override
getPreferenceScreenResId()168     protected int getPreferenceScreenResId() {
169         return R.xml.wifi_tether_settings;
170     }
171 
172     @Override
createPreferenceControllers(Context context)173     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
174         return buildPreferenceControllers(context, this::onTetherConfigUpdated);
175     }
176 
buildPreferenceControllers(Context context, WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener)177     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
178             WifiTetherBasePreferenceController.OnTetherConfigUpdateListener listener) {
179         final List<AbstractPreferenceController> controllers = new ArrayList<>();
180         controllers.add(new WifiTetherSSIDPreferenceController(context, listener));
181         controllers.add(new WifiTetherSecurityPreferenceController(context, listener));
182         controllers.add(new WifiTetherPasswordPreferenceController(context, listener));
183         controllers.add(
184                 new WifiTetherAutoOffPreferenceController(context, KEY_WIFI_TETHER_AUTO_OFF));
185         controllers.add(new WifiTetherMaximizeCompatibilityPreferenceController(context, listener));
186         return controllers;
187     }
188 
189     @Override
onTetherConfigUpdated(AbstractPreferenceController context)190     public void onTetherConfigUpdated(AbstractPreferenceController context) {
191         final SoftApConfiguration config = buildNewConfig();
192         mPasswordPreferenceController.setSecurityType(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 
buildNewConfig()208     private SoftApConfiguration buildNewConfig() {
209         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
210         final int securityType = mSecurityPreferenceController.getSecurityType();
211         configBuilder.setSsid(mSSIDPreferenceController.getSSID());
212         if (securityType != SoftApConfiguration.SECURITY_TYPE_OPEN) {
213             configBuilder.setPassphrase(
214                     mPasswordPreferenceController.getPasswordValidated(securityType),
215                     securityType);
216         }
217         mMaxCompatibilityPrefController.setupMaximizeCompatibility(configBuilder);
218         return configBuilder.build();
219     }
220 
startTether()221     private void startTether() {
222         mRestartWifiApAfterConfigChange = false;
223         mSwitchBarController.startTether();
224     }
225 
updateDisplayWithNewConfig()226     private void updateDisplayWithNewConfig() {
227         use(WifiTetherSSIDPreferenceController.class).updateDisplay();
228         use(WifiTetherSecurityPreferenceController.class).updateDisplay();
229         use(WifiTetherPasswordPreferenceController.class).updateDisplay();
230         use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay();
231     }
232 
233     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
234             new BaseSearchIndexProvider(R.xml.wifi_tether_settings) {
235 
236                 @Override
237                 public List<String> getNonIndexableKeys(Context context) {
238                     final List<String> keys = super.getNonIndexableKeys(context);
239 
240                     if (!TetherUtil.isTetherAvailable(context)) {
241                         keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
242                         keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
243                         keys.add(KEY_WIFI_TETHER_AUTO_OFF);
244                         keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
245                     }
246 
247                     // Remove duplicate
248                     keys.add(KEY_WIFI_TETHER_SCREEN);
249                     return keys;
250                 }
251 
252                 @Override
253                 protected boolean isPageSearchEnabled(Context context) {
254                     return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
255                 }
256 
257                 @Override
258                 public List<AbstractPreferenceController> createPreferenceControllers(
259                         Context context) {
260                     return buildPreferenceControllers(context, null /* listener */);
261                 }
262             };
263 
264     @VisibleForTesting
265     class TetherChangeReceiver extends BroadcastReceiver {
266         @Override
onReceive(Context content, Intent intent)267         public void onReceive(Context content, Intent intent) {
268             String action = intent.getAction();
269             Log.d(TAG, "updating display config due to receiving broadcast action " + action);
270             updateDisplayWithNewConfig();
271             if (action.equals(ACTION_TETHER_STATE_CHANGED)) {
272                 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED
273                         && mRestartWifiApAfterConfigChange) {
274                     startTether();
275                 }
276             } else if (action.equals(WIFI_AP_STATE_CHANGED_ACTION)) {
277                 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0);
278                 if (state == WifiManager.WIFI_AP_STATE_DISABLED
279                         && mRestartWifiApAfterConfigChange) {
280                     startTether();
281                 }
282             }
283         }
284     }
285 }
286