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