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