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 static android.net.ConnectivityManager.TETHERING_BLUETOOTH; 20 import static android.net.ConnectivityManager.TETHERING_USB; 21 22 import android.app.Activity; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothPan; 25 import android.bluetooth.BluetoothProfile; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.PackageManager; 31 import android.hardware.usb.UsbManager; 32 import android.net.ConnectivityManager; 33 import android.os.Bundle; 34 import android.os.Environment; 35 import android.os.Handler; 36 import android.os.UserManager; 37 import android.support.v14.preference.SwitchPreference; 38 import android.support.v7.preference.Preference; 39 40 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 41 import com.android.settings.datausage.DataSaverBackend; 42 import com.android.settings.wifi.tether.WifiTetherPreferenceController; 43 import com.android.settingslib.TetherUtil; 44 45 import java.lang.ref.WeakReference; 46 import java.util.ArrayList; 47 import java.util.concurrent.atomic.AtomicReference; 48 49 /* 50 * Displays preferences for Tethering. 51 */ 52 public class TetherSettings extends RestrictedSettingsFragment 53 implements DataSaverBackend.Listener { 54 55 private static final String USB_TETHER_SETTINGS = "usb_tether_settings"; 56 private static final String ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering"; 57 private static final String DATA_SAVER_FOOTER = "disabled_on_data_saver"; 58 59 private static final String TAG = "TetheringSettings"; 60 61 private SwitchPreference mUsbTether; 62 63 private SwitchPreference mBluetoothTether; 64 65 private BroadcastReceiver mTetherChangeReceiver; 66 67 private String[] mUsbRegexs; 68 private String[] mBluetoothRegexs; 69 private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>(); 70 71 private Handler mHandler = new Handler(); 72 private OnStartTetheringCallback mStartTetheringCallback; 73 private ConnectivityManager mCm; 74 75 private WifiTetherPreferenceController mWifiTetherPreferenceController; 76 77 private boolean mUsbConnected; 78 private boolean mMassStorageActive; 79 80 private boolean mBluetoothEnableForTether; 81 private boolean mUnavailable; 82 83 private DataSaverBackend mDataSaverBackend; 84 private boolean mDataSaverEnabled; 85 private Preference mDataSaverFooter; 86 87 @Override getMetricsCategory()88 public int getMetricsCategory() { 89 return MetricsEvent.TETHER; 90 } 91 TetherSettings()92 public TetherSettings() { 93 super(UserManager.DISALLOW_CONFIG_TETHERING); 94 } 95 96 @Override onAttach(Context context)97 public void onAttach(Context context) { 98 super.onAttach(context); 99 mWifiTetherPreferenceController = 100 new WifiTetherPreferenceController(context, getLifecycle()); 101 } 102 103 @Override onCreate(Bundle icicle)104 public void onCreate(Bundle icicle) { 105 super.onCreate(icicle); 106 107 addPreferencesFromResource(R.xml.tether_prefs); 108 mFooterPreferenceMixin.createFooterPreference() 109 .setTitle(R.string.tethering_footer_info); 110 111 mDataSaverBackend = new DataSaverBackend(getContext()); 112 mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled(); 113 mDataSaverFooter = findPreference(DATA_SAVER_FOOTER); 114 115 setIfOnlyAvailableForAdmins(true); 116 if (isUiRestricted()) { 117 mUnavailable = true; 118 getPreferenceScreen().removeAll(); 119 return; 120 } 121 122 final Activity activity = getActivity(); 123 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 124 if (adapter != null) { 125 adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, 126 BluetoothProfile.PAN); 127 } 128 129 mUsbTether = (SwitchPreference) findPreference(USB_TETHER_SETTINGS); 130 mBluetoothTether = (SwitchPreference) findPreference(ENABLE_BLUETOOTH_TETHERING); 131 132 mDataSaverBackend.addListener(this); 133 134 mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); 135 136 mUsbRegexs = mCm.getTetherableUsbRegexs(); 137 mBluetoothRegexs = mCm.getTetherableBluetoothRegexs(); 138 139 final boolean usbAvailable = mUsbRegexs.length != 0; 140 final boolean bluetoothAvailable = mBluetoothRegexs.length != 0; 141 142 if (!usbAvailable || Utils.isMonkeyRunning()) { 143 getPreferenceScreen().removePreference(mUsbTether); 144 } 145 146 mWifiTetherPreferenceController.displayPreference(getPreferenceScreen()); 147 148 if (!bluetoothAvailable) { 149 getPreferenceScreen().removePreference(mBluetoothTether); 150 } else { 151 BluetoothPan pan = mBluetoothPan.get(); 152 if (pan != null && pan.isTetheringOn()) { 153 mBluetoothTether.setChecked(true); 154 } else { 155 mBluetoothTether.setChecked(false); 156 } 157 } 158 // Set initial state based on Data Saver mode. 159 onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled()); 160 } 161 162 @Override onDestroy()163 public void onDestroy() { 164 mDataSaverBackend.remListener(this); 165 166 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 167 BluetoothProfile profile = mBluetoothPan.getAndSet(null); 168 if (profile != null && adapter != null) { 169 adapter.closeProfileProxy(BluetoothProfile.PAN, profile); 170 } 171 172 super.onDestroy(); 173 } 174 175 @Override onDataSaverChanged(boolean isDataSaving)176 public void onDataSaverChanged(boolean isDataSaving) { 177 mDataSaverEnabled = isDataSaving; 178 mUsbTether.setEnabled(!mDataSaverEnabled); 179 mBluetoothTether.setEnabled(!mDataSaverEnabled); 180 mDataSaverFooter.setVisible(mDataSaverEnabled); 181 } 182 183 @Override onWhitelistStatusChanged(int uid, boolean isWhitelisted)184 public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) { 185 } 186 187 @Override onBlacklistStatusChanged(int uid, boolean isBlacklisted)188 public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) { 189 } 190 191 private class TetherChangeReceiver extends BroadcastReceiver { 192 @Override onReceive(Context content, Intent intent)193 public void onReceive(Context content, Intent intent) { 194 String action = intent.getAction(); 195 if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) { 196 // TODO - this should understand the interface types 197 ArrayList<String> available = intent.getStringArrayListExtra( 198 ConnectivityManager.EXTRA_AVAILABLE_TETHER); 199 ArrayList<String> active = intent.getStringArrayListExtra( 200 ConnectivityManager.EXTRA_ACTIVE_TETHER); 201 ArrayList<String> errored = intent.getStringArrayListExtra( 202 ConnectivityManager.EXTRA_ERRORED_TETHER); 203 updateState(available.toArray(new String[available.size()]), 204 active.toArray(new String[active.size()]), 205 errored.toArray(new String[errored.size()])); 206 } else if (action.equals(Intent.ACTION_MEDIA_SHARED)) { 207 mMassStorageActive = true; 208 updateState(); 209 } else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) { 210 mMassStorageActive = false; 211 updateState(); 212 } else if (action.equals(UsbManager.ACTION_USB_STATE)) { 213 mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); 214 updateState(); 215 } else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { 216 if (mBluetoothEnableForTether) { 217 switch (intent 218 .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { 219 case BluetoothAdapter.STATE_ON: 220 startTethering(TETHERING_BLUETOOTH); 221 mBluetoothEnableForTether = false; 222 break; 223 224 case BluetoothAdapter.STATE_OFF: 225 case BluetoothAdapter.ERROR: 226 mBluetoothEnableForTether = false; 227 break; 228 229 default: 230 // ignore transition states 231 } 232 } 233 updateState(); 234 } 235 } 236 } 237 238 @Override onStart()239 public void onStart() { 240 super.onStart(); 241 242 if (mUnavailable) { 243 if (!isUiRestrictedByOnlyAdmin()) { 244 getEmptyTextView().setText(R.string.tethering_settings_not_available); 245 } 246 getPreferenceScreen().removeAll(); 247 return; 248 } 249 250 final Activity activity = getActivity(); 251 252 mStartTetheringCallback = new OnStartTetheringCallback(this); 253 254 mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState()); 255 mTetherChangeReceiver = new TetherChangeReceiver(); 256 IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); 257 Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter); 258 259 filter = new IntentFilter(); 260 filter.addAction(UsbManager.ACTION_USB_STATE); 261 activity.registerReceiver(mTetherChangeReceiver, filter); 262 263 filter = new IntentFilter(); 264 filter.addAction(Intent.ACTION_MEDIA_SHARED); 265 filter.addAction(Intent.ACTION_MEDIA_UNSHARED); 266 filter.addDataScheme("file"); 267 activity.registerReceiver(mTetherChangeReceiver, filter); 268 269 filter = new IntentFilter(); 270 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 271 activity.registerReceiver(mTetherChangeReceiver, filter); 272 273 if (intent != null) mTetherChangeReceiver.onReceive(activity, intent); 274 275 updateState(); 276 } 277 278 @Override onStop()279 public void onStop() { 280 super.onStop(); 281 282 if (mUnavailable) { 283 return; 284 } 285 getActivity().unregisterReceiver(mTetherChangeReceiver); 286 mTetherChangeReceiver = null; 287 mStartTetheringCallback = null; 288 } 289 updateState()290 private void updateState() { 291 String[] available = mCm.getTetherableIfaces(); 292 String[] tethered = mCm.getTetheredIfaces(); 293 String[] errored = mCm.getTetheringErroredIfaces(); 294 updateState(available, tethered, errored); 295 } 296 updateState(String[] available, String[] tethered, String[] errored)297 private void updateState(String[] available, String[] tethered, 298 String[] errored) { 299 updateUsbState(available, tethered, errored); 300 updateBluetoothState(); 301 } 302 updateUsbState(String[] available, String[] tethered, String[] errored)303 private void updateUsbState(String[] available, String[] tethered, 304 String[] errored) { 305 boolean usbAvailable = mUsbConnected && !mMassStorageActive; 306 int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR; 307 for (String s : available) { 308 for (String regex : mUsbRegexs) { 309 if (s.matches(regex)) { 310 if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) { 311 usbError = mCm.getLastTetherError(s); 312 } 313 } 314 } 315 } 316 boolean usbTethered = false; 317 for (String s : tethered) { 318 for (String regex : mUsbRegexs) { 319 if (s.matches(regex)) usbTethered = true; 320 } 321 } 322 boolean usbErrored = false; 323 for (String s: errored) { 324 for (String regex : mUsbRegexs) { 325 if (s.matches(regex)) usbErrored = true; 326 } 327 } 328 329 if (usbTethered) { 330 mUsbTether.setEnabled(!mDataSaverEnabled); 331 mUsbTether.setChecked(true); 332 } else if (usbAvailable) { 333 mUsbTether.setEnabled(!mDataSaverEnabled); 334 mUsbTether.setChecked(false); 335 } else { 336 mUsbTether.setEnabled(false); 337 mUsbTether.setChecked(false); 338 } 339 } 340 updateBluetoothState()341 private void updateBluetoothState() { 342 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 343 if (adapter == null) { 344 return; 345 } 346 int btState = adapter.getState(); 347 if (btState == BluetoothAdapter.STATE_TURNING_OFF) { 348 mBluetoothTether.setEnabled(false); 349 } else if (btState == BluetoothAdapter.STATE_TURNING_ON) { 350 mBluetoothTether.setEnabled(false); 351 } else { 352 BluetoothPan bluetoothPan = mBluetoothPan.get(); 353 if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null 354 && bluetoothPan.isTetheringOn()) { 355 mBluetoothTether.setChecked(true); 356 mBluetoothTether.setEnabled(!mDataSaverEnabled); 357 } else { 358 mBluetoothTether.setEnabled(!mDataSaverEnabled); 359 mBluetoothTether.setChecked(false); 360 } 361 } 362 } 363 isProvisioningNeededButUnavailable(Context context)364 public static boolean isProvisioningNeededButUnavailable(Context context) { 365 return (TetherUtil.isProvisioningNeeded(context) 366 && !isIntentAvailable(context)); 367 } 368 isIntentAvailable(Context context)369 private static boolean isIntentAvailable(Context context) { 370 String[] provisionApp = context.getResources().getStringArray( 371 com.android.internal.R.array.config_mobile_hotspot_provision_app); 372 if (provisionApp.length < 2) { 373 return false; 374 } 375 final PackageManager packageManager = context.getPackageManager(); 376 Intent intent = new Intent(Intent.ACTION_MAIN); 377 intent.setClassName(provisionApp[0], provisionApp[1]); 378 379 return (packageManager.queryIntentActivities(intent, 380 PackageManager.MATCH_DEFAULT_ONLY).size() > 0); 381 } 382 startTethering(int choice)383 private void startTethering(int choice) { 384 if (choice == TETHERING_BLUETOOTH) { 385 // Turn on Bluetooth first. 386 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 387 if (adapter.getState() == BluetoothAdapter.STATE_OFF) { 388 mBluetoothEnableForTether = true; 389 adapter.enable(); 390 mBluetoothTether.setEnabled(false); 391 return; 392 } 393 } 394 395 mCm.startTethering(choice, true, mStartTetheringCallback, mHandler); 396 } 397 398 @Override onPreferenceTreeClick(Preference preference)399 public boolean onPreferenceTreeClick(Preference preference) { 400 if (preference == mUsbTether) { 401 if (mUsbTether.isChecked()) { 402 startTethering(TETHERING_USB); 403 } else { 404 mCm.stopTethering(TETHERING_USB); 405 } 406 } else if (preference == mBluetoothTether) { 407 if (mBluetoothTether.isChecked()) { 408 startTethering(TETHERING_BLUETOOTH); 409 } else { 410 mCm.stopTethering(TETHERING_BLUETOOTH); 411 // No ACTION_TETHER_STATE_CHANGED is fired or bluetooth unless a device is 412 // connected. Need to update state manually. 413 updateState(); 414 } 415 } 416 417 return super.onPreferenceTreeClick(preference); 418 } 419 420 @Override getHelpResource()421 public int getHelpResource() { 422 return R.string.help_url_tether; 423 } 424 425 private BluetoothProfile.ServiceListener mProfileServiceListener = 426 new BluetoothProfile.ServiceListener() { 427 public void onServiceConnected(int profile, BluetoothProfile proxy) { 428 mBluetoothPan.set((BluetoothPan) proxy); 429 } 430 public void onServiceDisconnected(int profile) { 431 mBluetoothPan.set(null); 432 } 433 }; 434 435 private static final class OnStartTetheringCallback extends 436 ConnectivityManager.OnStartTetheringCallback { 437 final WeakReference<TetherSettings> mTetherSettings; 438 OnStartTetheringCallback(TetherSettings settings)439 OnStartTetheringCallback(TetherSettings settings) { 440 mTetherSettings = new WeakReference<>(settings); 441 } 442 443 @Override onTetheringStarted()444 public void onTetheringStarted() { 445 update(); 446 } 447 448 @Override onTetheringFailed()449 public void onTetheringFailed() { 450 update(); 451 } 452 update()453 private void update() { 454 TetherSettings settings = mTetherSettings.get(); 455 if (settings != null) { 456 settings.updateState(); 457 } 458 } 459 } 460 } 461