OSDN Git Service

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