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