TetherSettings.java revision 0f47465a3f73a4f013ec7958efec95ecbb848ba8
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
17package com.android.settings;
18
19import com.android.settings.wifi.WifiApEnabler;
20import com.android.settings.wifi.WifiApDialog;
21
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.bluetooth.BluetoothAdapter;
26import android.bluetooth.BluetoothPan;
27import android.bluetooth.BluetoothProfile;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.DialogInterface;
31import android.content.Intent;
32import android.content.IntentFilter;
33import android.content.res.AssetManager;
34import android.hardware.usb.UsbManager;
35import android.net.ConnectivityManager;
36import android.net.wifi.WifiConfiguration;
37import android.net.wifi.WifiManager;
38import android.os.Bundle;
39import android.os.Environment;
40import android.preference.CheckBoxPreference;
41import android.preference.Preference;
42import android.preference.PreferenceScreen;
43import android.view.ViewGroup;
44import android.view.ViewParent;
45import android.webkit.WebView;
46
47import java.io.InputStream;
48import java.util.ArrayList;
49import java.util.Locale;
50
51/*
52 * Displays preferences for Tethering.
53 */
54public class TetherSettings extends SettingsPreferenceFragment
55        implements DialogInterface.OnClickListener {
56
57    private static final String USB_TETHER_SETTINGS = "usb_tether_settings";
58    private static final String ENABLE_WIFI_AP = "enable_wifi_ap";
59    private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
60    private static final String TETHERING_HELP = "tethering_help";
61    private static final String USB_HELP_MODIFIER = "usb_";
62    private static final String WIFI_HELP_MODIFIER = "wifi_";
63    private static final String HELP_URL = "file:///android_asset/html/%y%z/tethering_%xhelp.html";
64    private static final String HELP_PATH = "html/%y%z/tethering_help.html";
65
66    private static final int DIALOG_TETHER_HELP = 1;
67    private static final int DIALOG_AP_SETTINGS = 2;
68
69    private WebView mView;
70    private CheckBoxPreference mUsbTether;
71
72    private WifiApEnabler mWifiApEnabler;
73
74    private CheckBoxPreference mBluetoothTether;
75
76    private PreferenceScreen mTetherHelp;
77
78    private BroadcastReceiver mTetherChangeReceiver;
79
80    private String[] mUsbRegexs;
81
82    private String[] mWifiRegexs;
83
84    private String[] mBluetoothRegexs;
85    private BluetoothPan mBluetoothPan;
86
87    private static final String WIFI_AP_SSID_AND_SECURITY = "wifi_ap_ssid_and_security";
88    private static final int CONFIG_SUBTEXT = R.string.wifi_tether_configure_subtext;
89
90    private String[] mSecurityType;
91    private Preference mCreateNetwork;
92    private CheckBoxPreference mEnableWifiAp;
93
94    private WifiApDialog mDialog;
95    private WifiManager mWifiManager;
96    private WifiConfiguration mWifiConfig = null;
97
98    private boolean mUsbConnected;
99    private boolean mMassStorageActive;
100
101    private boolean mBluetoothEnableForTether;
102
103    @Override
104    public void onCreate(Bundle icicle) {
105        super.onCreate(icicle);
106        addPreferencesFromResource(R.xml.tether_prefs);
107
108        final Activity activity = getActivity();
109        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
110        if (adapter != null) {
111            adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
112                    BluetoothProfile.PAN);
113        }
114
115        CheckBoxPreference enableWifiAp =
116                (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
117        Preference wifiApSettings = findPreference(WIFI_AP_SSID_AND_SECURITY);
118        mUsbTether = (CheckBoxPreference) findPreference(USB_TETHER_SETTINGS);
119        mBluetoothTether = (CheckBoxPreference) findPreference(ENABLE_BLUETOOTH_TETHERING);
120        mTetherHelp = (PreferenceScreen) findPreference(TETHERING_HELP);
121
122        ConnectivityManager cm =
123                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
124
125        mUsbRegexs = cm.getTetherableUsbRegexs();
126        mWifiRegexs = cm.getTetherableWifiRegexs();
127        mBluetoothRegexs = cm.getTetherableBluetoothRegexs();
128
129        final boolean usbAvailable = mUsbRegexs.length != 0;
130        final boolean wifiAvailable = mWifiRegexs.length != 0;
131        final boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
132
133        if (!usbAvailable || Utils.isMonkeyRunning()) {
134            getPreferenceScreen().removePreference(mUsbTether);
135        }
136
137        if (wifiAvailable) {
138            mWifiApEnabler = new WifiApEnabler(activity, enableWifiAp);
139            initWifiTethering();
140        } else {
141            getPreferenceScreen().removePreference(enableWifiAp);
142            getPreferenceScreen().removePreference(wifiApSettings);
143        }
144
145        if (!bluetoothAvailable) {
146            getPreferenceScreen().removePreference(mBluetoothTether);
147        } else {
148            if (mBluetoothPan != null && mBluetoothPan.isTetheringOn()) {
149                mBluetoothTether.setChecked(true);
150            } else {
151                mBluetoothTether.setChecked(false);
152            }
153        }
154
155        mView = new WebView(activity);
156    }
157
158    private void initWifiTethering() {
159        final Activity activity = getActivity();
160        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
161        mWifiConfig = mWifiManager.getWifiApConfiguration();
162        mSecurityType = getResources().getStringArray(R.array.wifi_ap_security);
163
164        mCreateNetwork = findPreference(WIFI_AP_SSID_AND_SECURITY);
165        mEnableWifiAp = (CheckBoxPreference) findPreference(ENABLE_WIFI_AP);
166
167        if (mWifiConfig == null) {
168            final String s = activity.getString(
169                    com.android.internal.R.string.wifi_tether_configure_ssid_default);
170            mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
171                    s, mSecurityType[WifiApDialog.OPEN_INDEX]));
172        } else {
173            int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
174            mCreateNetwork.setSummary(String.format(activity.getString(CONFIG_SUBTEXT),
175                    mWifiConfig.SSID,
176                    mSecurityType[index]));
177        }
178    }
179
180    private BluetoothProfile.ServiceListener mProfileServiceListener =
181        new BluetoothProfile.ServiceListener() {
182        public void onServiceConnected(int profile, BluetoothProfile proxy) {
183            mBluetoothPan = (BluetoothPan) proxy;
184        }
185        public void onServiceDisconnected(int profile) {
186            mBluetoothPan = null;
187        }
188    };
189
190    @Override
191    public Dialog onCreateDialog(int id) {
192        if (id == DIALOG_TETHER_HELP) {
193            Locale locale = Locale.getDefault();
194
195            // check for the full language + country resource, if not there, try just language
196            final AssetManager am = getActivity().getAssets();
197            String path = HELP_PATH.replace("%y", locale.getLanguage().toLowerCase());
198            path = path.replace("%z", '_'+locale.getCountry().toLowerCase());
199            boolean useCountry = true;
200            InputStream is = null;
201            try {
202                is = am.open(path);
203            } catch (Exception ignored) {
204                useCountry = false;
205            } finally {
206                if (is != null) {
207                    try {
208                        is.close();
209                    } catch (Exception ignored) {}
210                }
211            }
212            String url = HELP_URL.replace("%y", locale.getLanguage().toLowerCase());
213            url = url.replace("%z", useCountry ? '_'+locale.getCountry().toLowerCase() : "");
214            if ((mUsbRegexs.length != 0) && (mWifiRegexs.length == 0)) {
215                url = url.replace("%x", USB_HELP_MODIFIER);
216            } else if ((mWifiRegexs.length != 0) && (mUsbRegexs.length == 0)) {
217                url = url.replace("%x", WIFI_HELP_MODIFIER);
218            } else {
219                // could assert that both wifi and usb have regexs, but the default
220                // is to use this anyway so no check is needed
221                url = url.replace("%x", "");
222            }
223
224            mView.loadUrl(url);
225            // Detach from old parent first
226            ViewParent parent = mView.getParent();
227            if (parent != null && parent instanceof ViewGroup) {
228                ((ViewGroup) parent).removeView(mView);
229            }
230            return new AlertDialog.Builder(getActivity())
231                .setCancelable(true)
232                .setTitle(R.string.tethering_help_button_text)
233                .setView(mView)
234                .create();
235        } else if (id == DIALOG_AP_SETTINGS) {
236            final Activity activity = getActivity();
237            mDialog = new WifiApDialog(activity, this, mWifiConfig);
238            return mDialog;
239        }
240
241        return null;
242    }
243
244    private class TetherChangeReceiver extends BroadcastReceiver {
245        @Override
246        public void onReceive(Context content, Intent intent) {
247            String action = intent.getAction();
248            if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
249                // TODO - this should understand the interface types
250                ArrayList<String> available = intent.getStringArrayListExtra(
251                        ConnectivityManager.EXTRA_AVAILABLE_TETHER);
252                ArrayList<String> active = intent.getStringArrayListExtra(
253                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
254                ArrayList<String> errored = intent.getStringArrayListExtra(
255                        ConnectivityManager.EXTRA_ERRORED_TETHER);
256                updateState(available.toArray(new String[available.size()]),
257                        active.toArray(new String[active.size()]),
258                        errored.toArray(new String[errored.size()]));
259            } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
260                mMassStorageActive = true;
261                updateState();
262            } else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
263                mMassStorageActive = false;
264                updateState();
265            } else if (action.equals(UsbManager.ACTION_USB_STATE)) {
266                mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
267                updateState();
268            } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
269                if (mBluetoothEnableForTether) {
270                    switch (intent
271                            .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
272                        case BluetoothAdapter.STATE_ON:
273                            mBluetoothPan.setBluetoothTethering(true);
274                            mBluetoothEnableForTether = false;
275                            break;
276
277                        case BluetoothAdapter.STATE_OFF:
278                        case BluetoothAdapter.ERROR:
279                            mBluetoothEnableForTether = false;
280                            break;
281
282                        default:
283                            // ignore transition states
284                    }
285                }
286                updateState();
287            }
288        }
289    }
290
291    @Override
292    public void onStart() {
293        super.onStart();
294
295        final Activity activity = getActivity();
296
297        mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
298        mTetherChangeReceiver = new TetherChangeReceiver();
299        IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
300        Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
301
302        filter = new IntentFilter();
303        filter.addAction(UsbManager.ACTION_USB_STATE);
304        activity.registerReceiver(mTetherChangeReceiver, filter);
305
306        filter = new IntentFilter();
307        filter.addAction(Intent.ACTION_MEDIA_SHARED);
308        filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
309        filter.addDataScheme("file");
310        activity.registerReceiver(mTetherChangeReceiver, filter);
311
312        filter = new IntentFilter();
313        filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
314        activity.registerReceiver(mTetherChangeReceiver, filter);
315
316        if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
317        if (mWifiApEnabler != null) {
318            mWifiApEnabler.resume();
319        }
320
321        updateState();
322    }
323
324    @Override
325    public void onStop() {
326        super.onStop();
327        getActivity().unregisterReceiver(mTetherChangeReceiver);
328        mTetherChangeReceiver = null;
329        if (mWifiApEnabler != null) {
330            mWifiApEnabler.pause();
331        }
332    }
333
334    private void updateState() {
335        ConnectivityManager cm =
336                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
337
338        String[] available = cm.getTetherableIfaces();
339        String[] tethered = cm.getTetheredIfaces();
340        String[] errored = cm.getTetheringErroredIfaces();
341        updateState(available, tethered, errored);
342    }
343
344    private void updateState(String[] available, String[] tethered,
345            String[] errored) {
346        updateUsbState(available, tethered, errored);
347        updateBluetoothState(available, tethered, errored);
348    }
349
350
351    private void updateUsbState(String[] available, String[] tethered,
352            String[] errored) {
353        ConnectivityManager cm =
354                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
355        boolean usbAvailable = mUsbConnected && !mMassStorageActive;
356        int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
357        for (String s : available) {
358            for (String regex : mUsbRegexs) {
359                if (s.matches(regex)) {
360                    if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
361                        usbError = cm.getLastTetherError(s);
362                    }
363                }
364            }
365        }
366        boolean usbTethered = false;
367        for (String s : tethered) {
368            for (String regex : mUsbRegexs) {
369                if (s.matches(regex)) usbTethered = true;
370            }
371        }
372        boolean usbErrored = false;
373        for (String s: errored) {
374            for (String regex : mUsbRegexs) {
375                if (s.matches(regex)) usbErrored = true;
376            }
377        }
378
379        if (usbTethered) {
380            mUsbTether.setSummary(R.string.usb_tethering_active_subtext);
381            mUsbTether.setEnabled(true);
382            mUsbTether.setChecked(true);
383        } else if (usbAvailable) {
384            if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
385                mUsbTether.setSummary(R.string.usb_tethering_available_subtext);
386            } else {
387                mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
388            }
389            mUsbTether.setEnabled(true);
390            mUsbTether.setChecked(false);
391        } else if (usbErrored) {
392            mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
393            mUsbTether.setEnabled(false);
394            mUsbTether.setChecked(false);
395        } else if (mMassStorageActive) {
396            mUsbTether.setSummary(R.string.usb_tethering_storage_active_subtext);
397            mUsbTether.setEnabled(false);
398            mUsbTether.setChecked(false);
399        } else {
400            mUsbTether.setSummary(R.string.usb_tethering_unavailable_subtext);
401            mUsbTether.setEnabled(false);
402            mUsbTether.setChecked(false);
403        }
404    }
405
406    private void updateBluetoothState(String[] available, String[] tethered,
407            String[] errored) {
408        int bluetoothTethered = 0;
409        for (String s : tethered) {
410            for (String regex : mBluetoothRegexs) {
411                if (s.matches(regex)) bluetoothTethered++;
412            }
413        }
414        boolean bluetoothErrored = false;
415        for (String s: errored) {
416            for (String regex : mBluetoothRegexs) {
417                if (s.matches(regex)) bluetoothErrored = true;
418            }
419        }
420
421        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
422        int btState = adapter.getState();
423        if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
424            mBluetoothTether.setEnabled(false);
425            mBluetoothTether.setSummary(R.string.wifi_stopping);
426        } else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
427            mBluetoothTether.setEnabled(false);
428            mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
429        } else if (btState == BluetoothAdapter.STATE_ON && mBluetoothPan.isTetheringOn()) {
430            mBluetoothTether.setChecked(true);
431            mBluetoothTether.setEnabled(true);
432            if (bluetoothTethered > 1) {
433                String summary = getString(
434                        R.string.bluetooth_tethering_devices_connected_subtext, bluetoothTethered);
435                mBluetoothTether.setSummary(summary);
436            } else if (bluetoothTethered == 1) {
437                mBluetoothTether.setSummary(R.string.bluetooth_tethering_device_connected_subtext);
438            } else if (bluetoothErrored) {
439                mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
440            } else {
441                mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
442            }
443        } else {
444            mBluetoothTether.setEnabled(true);
445            mBluetoothTether.setChecked(false);
446            mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
447        }
448    }
449
450    @Override
451    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
452        ConnectivityManager cm =
453                (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
454
455        if (preference == mUsbTether) {
456            boolean newState = mUsbTether.isChecked();
457
458            if (cm.setUsbTethering(newState) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
459                mUsbTether.setChecked(false);
460                mUsbTether.setSummary(R.string.usb_tethering_errored_subtext);
461                return true;
462            }
463            mUsbTether.setSummary("");
464        } else if (preference == mBluetoothTether) {
465            boolean bluetoothTetherState = mBluetoothTether.isChecked();
466
467            if (bluetoothTetherState) {
468                // turn on Bluetooth first
469                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
470                if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
471                    mBluetoothEnableForTether = true;
472                    adapter.enable();
473                    mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
474                    mBluetoothTether.setEnabled(false);
475                } else {
476                    mBluetoothPan.setBluetoothTethering(true);
477                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_available_subtext);
478                }
479            } else {
480                boolean errored = false;
481
482                String [] tethered = cm.getTetheredIfaces();
483                String bluetoothIface = findIface(tethered, mBluetoothRegexs);
484                if (bluetoothIface != null &&
485                        cm.untether(bluetoothIface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
486                    errored = true;
487                }
488
489                mBluetoothPan.setBluetoothTethering(false);
490                if (errored) {
491                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_errored_subtext);
492                } else {
493                    mBluetoothTether.setSummary(R.string.bluetooth_tethering_off_subtext);
494                }
495            }
496        } else if (preference == mTetherHelp) {
497            showDialog(DIALOG_TETHER_HELP);
498            return true;
499        } else if (preference == mCreateNetwork) {
500            showDialog(DIALOG_AP_SETTINGS);
501        }
502
503        return super.onPreferenceTreeClick(screen, preference);
504    }
505
506    private static String findIface(String[] ifaces, String[] regexes) {
507        for (String iface : ifaces) {
508            for (String regex : regexes) {
509                if (iface.matches(regex)) {
510                    return iface;
511                }
512            }
513        }
514        return null;
515    }
516
517    public void onClick(DialogInterface dialogInterface, int button) {
518        if (button == DialogInterface.BUTTON_POSITIVE) {
519            mWifiConfig = mDialog.getConfig();
520            if (mWifiConfig != null) {
521                /**
522                 * if soft AP is stopped, bring up
523                 * else restart with new config
524                 * TODO: update config on a running access point when framework support is added
525                 */
526                if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
527                    mWifiManager.setWifiApEnabled(null, false);
528                    mWifiManager.setWifiApEnabled(mWifiConfig, true);
529                } else {
530                    mWifiManager.setWifiApConfiguration(mWifiConfig);
531                }
532                int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
533                mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
534                        mWifiConfig.SSID,
535                        mSecurityType[index]));
536            }
537        }
538    }
539}
540