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 android.app.Activity; 20 import android.app.Dialog; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothPan; 23 import android.bluetooth.BluetoothProfile; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageManager; 30 import android.hardware.usb.UsbManager; 31 import android.net.ConnectivityManager; 32 import android.net.wifi.WifiConfiguration; 33 import android.net.wifi.WifiManager; 34 import android.os.Bundle; 35 import android.os.Environment; 36 import android.os.Handler; 37 import android.os.UserManager; 38 import android.support.v14.preference.SwitchPreference; 39 import android.support.v7.preference.Preference; 40 import android.util.Log; 41 42 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 43 import com.android.settings.datausage.DataSaverBackend; 44 import com.android.settings.wifi.WifiApDialog; 45 import com.android.settings.wifi.WifiApEnabler; 46 import com.android.settings.wifi.tether.WifiTetherPreferenceController; 47 import com.android.settings.wifi.tether.WifiTetherSettings; 48 import com.android.settingslib.TetherUtil; 49 50 import java.lang.ref.WeakReference; 51 import java.util.ArrayList; 52 import java.util.concurrent.atomic.AtomicReference; 53 54 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; 55 import static android.net.ConnectivityManager.TETHERING_USB; 56 import static android.net.ConnectivityManager.TETHERING_WIFI; 57 58 /* 59 * Displays preferences for Tethering. 60 */ 61 public class TetherSettings extends RestrictedSettingsFragment 62 implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, 63 DataSaverBackend.Listener { 64 65 private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; 66 private static final String ENABLE_WIFI_AP = "enable_wifi_ap"; 67 private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; 68 private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver"; 69 70 private static final int DIALOG_AP_SETTINGS = 1; 71 72 private static final String TAG = "TetheringSettings"; 73 74 private SwitchPreference mUsbTether; 75 76 private WifiApEnabler mWifiApEnabler; 77 private SwitchPreference mEnableWifiAp; 78 79 private SwitchPreference mBluetoothTether; 80 81 private BroadcastReceiver mTetherChangeReceiver; 82 83 private String[] mUsbRegexs; 84 85 private String[] mWifiRegexs; 86 87 private String[] mBluetoothRegexs; 88 private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<BluetoothPan>(); 89 90 private Handler mHandler = new Handler(); 91 private OnStartTetheringCallback mStartTetheringCallback; 92 93 private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security"; 94 private static final int CONFIG_SUBTEXT = R.string.wifi_tether_configure_subtext; 95 96 private String[] mSecurityType; 97 private Preference mCreateNetwork; 98 99 private WifiApDialog mDialog; 100 private WifiManager mWifiManager; 101 private WifiConfiguration mWifiConfig = null; 102 private ConnectivityManager mCm; 103 104 private WifiTetherPreferenceController mWifiTetherPreferenceController; 105 106 private boolean mRestartWifiApAfterConfigChange; 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 119 public int getMetricsCategory() { 120 return MetricsEvent.TETHER; 121 } 122 123 public TetherSettings() { 124 super(UserManager.DISALLOW_CONFIG_TETHERING); 125 } 126 127 @Override 128 public void onAttach(Context context) { 129 super.onAttach(context); 130 mWifiTetherPreferenceController = 131 new WifiTetherPreferenceController(context, getLifecycle()); 132 } 133 134 @Override 135 public void onCreate(Bundle icicle) { 136 super.onCreate(icicle); 137 138 addPreferencesFromResource(R.xml.tether_prefs); 139 mFooterPreferenceMixin.createFooterPreference() 140 .setTitle(R.string.tethering_footer_info); 141 142 mDataSaverBackend = new DataSaverBackend(getContext()); 143 mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); 144 mDataSaverFooter = findPreference(DATA_SAVER_FOOTER); 145 146 setIfOnlyAvailableForAdmins(true); 147 if (isUiRestricted()) { 148 mUnavailable = true; 149 getPreferenceScreen().removeAll(); 150 return; 151 } 152 153 final Activity activity = getActivity(); 154 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 155 if (adapter != null) { 156 adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, 157 BluetoothProfile.PAN); 158 } 159 160 mEnableWifiAp = 161 (SwitchPreference) findPreference(ENABLE_WIFI_AP); 162 163 Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY); 164 mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS); 165 mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); 166 167 mDataSaverBackend.addListener(this); 168 169 mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 170 mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); 171 172 mUsbRegexs = mCm.getTetherableUsbRegexs(); 173 mWifiRegexs = mCm.getTetherableWifiRegexs(); 174 mBluetoothRegexs = mCm.getTetherableBluetoothRegexs(); 175 176 final boolean usbAvailable = mUsbRegexs.length != 0; 177 final boolean wifiAvailable = mWifiRegexs.length != 0; 178 final boolean bluetoothAvailable = mBluetoothRegexs.length != 0; 179 180 if (!usbAvailable || Utils.isMonkeyRunning()) { 181 getPreferenceScreen().removePreference(mUsbTether); 182 } 183 184 mWifiTetherPreferenceController.displayPreference(getPreferenceScreen()); 185 if (WifiTetherSettings.isTetherSettingPageEnabled()) { 186 removePreference(ENABLE_WIFI_AP); 187 removePreference(WIFI_AP_SSID_AND_SECURITY); 188 } else { 189 if (wifiAvailable && !Utils.isMonkeyRunning()) { 190 mWifiApEnabler = new WifiApEnabler(activity, mDataSaverBackend, mEnableWifiAp); 191 initWifiTethering(); 192 } else { 193 getPreferenceScreen().removePreference(mEnableWifiAp); 194 getPreferenceScreen().removePreference(wifiApSettings); 195 } 196 } 197 198 if (!bluetoothAvailable) { 199 getPreferenceScreen().removePreference(mBluetoothTether); 200 } else { 201 BluetoothPan pan = mBluetoothPan.get(); 202 if (pan != null && pan.isTetheringOn()) { 203 mBluetoothTether.setChecked(true); 204 } else { 205 mBluetoothTether.setChecked(false); 206 } 207 } 208 // Set initial state based on Data Saver mode. 209 onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled()); 210 } 211 212 @Override 213 public void onDestroy() { 214 mDataSaverBackend.remListener(this); 215 216 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 217 BluetoothProfile profile = mBluetoothPan.getAndSet(null); 218 if (profile != null && adapter != null) { 219 adapter.closeProfileProxy(BluetoothProfile.PAN, profile); 220 } 221 222 super.onDestroy(); 223 } 224 225 @Override 226 public void onDataSaverChanged(boolean isDataSaving) { 227 mDataSaverEnabled = isDataSaving; 228 mEnableWifiAp.setEnabled(!mDataSaverEnabled); 229 mUsbTether.setEnabled(!mDataSaverEnabled); 230 mBluetoothTether.setEnabled(!mDataSaverEnabled); 231 mDataSaverFooter.setVisible(mDataSaverEnabled); 232 } 233 234 @Override 235 public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) { 236 } 237 238 @Override 239 public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) { 240 } 241 242 private void initWifiTethering() { 243 final Activity activity = getActivity(); 244 mWifiConfig = mWifiManager.getWifiApConfiguration(); 245 mSecurityType = getResources().getStringArray(R.array.wifi_ap_security); 246 247 mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY); 248 249 mRestartWifiApAfterConfigChange = false; 250 251 if (mWifiConfig == null) { 252 final String s = activity.getString( 253 com.android.internal.R.string.wifi_tether_configure_ssid_default); 254 mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT), 255 s, mSecurityType[WifiApDialog.OPEN_INDEX])); 256 } else { 257 int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig); 258 mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT), 259 mWifiConfig.SSID, 260 mSecurityType[index])); 261 } 262 } 263 264 @Override 265 public Dialog onCreateDialog(int id) { 266 if (id == DIALOG_AP_SETTINGS) { 267 final Activity activity = getActivity(); 268 mDialog = new WifiApDialog(activity, this, mWifiConfig); 269 return mDialog; 270 } 271 272 return null; 273 } 274 275 @Override 276 public int getDialogMetricsCategory(int dialogId) { 277 if (dialogId == DIALOG_AP_SETTINGS) { 278 return MetricsEvent.DIALOG_AP_SETTINGS; 279 } 280 return 0; 281 } 282 283 private class TetherChangeReceiver extends BroadcastReceiver { 284 @Override 285 public void onReceive(Context content, Intent intent) { 286 String action = intent.getAction(); 287 if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { 288 // TODO - this should understand the interface types 289 ArrayList<String> available = intent.getStringArrayListExtra( 290 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 291 ArrayList<String> active = intent.getStringArrayListExtra( 292 ConnectivityManager.EXTRA_ACTIVE_TETHER); 293 ArrayList<String> errored = intent.getStringArrayListExtra( 294 ConnectivityManager.EXTRA_ERRORED_TETHER); 295 updateState(available.toArray(new String[available.size()]), 296 active.toArray(new String[active.size()]), 297 errored.toArray(new String[errored.size()])); 298 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED 299 && mRestartWifiApAfterConfigChange) { 300 mRestartWifiApAfterConfigChange = false; 301 Log.d(TAG, "Restarting WifiAp due to prior config change."); 302 startTethering(TETHERING_WIFI); 303 } 304 } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) { 305 int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); 306 if (state == WifiManager.WIFI_AP_STATE_DISABLED 307 && mRestartWifiApAfterConfigChange) { 308 mRestartWifiApAfterConfigChange = false; 309 Log.d(TAG, "Restarting WifiAp due to prior config change."); 310 startTethering(TETHERING_WIFI); 311 } 312 } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) { 313 mMassStorageActive = true; 314 updateState(); 315 } else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) { 316 mMassStorageActive = false; 317 updateState(); 318 } else if (action.equals(UsbManager.ACTION_USB_STATE)) { 319 mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); 320 updateState(); 321 } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 322 if (mBluetoothEnableForTether) { 323 switch (intent 324 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { 325 case BluetoothAdapter.STATE_ON: 326 startTethering(TETHERING_BLUETOOTH); 327 mBluetoothEnableForTether = false; 328 break; 329 330 case BluetoothAdapter.STATE_OFF: 331 case BluetoothAdapter.ERROR: 332 mBluetoothEnableForTether = false; 333 break; 334 335 default: 336 // ignore transition states 337 } 338 } 339 updateState(); 340 } 341 } 342 } 343 344 @Override 345 public void onStart() { 346 super.onStart(); 347 348 if (mUnavailable) { 349 if (!isUiRestrictedByOnlyAdmin()) { 350 getEmptyTextView().setText(R.string.tethering_settings_not_available); 351 } 352 getPreferenceScreen().removeAll(); 353 return; 354 } 355 356 final Activity activity = getActivity(); 357 358 mStartTetheringCallback = new OnStartTetheringCallback(this); 359 360 mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState()); 361 mTetherChangeReceiver = new TetherChangeReceiver(); 362 IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 363 filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); 364 Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); 365 366 filter = new IntentFilter(); 367 filter.addAction(UsbManager.ACTION_USB_STATE); 368 activity.registerReceiver(mTetherChangeReceiver, filter); 369 370 filter = new IntentFilter(); 371 filter.addAction(Intent.ACTION_MEDIA_SHARED); 372 filter.addAction(Intent.ACTION_MEDIA_UNSHARED); 373 filter.addDataScheme("file"); 374 activity.registerReceiver(mTetherChangeReceiver, filter); 375 376 filter = new IntentFilter(); 377 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 378 activity.registerReceiver(mTetherChangeReceiver, filter); 379 380 if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); 381 if (mWifiApEnabler != null) { 382 mEnableWifiAp.setOnPreferenceChangeListener(this); 383 mWifiApEnabler.resume(); 384 } 385 386 updateState(); 387 } 388 389 @Override 390 public void onStop() { 391 super.onStop(); 392 393 if (mUnavailable) { 394 return; 395 } 396 getActivity().unregisterReceiver(mTetherChangeReceiver); 397 mTetherChangeReceiver = null; 398 mStartTetheringCallback = null; 399 if (mWifiApEnabler != null) { 400 mEnableWifiAp.setOnPreferenceChangeListener(null); 401 mWifiApEnabler.pause(); 402 } 403 } 404 405 private void updateState() { 406 String[] available = mCm.getTetherableIfaces(); 407 String[] tethered = mCm.getTetheredIfaces(); 408 String[] errored = mCm.getTetheringErroredIfaces(); 409 updateState(available, tethered, errored); 410 } 411 412 private void updateState(String[] available, String[] tethered, 413 String[] errored) { 414 updateUsbState(available, tethered, errored); 415 updateBluetoothState(); 416 } 417 418 419 private void updateUsbState(String[] available, String[] tethered, 420 String[] errored) { 421 boolean usbAvailable = mUsbConnected && !mMassStorageActive; 422 int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR; 423 for (String s : available) { 424 for (String regex : mUsbRegexs) { 425 if (s.matches(regex)) { 426 if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) { 427 usbError = mCm.getLastTetherError(s); 428 } 429 } 430 } 431 } 432 boolean usbTethered = false; 433 for (String s : tethered) { 434 for (String regex : mUsbRegexs) { 435 if (s.matches(regex)) usbTethered = true; 436 } 437 } 438 boolean usbErrored = false; 439 for (String s: errored) { 440 for (String regex : mUsbRegexs) { 441 if (s.matches(regex)) usbErrored = true; 442 } 443 } 444 445 if (usbTethered) { 446 mUsbTether.setEnabled(!mDataSaverEnabled); 447 mUsbTether.setChecked(true); 448 } else if (usbAvailable) { 449 mUsbTether.setEnabled(!mDataSaverEnabled); 450 mUsbTether.setChecked(false); 451 } else { 452 mUsbTether.setEnabled(false); 453 mUsbTether.setChecked(false); 454 } 455 } 456 457 private void updateBluetoothState() { 458 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 459 if (adapter == null) { 460 return; 461 } 462 int btState = adapter.getState(); 463 if (btState == BluetoothAdapter.STATE_TURNING_OFF) { 464 mBluetoothTether.setEnabled(false); 465 } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { 466 mBluetoothTether.setEnabled(false); 467 } else { 468 BluetoothPan bluetoothPan = mBluetoothPan.get(); 469 if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null 470 && bluetoothPan.isTetheringOn()) { 471 mBluetoothTether.setChecked(true); 472 mBluetoothTether.setEnabled(!mDataSaverEnabled); 473 } else { 474 mBluetoothTether.setEnabled(!mDataSaverEnabled); 475 mBluetoothTether.setChecked(false); 476 } 477 } 478 } 479 480 @Override 481 public boolean onPreferenceChange(Preference preference, Object value) { 482 boolean enable = (Boolean) value; 483 484 if (enable) { 485 startTethering(TETHERING_WIFI); 486 } else { 487 mCm.stopTethering(TETHERING_WIFI); 488 } 489 return false; 490 } 491 492 public static boolean isProvisioningNeededButUnavailable(Context context) { 493 return (TetherUtil.isProvisioningNeeded(context) 494 && !isIntentAvailable(context)); 495 } 496 497 private static boolean isIntentAvailable(Context context) { 498 String[] provisionApp = context.getResources().getStringArray( 499 com.android.internal.R.array.config_mobile_hotspot_provision_app); 500 if (provisionApp.length < 2) { 501 return false; 502 } 503 final PackageManager packageManager = context.getPackageManager(); 504 Intent intent = new Intent(Intent.ACTION_MAIN); 505 intent.setClassName(provisionApp[0], provisionApp[1]); 506 507 return (packageManager.queryIntentActivities(intent, 508 PackageManager.MATCH_DEFAULT_ONLY).size() > 0); 509 } 510 511 private void startTethering(int choice) { 512 if (choice == TETHERING_BLUETOOTH) { 513 // Turn on Bluetooth first. 514 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 515 if (adapter.getState() == BluetoothAdapter.STATE_OFF) { 516 mBluetoothEnableForTether = true; 517 adapter.enable(); 518 mBluetoothTether.setSummary(R.string.bluetooth_turning_on); 519 mBluetoothTether.setEnabled(false); 520 return; 521 } 522 } 523 524 mCm.startTethering(choice, true, mStartTetheringCallback, mHandler); 525 } 526 527 @Override 528 public boolean onPreferenceTreeClick(Preference preference) { 529 if (preference == mUsbTether) { 530 if (mUsbTether.isChecked()) { 531 startTethering(TETHERING_USB); 532 } else { 533 mCm.stopTethering(TETHERING_USB); 534 } 535 } else if (preference == mBluetoothTether) { 536 if (mBluetoothTether.isChecked()) { 537 startTethering(TETHERING_BLUETOOTH); 538 } else { 539 mCm.stopTethering(TETHERING_BLUETOOTH); 540 // No ACTION_TETHER_STATE_CHANGED is fired or bluetooth unless a device is 541 // connected. Need to update state manually. 542 updateState(); 543 } 544 } else if (preference == mCreateNetwork) { 545 showDialog(DIALOG_AP_SETTINGS); 546 } 547 548 return super.onPreferenceTreeClick(preference); 549 } 550 551 public void onClick(DialogInterface dialogInterface, int button) { 552 if (button == DialogInterface.BUTTON_POSITIVE) { 553 mWifiConfig = mDialog.getConfig(); 554 if (mWifiConfig != null) { 555 /** 556 * if soft AP is stopped, bring up 557 * else restart with new config 558 * TODO: update config on a running access point when framework support is added 559 */ 560 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { 561 Log.d("TetheringSettings", 562 "Wifi AP config changed while enabled, stop and restart"); 563 mRestartWifiApAfterConfigChange = true; 564 mCm.stopTethering(TETHERING_WIFI); 565 } 566 mWifiManager.setWifiApConfiguration(mWifiConfig); 567 int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig); 568 mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT), 569 mWifiConfig.SSID, 570 mSecurityType[index])); 571 } 572 } 573 } 574 575 @Override 576 public int getHelpResource() { 577 return R.string.help_url_tether; 578 } 579 580 private BluetoothProfile.ServiceListener mProfileServiceListener = 581 new BluetoothProfile.ServiceListener() { 582 public void onServiceConnected(int profile, BluetoothProfile proxy) { 583 mBluetoothPan.set((BluetoothPan) proxy); 584 } 585 public void onServiceDisconnected(int profile) { 586 mBluetoothPan.set(null); 587 } 588 }; 589 590 private static final class OnStartTetheringCallback extends 591 ConnectivityManager.OnStartTetheringCallback { 592 final WeakReference<TetherSettings> mTetherSettings; 593 594 OnStartTetheringCallback(TetherSettings settings) { 595 mTetherSettings = new WeakReference<TetherSettings>(settings); 596 } 597 598 @Override 599 public void onTetheringStarted() { 600 update(); 601 } 602 603 @Override 604 public void onTetheringFailed() { 605 update(); 606 } 607 608 private void update() { 609 TetherSettings settings = mTetherSettings.get(); 610 if (settings != null) { 611 settings.updateState(); 612 } 613 } 614 } 615 } 616