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