• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.settings.wifi.WifiApEnabler;
20 import com.android.settings.wifi.WifiApDialog;
21 
22 import android.app.Activity;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
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.DialogInterface;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.res.AssetManager;
34 import android.hardware.usb.UsbManager;
35 import android.net.ConnectivityManager;
36 import android.net.wifi.WifiConfiguration;
37 import android.net.wifi.WifiManager;
38 import android.os.Bundle;
39 import android.os.Environment;
40 import android.os.SystemProperties;
41 import android.preference.CheckBoxPreference;
42 import android.preference.Preference;
43 import android.preference.PreferenceScreen;
44 import android.text.TextUtils;
45 import android.view.ViewGroup;
46 import android.view.ViewParent;
47 import android.webkit.WebView;
48 
49 import java.io.InputStream;
50 import java.util.ArrayList;
51 import java.util.concurrent.atomic.AtomicReference;
52 import java.util.Locale;
53 
54 /*
55  * Displays preferences for Tethering.
56  */
57 public class TetherSettings extends SettingsPreferenceFragment
58         implements DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener {
59     private static final String TAG = "TetherSettings";
60 
61     private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
62     private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
63     private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
64 
65     private static final int DIALOG_AP_SETTINGS = 1;
66 
67     private WebView mView;
68     private CheckBoxPreference mUsbTether;
69 
70     private WifiApEnabler mWifiApEnabler;
71     private CheckBoxPreference mEnableWifiAp;
72 
73     private CheckBoxPreference mBluetoothTether;
74 
75     private BroadcastReceiver mTetherChangeReceiver;
76 
77     private String[] mUsbRegexs;
78 
79     private String[] mWifiRegexs;
80 
81     private String[] mBluetoothRegexs;
82     private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<BluetoothPan>();
83 
84     private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security";
85     private static final int CONFIG_SUBTEXT = R.string.wifi_tether_configure_subtext;
86 
87     private String[] mSecurityType;
88     private Preference mCreateNetwork;
89 
90     private WifiApDialog mDialog;
91     private WifiManager mWifiManager;
92     private WifiConfiguration mWifiConfig = null;
93 
94     private boolean mUsbConnected;
95     private boolean mMassStorageActive;
96 
97     private boolean mBluetoothEnableForTether;
98 
99     private static final int INVALID             = -1;
100     private static final int WIFI_TETHERING      = 0;
101     private static final int USB_TETHERING       = 1;
102     private static final int BLUETOOTH_TETHERING = 2;
103 
104     /* One of INVALID, WIFI_TETHERING, USB_TETHERING or BLUETOOTH_TETHERING */
105     private int mTetherChoice = INVALID;
106 
107     /* Stores the package name and the class name of the provisioning app */
108     private String[] mProvisionApp;
109     private static final int PROVISION_REQUEST = 0;
110 
111     @Override
onCreate(Bundle icicle)112     public void onCreate(Bundle icicle) {
113         super.onCreate(icicle);
114         addPreferencesFromResource(R.xml.tether_prefs);
115 
116         final Activity activity = getActivity();
117         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
118         if (adapter != null) {
119             adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
120                     BluetoothProfile.PAN);
121         }
122 
123         mEnableWifiAp =
124                 (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
125         Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY);
126         mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
127         mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
128 
129         ConnectivityManager cm =
130                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
131 
132         mUsbRegexs = cm.getTetherableUsbRegexs();
133         mWifiRegexs = cm.getTetherableWifiRegexs();
134         mBluetoothRegexs = cm.getTetherableBluetoothRegexs();
135 
136         final boolean usbAvailable = mUsbRegexs.length != 0;
137         final boolean wifiAvailable = mWifiRegexs.length != 0;
138         final boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
139 
140         if (!usbAvailable || Utils.isMonkeyRunning()) {
141             getPreferenceScreen().removePreference(mUsbTether);
142         }
143 
144         if (wifiAvailable && !Utils.isMonkeyRunning()) {
145             mWifiApEnabler = new WifiApEnabler(activity, mEnableWifiAp);
146             initWifiTethering();
147         } else {
148             getPreferenceScreen().removePreference(mEnableWifiAp);
149             getPreferenceScreen().removePreference(wifiApSettings);
150         }
151 
152         if (!bluetoothAvailable) {
153             getPreferenceScreen().removePreference(mBluetoothTether);
154         } else {
155             BluetoothPan pan = mBluetoothPan.get();
156             if (pan != null && pan.isTetheringOn()) {
157                 mBluetoothTether.setChecked(true);
158             } else {
159                 mBluetoothTether.setChecked(false);
160             }
161         }
162 
163         mProvisionApp = getResources().getStringArray(
164                 com.android.internal.R.array.config_mobile_hotspot_provision_app);
165 
166         mView = new WebView(activity);
167     }
168 
initWifiTethering()169     private void initWifiTethering() {
170         final Activity activity = getActivity();
171         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
172         mWifiConfig = mWifiManager.getWifiApConfiguration();
173         mSecurityType = getResources().getStringArray(R.array.wifi_ap_security);
174 
175         mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY);
176 
177         if (mWifiConfig == null) {
178             final String s = activity.getString(
179                     com.android.internal.R.string.wifi_tether_configure_ssid_default);
180             mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
181                     s, mSecurityType[WifiApDialog.OPEN_INDEX]));
182         } else {
183             int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
184             mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
185                     mWifiConfig.SSID,
186                     mSecurityType[index]));
187         }
188     }
189 
190     private BluetoothProfile.ServiceListener mProfileServiceListener =
191         new BluetoothProfile.ServiceListener() {
192         public void onServiceConnected(int profile, BluetoothProfile proxy) {
193             mBluetoothPan.set((BluetoothPan) proxy);
194         }
195         public void onServiceDisconnected(int profile) {
196             mBluetoothPan.set(null);
197         }
198     };
199 
200     @Override
onCreateDialog(int id)201     public Dialog onCreateDialog(int id) {
202         if (id == DIALOG_AP_SETTINGS) {
203             final Activity activity = getActivity();
204             mDialog = new WifiApDialog(activity, this, mWifiConfig);
205             return mDialog;
206         }
207 
208         return null;
209     }
210 
211     private class TetherChangeReceiver extends BroadcastReceiver {
212         @Override
onReceive(Context content, Intent intent)213         public void onReceive(Context content, Intent intent) {
214             String action = intent.getAction();
215             if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
216                 // TODO - this should understand the interface types
217                 ArrayList<String> available = intent.getStringArrayListExtra(
218                         ConnectivityManager.EXTRA_AVAILABLE_TETHER);
219                 ArrayList<String> active = intent.getStringArrayListExtra(
220                         ConnectivityManager.EXTRA_ACTIVE_TETHER);
221                 ArrayList<String> errored = intent.getStringArrayListExtra(
222                         ConnectivityManager.EXTRA_ERRORED_TETHER);
223                 updateState(available.toArray(new String[available.size()]),
224                         active.toArray(new String[active.size()]),
225                         errored.toArray(new String[errored.size()]));
226             } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
227                 mMassStorageActive = true;
228                 updateState();
229             } else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
230                 mMassStorageActive = false;
231                 updateState();
232             } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
233                 mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
234                 updateState();
235             } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
236                 if (mBluetoothEnableForTether) {
237                     switch (intent
238                             .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
239                         case BluetoothAdapter.STATE_ON:
240                             BluetoothPan bluetoothPan = mBluetoothPan.get();
241                             if (bluetoothPan != null) {
242                                 bluetoothPan.setBluetoothTethering(true);
243                                 mBluetoothEnableForTether = false;
244                             }
245                             break;
246 
247                         case BluetoothAdapter.STATE_OFF:
248                         case BluetoothAdapter.ERROR:
249                             mBluetoothEnableForTether = false;
250                             break;
251 
252                         default:
253                             // ignore transition states
254                     }
255                 }
256                 updateState();
257             }
258         }
259     }
260 
261     @Override
onStart()262     public void onStart() {
263         super.onStart();
264 
265         final Activity activity = getActivity();
266 
267         mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
268         mTetherChangeReceiver = new TetherChangeReceiver();
269         IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
270         Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
271 
272         filter = new IntentFilter();
273         filter.addAction(UsbManager.ACTION_USB_STATE);
274         activity.registerReceiver(mTetherChangeReceiver, filter);
275 
276         filter = new IntentFilter();
277         filter.addAction(Intent.ACTION_MEDIA_SHARED);
278         filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
279         filter.addDataScheme("file");
280         activity.registerReceiver(mTetherChangeReceiver, filter);
281 
282         filter = new IntentFilter();
283         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
284         activity.registerReceiver(mTetherChangeReceiver, filter);
285 
286         if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
287         if (mWifiApEnabler != null) {
288             mEnableWifiAp.setOnPreferenceChangeListener(this);
289             mWifiApEnabler.resume();
290         }
291 
292         updateState();
293     }
294 
295     @Override
onStop()296     public void onStop() {
297         super.onStop();
298         getActivity().unregisterReceiver(mTetherChangeReceiver);
299         mTetherChangeReceiver = null;
300         if (mWifiApEnabler != null) {
301             mEnableWifiAp.setOnPreferenceChangeListener(null);
302             mWifiApEnabler.pause();
303         }
304     }
305 
updateState()306     private void updateState() {
307         ConnectivityManager cm =
308                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
309 
310         String[] available = cm.getTetherableIfaces();
311         String[] tethered = cm.getTetheredIfaces();
312         String[] errored = cm.getTetheringErroredIfaces();
313         updateState(available, tethered, errored);
314     }
315 
updateState(String[] available, String[] tethered, String[] errored)316     private void updateState(String[] available, String[] tethered,
317             String[] errored) {
318         updateUsbState(available, tethered, errored);
319         updateBluetoothState(available, tethered, errored);
320     }
321 
322 
updateUsbState(String[] available, String[] tethered, String[] errored)323     private void updateUsbState(String[] available, String[] tethered,
324             String[] errored) {
325         ConnectivityManager cm =
326                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
327         boolean usbAvailable = mUsbConnected && !mMassStorageActive;
328         int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
329         for (String s : available) {
330             for (String regex : mUsbRegexs) {
331                 if (s.matches(regex)) {
332                     if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
333                         usbError = cm.getLastTetherError(s);
334                     }
335                 }
336             }
337         }
338         boolean usbTethered = false;
339         for (String s : tethered) {
340             for (String regex : mUsbRegexs) {
341                 if (s.matches(regex)) usbTethered = true;
342             }
343         }
344         boolean usbErrored = false;
345         for (String s: errored) {
346             for (String regex : mUsbRegexs) {
347                 if (s.matches(regex)) usbErrored = true;
348             }
349         }
350 
351         if (usbTethered) {
352             mUsbTether.setSummary(R.string.usb_tethering_active_subtext);
353             mUsbTether.setEnabled(true);
354             mUsbTether.setChecked(true);
355         } else if (usbAvailable) {
356             if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
357                 mUsbTether.setSummary(R.string.usb_tethering_available_subtext);
358             } else {
359                 mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
360             }
361             mUsbTether.setEnabled(true);
362             mUsbTether.setChecked(false);
363         } else if (usbErrored) {
364             mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
365             mUsbTether.setEnabled(false);
366             mUsbTether.setChecked(false);
367         } else if (mMassStorageActive) {
368             mUsbTether.setSummary(R.string.usb_tethering_storage_active_subtext);
369             mUsbTether.setEnabled(false);
370             mUsbTether.setChecked(false);
371         } else {
372             mUsbTether.setSummary(R.string.usb_tethering_unavailable_subtext);
373             mUsbTether.setEnabled(false);
374             mUsbTether.setChecked(false);
375         }
376     }
377 
updateBluetoothState(String[] available, String[] tethered, String[] errored)378     private void updateBluetoothState(String[] available, String[] tethered,
379             String[] errored) {
380         boolean bluetoothErrored = false;
381         for (String s: errored) {
382             for (String regex : mBluetoothRegexs) {
383                 if (s.matches(regex)) bluetoothErrored = true;
384             }
385         }
386 
387         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
388         int btState = adapter.getState();
389         if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
390             mBluetoothTether.setEnabled(false);
391             mBluetoothTether.setSummary(R.string.bluetooth_turning_off);
392         } else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
393             mBluetoothTether.setEnabled(false);
394             mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
395         } else {
396             BluetoothPan bluetoothPan = mBluetoothPan.get();
397             if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null &&
398                     bluetoothPan.isTetheringOn()) {
399                 mBluetoothTether.setChecked(true);
400                 mBluetoothTether.setEnabled(true);
401                 int bluetoothTethered = bluetoothPan.getConnectedDevices().size();
402                 if (bluetoothTethered > 1) {
403                     String summary = getString(
404                             R.string.bluetooth_tethering_devices_connected_subtext,
405                             bluetoothTethered);
406                     mBluetoothTether.setSummary(summary);
407                 } else if (bluetoothTethered == 1) {
408                     mBluetoothTether.setSummary(
409                             R.string.bluetooth_tethering_device_connected_subtext);
410                 } else if (bluetoothErrored) {
411                     mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
412                 } else {
413                     mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
414                 }
415             } else {
416                 mBluetoothTether.setEnabled(true);
417                 mBluetoothTether.setChecked(false);
418                 mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
419             }
420         }
421     }
422 
onPreferenceChange(Preference preference, Object value)423     public boolean onPreferenceChange(Preference preference, Object value) {
424         boolean enable = (Boolean) value;
425 
426         if (enable) {
427             startProvisioningIfNecessary(WIFI_TETHERING);
428         } else {
429             mWifiApEnabler.setSoftapEnabled(false);
430         }
431         return false;
432     }
433 
isProvisioningNeeded()434     boolean isProvisioningNeeded() {
435         if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
436             return false;
437         }
438         return mProvisionApp.length == 2;
439     }
440 
startProvisioningIfNecessary(int choice)441     private void startProvisioningIfNecessary(int choice) {
442         mTetherChoice = choice;
443         if (isProvisioningNeeded()) {
444             Intent intent = new Intent(Intent.ACTION_MAIN);
445             intent.setClassName(mProvisionApp[0], mProvisionApp[1]);
446             startActivityForResult(intent, PROVISION_REQUEST);
447         } else {
448             startTethering();
449         }
450     }
451 
onActivityResult(int requestCode, int resultCode, Intent intent)452     public void onActivityResult(int requestCode, int resultCode, Intent intent) {
453         super.onActivityResult(requestCode, resultCode, intent);
454         if (requestCode == PROVISION_REQUEST) {
455             if (resultCode == Activity.RESULT_OK) {
456                 startTethering();
457             } else {
458                 //BT and USB need checkbox turned off on failure
459                 //Wifi tethering is never turned on until afterwards
460                 switch (mTetherChoice) {
461                     case BLUETOOTH_TETHERING:
462                         mBluetoothTether.setChecked(false);
463                         break;
464                     case USB_TETHERING:
465                         mUsbTether.setChecked(false);
466                         break;
467                 }
468                 mTetherChoice = INVALID;
469             }
470         }
471     }
472 
startTethering()473     private void startTethering() {
474         switch (mTetherChoice) {
475             case WIFI_TETHERING:
476                 mWifiApEnabler.setSoftapEnabled(true);
477                 break;
478             case BLUETOOTH_TETHERING:
479                 // turn on Bluetooth first
480                 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
481                 if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
482                     mBluetoothEnableForTether = true;
483                     adapter.enable();
484                     mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
485                     mBluetoothTether.setEnabled(false);
486                 } else {
487                     BluetoothPan bluetoothPan = mBluetoothPan.get();
488                     if (bluetoothPan != null) bluetoothPan.setBluetoothTethering(true);
489                     mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
490                 }
491                 break;
492             case USB_TETHERING:
493                 setUsbTethering(true);
494                 break;
495             default:
496                 //should not happen
497                 break;
498         }
499     }
500 
setUsbTethering(boolean enabled)501     private void setUsbTethering(boolean enabled) {
502         ConnectivityManager cm =
503             (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
504         mUsbTether.setChecked(false);
505         if (cm.setUsbTethering(enabled) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
506             mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
507             return;
508         }
509         mUsbTether.setSummary("");
510     }
511 
512     @Override
onPreferenceTreeClick(PreferenceScreen screen, Preference preference)513     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
514         ConnectivityManager cm =
515                 (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
516 
517         if (preference == mUsbTether) {
518             boolean newState = mUsbTether.isChecked();
519 
520             if (newState) {
521                 startProvisioningIfNecessary(USB_TETHERING);
522             } else {
523                 setUsbTethering(newState);
524             }
525         } else if (preference == mBluetoothTether) {
526             boolean bluetoothTetherState = mBluetoothTether.isChecked();
527 
528             if (bluetoothTetherState) {
529                 startProvisioningIfNecessary(BLUETOOTH_TETHERING);
530             } else {
531                 boolean errored = false;
532 
533                 String [] tethered = cm.getTetheredIfaces();
534                 String bluetoothIface = findIface(tethered, mBluetoothRegexs);
535                 if (bluetoothIface != null &&
536                         cm.untether(bluetoothIface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
537                     errored = true;
538                 }
539 
540                 BluetoothPan bluetoothPan = mBluetoothPan.get();
541                 if (bluetoothPan != null) bluetoothPan.setBluetoothTethering(false);
542                 if (errored) {
543                     mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
544                 } else {
545                     mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
546                 }
547             }
548         } else if (preference == mCreateNetwork) {
549             showDialog(DIALOG_AP_SETTINGS);
550         }
551 
552         return super.onPreferenceTreeClick(screen, preference);
553     }
554 
findIface(String[] ifaces, String[] regexes)555     private static String findIface(String[] ifaces, String[] regexes) {
556         for (String iface : ifaces) {
557             for (String regex : regexes) {
558                 if (iface.matches(regex)) {
559                     return iface;
560                 }
561             }
562         }
563         return null;
564     }
565 
onClick(DialogInterface dialogInterface, int button)566     public void onClick(DialogInterface dialogInterface, int button) {
567         if (button == DialogInterface.BUTTON_POSITIVE) {
568             mWifiConfig = mDialog.getConfig();
569             if (mWifiConfig != null) {
570                 /**
571                  * if soft AP is stopped, bring up
572                  * else restart with new config
573                  * TODO: update config on a running access point when framework support is added
574                  */
575                 if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
576                     mWifiManager.setWifiApEnabled(null, false);
577                     mWifiManager.setWifiApEnabled(mWifiConfig, true);
578                 } else {
579                     mWifiManager.setWifiApConfiguration(mWifiConfig);
580                 }
581                 int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
582                 mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
583                         mWifiConfig.SSID,
584                         mSecurityType[index]));
585             }
586         }
587     }
588 
589     @Override
getHelpResource()590     public int getHelpResource() {
591         return R.string.help_url_tether;
592     }
593 }
594