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