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