1 /* 2 * Copyright (C) 2008 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.TETHERING_BLUETOOTH; 20 import static android.net.ConnectivityManager.TETHERING_USB; 21 import static android.net.TetheringManager.TETHERING_ETHERNET; 22 23 import android.app.Activity; 24 import android.app.settings.SettingsEnums; 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothPan; 27 import android.bluetooth.BluetoothProfile; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.hardware.usb.UsbManager; 33 import android.net.ConnectivityManager; 34 import android.net.EthernetManager; 35 import android.net.TetheringManager; 36 import android.net.wifi.WifiManager; 37 import android.os.Bundle; 38 import android.os.Environment; 39 import android.os.Handler; 40 import android.os.HandlerExecutor; 41 import android.os.UserManager; 42 import android.provider.SearchIndexableResource; 43 import android.text.TextUtils; 44 import android.util.FeatureFlagUtils; 45 46 import androidx.annotation.VisibleForTesting; 47 import androidx.preference.Preference; 48 import androidx.preference.SwitchPreference; 49 50 import com.android.settings.core.FeatureFlags; 51 import com.android.settings.datausage.DataSaverBackend; 52 import com.android.settings.search.BaseSearchIndexProvider; 53 import com.android.settings.wifi.tether.WifiTetherPreferenceController; 54 import com.android.settingslib.TetherUtil; 55 import com.android.settingslib.search.SearchIndexable; 56 57 import java.lang.ref.WeakReference; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.List; 61 import java.util.concurrent.atomic.AtomicReference; 62 63 /* 64 * Displays preferences for Tethering. 65 */ 66 @SearchIndexable 67 public class TetherSettings extends RestrictedSettingsFragment 68 implements DataSaverBackend.Listener { 69 70 @VisibleForTesting 71 static final String KEY_TETHER_PREFS_SCREEN = "tether_prefs_screen"; 72 @VisibleForTesting 73 static final String KEY_WIFI_TETHER = "wifi_tether"; 74 @VisibleForTesting 75 static final String KEY_USB_TETHER_SETTINGS = "usb_tether_settings"; 76 @VisibleForTesting 77 static final String KEY_ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; 78 private static final String KEY_ENABLE_ETHERNET_TETHERING = "enable_ethernet_tethering"; 79 private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver"; 80 @VisibleForTesting 81 static final String KEY_TETHER_PREFS_FOOTER = "tether_prefs_footer"; 82 83 private static final String TAG = "TetheringSettings"; 84 85 private SwitchPreference mUsbTether; 86 87 private SwitchPreference mBluetoothTether; 88 89 private SwitchPreference mEthernetTether; 90 91 private BroadcastReceiver mTetherChangeReceiver; 92 93 private String[] mUsbRegexs; 94 private String[] mBluetoothRegexs; 95 private String mEthernetRegex; 96 private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>(); 97 98 private Handler mHandler = new Handler(); 99 private OnStartTetheringCallback mStartTetheringCallback; 100 private ConnectivityManager mCm; 101 private EthernetManager mEm; 102 private TetheringManager mTm; 103 private TetheringEventCallback mTetheringEventCallback; 104 private EthernetListener mEthernetListener; 105 106 private WifiTetherPreferenceController mWifiTetherPreferenceController; 107 108 private boolean mUsbConnected; 109 private boolean mMassStorageActive; 110 111 private boolean mBluetoothEnableForTether; 112 private boolean mUnavailable; 113 114 private DataSaverBackend mDataSaverBackend; 115 private boolean mDataSaverEnabled; 116 private Preference mDataSaverFooter; 117 118 @Override getMetricsCategory()119 public int getMetricsCategory() { 120 return SettingsEnums.TETHER; 121 } 122 TetherSettings()123 public TetherSettings() { 124 super(UserManager.DISALLOW_CONFIG_TETHERING); 125 } 126 127 @Override onAttach(Context context)128 public void onAttach(Context context) { 129 super.onAttach(context); 130 mWifiTetherPreferenceController = 131 new WifiTetherPreferenceController(context, getSettingsLifecycle()); 132 } 133 134 @Override onCreate(Bundle icicle)135 public void onCreate(Bundle icicle) { 136 super.onCreate(icicle); 137 138 addPreferencesFromResource(R.xml.tether_prefs); 139 mDataSaverBackend = new DataSaverBackend(getContext()); 140 mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); 141 mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER); 142 143 setIfOnlyAvailableForAdmins(true); 144 if (isUiRestricted()) { 145 mUnavailable = true; 146 getPreferenceScreen().removeAll(); 147 return; 148 } 149 150 final Activity activity = getActivity(); 151 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 152 if (adapter != null) { 153 adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, 154 BluetoothProfile.PAN); 155 } 156 157 mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS); 158 mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING); 159 mEthernetTether = (SwitchPreference) findPreference(KEY_ENABLE_ETHERNET_TETHERING); 160 setFooterPreferenceTitle(); 161 162 mDataSaverBackend.addListener(this); 163 164 mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 165 mEm = (EthernetManager) getSystemService(Context.ETHERNET_SERVICE); 166 mTm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE); 167 168 mUsbRegexs = mCm.getTetherableUsbRegexs(); 169 mBluetoothRegexs = mCm.getTetherableBluetoothRegexs(); 170 mEthernetRegex = getContext().getResources().getString( 171 com.android.internal.R.string.config_ethernet_iface_regex); 172 173 final boolean usbAvailable = mUsbRegexs.length != 0; 174 final boolean bluetoothAvailable = adapter != null && mBluetoothRegexs.length != 0; 175 final boolean ethernetAvailable = !TextUtils.isEmpty(mEthernetRegex); 176 177 if (!usbAvailable || Utils.isMonkeyRunning()) { 178 getPreferenceScreen().removePreference(mUsbTether); 179 } 180 181 mWifiTetherPreferenceController.displayPreference(getPreferenceScreen()); 182 183 if (!bluetoothAvailable) { 184 getPreferenceScreen().removePreference(mBluetoothTether); 185 } else { 186 BluetoothPan pan = mBluetoothPan.get(); 187 if (pan != null && pan.isTetheringOn()) { 188 mBluetoothTether.setChecked(true); 189 } else { 190 mBluetoothTether.setChecked(false); 191 } 192 } 193 if (!ethernetAvailable) getPreferenceScreen().removePreference(mEthernetTether); 194 // Set initial state based on Data Saver mode. 195 onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled()); 196 } 197 198 @Override onDestroy()199 public void onDestroy() { 200 mDataSaverBackend.remListener(this); 201 202 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 203 BluetoothProfile profile = mBluetoothPan.getAndSet(null); 204 if (profile != null && adapter != null) { 205 adapter.closeProfileProxy(BluetoothProfile.PAN, profile); 206 } 207 208 super.onDestroy(); 209 } 210 211 @Override onDataSaverChanged(boolean isDataSaving)212 public void onDataSaverChanged(boolean isDataSaving) { 213 mDataSaverEnabled = isDataSaving; 214 mUsbTether.setEnabled(!mDataSaverEnabled); 215 mBluetoothTether.setEnabled(!mDataSaverEnabled); 216 mEthernetTether.setEnabled(!mDataSaverEnabled); 217 mDataSaverFooter.setVisible(mDataSaverEnabled); 218 } 219 220 @Override onWhitelistStatusChanged(int uid, boolean isWhitelisted)221 public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) { 222 } 223 224 @Override onBlacklistStatusChanged(int uid, boolean isBlacklisted)225 public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) { 226 } 227 228 @VisibleForTesting setFooterPreferenceTitle()229 void setFooterPreferenceTitle() { 230 final Preference footerPreference = findPreference(KEY_TETHER_PREFS_FOOTER); 231 final WifiManager wifiManager = 232 (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE); 233 if (wifiManager.isStaApConcurrencySupported()) { 234 footerPreference.setTitle(R.string.tethering_footer_info_sta_ap_concurrency); 235 } else { 236 footerPreference.setTitle(R.string.tethering_footer_info); 237 } 238 } 239 240 private class TetherChangeReceiver extends BroadcastReceiver { 241 @Override onReceive(Context content, Intent intent)242 public void onReceive(Context content, Intent intent) { 243 String action = intent.getAction(); 244 // TODO: stop using ACTION_TETHER_STATE_CHANGED and use mTetheringEventCallback instead. 245 if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { 246 // TODO - this should understand the interface types 247 ArrayList<String> available = intent.getStringArrayListExtra( 248 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 249 ArrayList<String> active = intent.getStringArrayListExtra( 250 ConnectivityManager.EXTRA_ACTIVE_TETHER); 251 ArrayList<String> errored = intent.getStringArrayListExtra( 252 ConnectivityManager.EXTRA_ERRORED_TETHER); 253 updateState(available.toArray(new String[available.size()]), 254 active.toArray(new String[active.size()]), 255 errored.toArray(new String[errored.size()])); 256 } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) { 257 mMassStorageActive = true; 258 updateState(); 259 } else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) { 260 mMassStorageActive = false; 261 updateState(); 262 } else if (action.equals(UsbManager.ACTION_USB_STATE)) { 263 mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); 264 updateState(); 265 } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 266 if (mBluetoothEnableForTether) { 267 switch (intent 268 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { 269 case BluetoothAdapter.STATE_ON: 270 startTethering(TETHERING_BLUETOOTH); 271 mBluetoothEnableForTether = false; 272 break; 273 274 case BluetoothAdapter.STATE_OFF: 275 case BluetoothAdapter.ERROR: 276 mBluetoothEnableForTether = false; 277 break; 278 279 default: 280 // ignore transition states 281 } 282 } 283 updateState(); 284 } 285 } 286 } 287 288 @Override onStart()289 public void onStart() { 290 super.onStart(); 291 292 if (mUnavailable) { 293 if (!isUiRestrictedByOnlyAdmin()) { 294 getEmptyTextView().setText(R.string.tethering_settings_not_available); 295 } 296 getPreferenceScreen().removeAll(); 297 return; 298 } 299 300 final Activity activity = getActivity(); 301 302 mStartTetheringCallback = new OnStartTetheringCallback(this); 303 mTetheringEventCallback = new TetheringEventCallback(); 304 mTm.registerTetheringEventCallback(new HandlerExecutor(mHandler), mTetheringEventCallback); 305 306 mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState()); 307 mTetherChangeReceiver = new TetherChangeReceiver(); 308 IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 309 Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); 310 311 filter = new IntentFilter(); 312 filter.addAction(UsbManager.ACTION_USB_STATE); 313 activity.registerReceiver(mTetherChangeReceiver, filter); 314 315 filter = new IntentFilter(); 316 filter.addAction(Intent.ACTION_MEDIA_SHARED); 317 filter.addAction(Intent.ACTION_MEDIA_UNSHARED); 318 filter.addDataScheme("file"); 319 activity.registerReceiver(mTetherChangeReceiver, filter); 320 321 filter = new IntentFilter(); 322 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 323 activity.registerReceiver(mTetherChangeReceiver, filter); 324 325 if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); 326 327 mEthernetListener = new EthernetListener(); 328 if (mEm != null) 329 mEm.addListener(mEthernetListener); 330 331 updateState(); 332 } 333 334 @Override onStop()335 public void onStop() { 336 super.onStop(); 337 338 if (mUnavailable) { 339 return; 340 } 341 getActivity().unregisterReceiver(mTetherChangeReceiver); 342 mTm.unregisterTetheringEventCallback(mTetheringEventCallback); 343 if (mEm != null) 344 mEm.removeListener(mEthernetListener); 345 mTetherChangeReceiver = null; 346 mStartTetheringCallback = null; 347 mTetheringEventCallback = null; 348 mEthernetListener = null; 349 } 350 updateState()351 private void updateState() { 352 String[] available = mCm.getTetherableIfaces(); 353 String[] tethered = mCm.getTetheredIfaces(); 354 String[] errored = mCm.getTetheringErroredIfaces(); 355 updateState(available, tethered, errored); 356 } 357 updateState(String[] available, String[] tethered, String[] errored)358 private void updateState(String[] available, String[] tethered, 359 String[] errored) { 360 updateUsbState(available, tethered, errored); 361 updateBluetoothState(); 362 updateEthernetState(available, tethered); 363 } 364 updateUsbState(String[] available, String[] tethered, String[] errored)365 private void updateUsbState(String[] available, String[] tethered, 366 String[] errored) { 367 boolean usbAvailable = mUsbConnected && !mMassStorageActive; 368 int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR; 369 for (String s : available) { 370 for (String regex : mUsbRegexs) { 371 if (s.matches(regex)) { 372 if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) { 373 usbError = mCm.getLastTetherError(s); 374 } 375 } 376 } 377 } 378 boolean usbTethered = false; 379 for (String s : tethered) { 380 for (String regex : mUsbRegexs) { 381 if (s.matches(regex)) usbTethered = true; 382 } 383 } 384 boolean usbErrored = false; 385 for (String s: errored) { 386 for (String regex : mUsbRegexs) { 387 if (s.matches(regex)) usbErrored = true; 388 } 389 } 390 391 if (usbTethered) { 392 mUsbTether.setEnabled(!mDataSaverEnabled); 393 mUsbTether.setChecked(true); 394 } else if (usbAvailable) { 395 mUsbTether.setEnabled(!mDataSaverEnabled); 396 mUsbTether.setChecked(false); 397 } else { 398 mUsbTether.setEnabled(false); 399 mUsbTether.setChecked(false); 400 } 401 } 402 updateBluetoothState()403 private void updateBluetoothState() { 404 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 405 if (adapter == null) { 406 return; 407 } 408 int btState = adapter.getState(); 409 if (btState == BluetoothAdapter.STATE_TURNING_OFF) { 410 mBluetoothTether.setEnabled(false); 411 } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { 412 mBluetoothTether.setEnabled(false); 413 } else { 414 BluetoothPan bluetoothPan = mBluetoothPan.get(); 415 if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null 416 && bluetoothPan.isTetheringOn()) { 417 mBluetoothTether.setChecked(true); 418 mBluetoothTether.setEnabled(!mDataSaverEnabled); 419 } else { 420 mBluetoothTether.setEnabled(!mDataSaverEnabled); 421 mBluetoothTether.setChecked(false); 422 } 423 } 424 } 425 updateEthernetState(String[] available, String[] tethered)426 private void updateEthernetState(String[] available, String[] tethered) { 427 428 boolean isAvailable = false; 429 boolean isTethered = false; 430 431 for (String s : available) { 432 if (s.matches(mEthernetRegex)) isAvailable = true; 433 } 434 435 for (String s : tethered) { 436 if (s.matches(mEthernetRegex)) isTethered = true; 437 } 438 439 if (isTethered) { 440 mEthernetTether.setEnabled(!mDataSaverEnabled); 441 mEthernetTether.setChecked(true); 442 } else if (isAvailable || (mEm != null && mEm.isAvailable())) { 443 mEthernetTether.setEnabled(!mDataSaverEnabled); 444 mEthernetTether.setChecked(false); 445 } else { 446 mEthernetTether.setEnabled(false); 447 mEthernetTether.setChecked(false); 448 } 449 } 450 startTethering(int choice)451 private void startTethering(int choice) { 452 if (choice == TETHERING_BLUETOOTH) { 453 // Turn on Bluetooth first. 454 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 455 if (adapter.getState() == BluetoothAdapter.STATE_OFF) { 456 mBluetoothEnableForTether = true; 457 adapter.enable(); 458 mBluetoothTether.setEnabled(false); 459 return; 460 } 461 } 462 463 mCm.startTethering(choice, true, mStartTetheringCallback, mHandler); 464 } 465 466 @Override onPreferenceTreeClick(Preference preference)467 public boolean onPreferenceTreeClick(Preference preference) { 468 if (preference == mUsbTether) { 469 if (mUsbTether.isChecked()) { 470 startTethering(TETHERING_USB); 471 } else { 472 mCm.stopTethering(TETHERING_USB); 473 } 474 } else if (preference == mBluetoothTether) { 475 if (mBluetoothTether.isChecked()) { 476 startTethering(TETHERING_BLUETOOTH); 477 } else { 478 mCm.stopTethering(TETHERING_BLUETOOTH); 479 } 480 } else if (preference == mEthernetTether) { 481 if (mEthernetTether.isChecked()) { 482 startTethering(TETHERING_ETHERNET); 483 } else { 484 mCm.stopTethering(TETHERING_ETHERNET); 485 } 486 } 487 488 return super.onPreferenceTreeClick(preference); 489 } 490 491 @Override getHelpResource()492 public int getHelpResource() { 493 return R.string.help_url_tether; 494 } 495 496 private BluetoothProfile.ServiceListener mProfileServiceListener = 497 new BluetoothProfile.ServiceListener() { 498 public void onServiceConnected(int profile, BluetoothProfile proxy) { 499 mBluetoothPan.set((BluetoothPan) proxy); 500 } 501 public void onServiceDisconnected(int profile) { 502 mBluetoothPan.set(null); 503 } 504 }; 505 506 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 507 new BaseSearchIndexProvider() { 508 @Override 509 public List<SearchIndexableResource> getXmlResourcesToIndex( 510 Context context, boolean enabled) { 511 final SearchIndexableResource sir = new SearchIndexableResource(context); 512 sir.xmlResId = R.xml.tether_prefs; 513 return Arrays.asList(sir); 514 } 515 516 @Override 517 protected boolean isPageSearchEnabled(Context context) { 518 return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE); 519 } 520 521 @Override 522 public List<String> getNonIndexableKeys(Context context) { 523 final List<String> keys = super.getNonIndexableKeys(context); 524 final ConnectivityManager cm = 525 context.getSystemService(ConnectivityManager.class); 526 527 if (!TetherUtil.isTetherAvailable(context)) { 528 keys.add(KEY_TETHER_PREFS_SCREEN); 529 keys.add(KEY_WIFI_TETHER); 530 } 531 532 final boolean usbAvailable = 533 cm.getTetherableUsbRegexs().length != 0; 534 if (!usbAvailable || Utils.isMonkeyRunning()) { 535 keys.add(KEY_USB_TETHER_SETTINGS); 536 } 537 538 final boolean bluetoothAvailable = 539 cm.getTetherableBluetoothRegexs().length != 0; 540 if (!bluetoothAvailable) { 541 keys.add(KEY_ENABLE_BLUETOOTH_TETHERING); 542 } 543 544 final boolean ethernetAvailable = !TextUtils.isEmpty( 545 context.getResources().getString( 546 com.android.internal.R.string.config_ethernet_iface_regex)); 547 if (!ethernetAvailable) { 548 keys.add(KEY_ENABLE_ETHERNET_TETHERING); 549 } 550 return keys; 551 } 552 }; 553 554 private static final class OnStartTetheringCallback extends 555 ConnectivityManager.OnStartTetheringCallback { 556 final WeakReference<TetherSettings> mTetherSettings; 557 OnStartTetheringCallback(TetherSettings settings)558 OnStartTetheringCallback(TetherSettings settings) { 559 mTetherSettings = new WeakReference<>(settings); 560 } 561 562 @Override onTetheringStarted()563 public void onTetheringStarted() { 564 update(); 565 } 566 567 @Override onTetheringFailed()568 public void onTetheringFailed() { 569 update(); 570 } 571 update()572 private void update() { 573 TetherSettings settings = mTetherSettings.get(); 574 if (settings != null) { 575 settings.updateState(); 576 } 577 } 578 } 579 580 private final class TetheringEventCallback implements TetheringManager.TetheringEventCallback { 581 @Override onTetheredInterfacesChanged(List<String> interfaces)582 public void onTetheredInterfacesChanged(List<String> interfaces) { 583 updateState(); 584 } 585 } 586 587 private final class EthernetListener implements EthernetManager.Listener { onAvailabilityChanged(String iface, boolean isAvailable)588 public void onAvailabilityChanged(String iface, boolean isAvailable) { 589 mHandler.post(TetherSettings.this::updateState); 590 } 591 } 592 } 593