Adding this config value to make it easy to switch to developing Tethering settings UI. Also, using this config value to make sure that preferences from new AllInOneTetherSettings screen won't show up in the search results. Bug: 147915366 Test: make RunSettingsRoboTests ROBOTEST_FILTER=CodeInspectionTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=TetherSettingsTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=TetehrPreferenceControllerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=WifiTetherSettingsTest Change-Id: I8c88816a8f2b40fc219cae8d0d442454c5270b2b
511 lines
18 KiB
Java
511 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2008 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.settings;
|
|
|
|
import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
|
|
import static android.net.ConnectivityManager.TETHERING_USB;
|
|
|
|
import android.app.Activity;
|
|
import android.app.settings.SettingsEnums;
|
|
import android.bluetooth.BluetoothAdapter;
|
|
import android.bluetooth.BluetoothPan;
|
|
import android.bluetooth.BluetoothProfile;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.pm.PackageManager;
|
|
import android.hardware.usb.UsbManager;
|
|
import android.net.ConnectivityManager;
|
|
import android.os.Bundle;
|
|
import android.os.Environment;
|
|
import android.os.Handler;
|
|
import android.os.UserManager;
|
|
import android.provider.SearchIndexableResource;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.SwitchPreference;
|
|
|
|
import com.android.settings.datausage.DataSaverBackend;
|
|
import com.android.settings.search.BaseSearchIndexProvider;
|
|
import com.android.settings.wifi.tether.WifiTetherPreferenceController;
|
|
import com.android.settingslib.TetherUtil;
|
|
import com.android.settingslib.search.SearchIndexable;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
/*
|
|
* Displays preferences for Tethering.
|
|
*/
|
|
@SearchIndexable
|
|
public class TetherSettings extends RestrictedSettingsFragment
|
|
implements DataSaverBackend.Listener {
|
|
|
|
@VisibleForTesting
|
|
static final String KEY_TETHER_PREFS_SCREEN = "tether_prefs_screen";
|
|
@VisibleForTesting
|
|
static final String KEY_WIFI_TETHER = "wifi_tether";
|
|
@VisibleForTesting
|
|
static final String KEY_USB_TETHER_SETTINGS = "usb_tether_settings";
|
|
@VisibleForTesting
|
|
static final String KEY_ENABLE_BLUETOOTH_TETHERING = "enable_bluetooth_tethering";
|
|
private static final String KEY_DATA_SAVER_FOOTER = "disabled_on_data_saver";
|
|
|
|
private static final String TAG = "TetheringSettings";
|
|
|
|
private SwitchPreference mUsbTether;
|
|
|
|
private SwitchPreference mBluetoothTether;
|
|
|
|
private BroadcastReceiver mTetherChangeReceiver;
|
|
|
|
private String[] mUsbRegexs;
|
|
private String[] mBluetoothRegexs;
|
|
private AtomicReference<BluetoothPan> mBluetoothPan = new AtomicReference<>();
|
|
|
|
private Handler mHandler = new Handler();
|
|
private OnStartTetheringCallback mStartTetheringCallback;
|
|
private ConnectivityManager mCm;
|
|
|
|
private WifiTetherPreferenceController mWifiTetherPreferenceController;
|
|
|
|
private boolean mUsbConnected;
|
|
private boolean mMassStorageActive;
|
|
|
|
private boolean mBluetoothEnableForTether;
|
|
private boolean mUnavailable;
|
|
|
|
private DataSaverBackend mDataSaverBackend;
|
|
private boolean mDataSaverEnabled;
|
|
private Preference mDataSaverFooter;
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return SettingsEnums.TETHER;
|
|
}
|
|
|
|
public TetherSettings() {
|
|
super(UserManager.DISALLOW_CONFIG_TETHERING);
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
mWifiTetherPreferenceController =
|
|
new WifiTetherPreferenceController(context, getSettingsLifecycle());
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
|
|
addPreferencesFromResource(R.xml.tether_prefs);
|
|
mDataSaverBackend = new DataSaverBackend(getContext());
|
|
mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
|
|
mDataSaverFooter = findPreference(KEY_DATA_SAVER_FOOTER);
|
|
|
|
setIfOnlyAvailableForAdmins(true);
|
|
if (isUiRestricted()) {
|
|
mUnavailable = true;
|
|
getPreferenceScreen().removeAll();
|
|
return;
|
|
}
|
|
|
|
final Activity activity = getActivity();
|
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
if (adapter != null) {
|
|
adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener,
|
|
BluetoothProfile.PAN);
|
|
}
|
|
|
|
mUsbTether = (SwitchPreference) findPreference(KEY_USB_TETHER_SETTINGS);
|
|
mBluetoothTether = (SwitchPreference) findPreference(KEY_ENABLE_BLUETOOTH_TETHERING);
|
|
|
|
mDataSaverBackend.addListener(this);
|
|
|
|
mCm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
|
|
mUsbRegexs = mCm.getTetherableUsbRegexs();
|
|
mBluetoothRegexs = mCm.getTetherableBluetoothRegexs();
|
|
|
|
final boolean usbAvailable = mUsbRegexs.length != 0;
|
|
final boolean bluetoothAvailable = mBluetoothRegexs.length != 0;
|
|
|
|
if (!usbAvailable || Utils.isMonkeyRunning()) {
|
|
getPreferenceScreen().removePreference(mUsbTether);
|
|
}
|
|
|
|
mWifiTetherPreferenceController.displayPreference(getPreferenceScreen());
|
|
|
|
if (!bluetoothAvailable) {
|
|
getPreferenceScreen().removePreference(mBluetoothTether);
|
|
} else {
|
|
BluetoothPan pan = mBluetoothPan.get();
|
|
if (pan != null && pan.isTetheringOn()) {
|
|
mBluetoothTether.setChecked(true);
|
|
} else {
|
|
mBluetoothTether.setChecked(false);
|
|
}
|
|
}
|
|
// Set initial state based on Data Saver mode.
|
|
onDataSaverChanged(mDataSaverBackend.isDataSaverEnabled());
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
mDataSaverBackend.remListener(this);
|
|
|
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
BluetoothProfile profile = mBluetoothPan.getAndSet(null);
|
|
if (profile != null && adapter != null) {
|
|
adapter.closeProfileProxy(BluetoothProfile.PAN, profile);
|
|
}
|
|
|
|
super.onDestroy();
|
|
}
|
|
|
|
@Override
|
|
public void onDataSaverChanged(boolean isDataSaving) {
|
|
mDataSaverEnabled = isDataSaving;
|
|
mUsbTether.setEnabled(!mDataSaverEnabled);
|
|
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
|
mDataSaverFooter.setVisible(mDataSaverEnabled);
|
|
}
|
|
|
|
@Override
|
|
public void onWhitelistStatusChanged(int uid, boolean isWhitelisted) {
|
|
}
|
|
|
|
@Override
|
|
public void onBlacklistStatusChanged(int uid, boolean isBlacklisted) {
|
|
}
|
|
|
|
private class TetherChangeReceiver extends BroadcastReceiver {
|
|
@Override
|
|
public void onReceive(Context content, Intent intent) {
|
|
String action = intent.getAction();
|
|
if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
|
|
// TODO - this should understand the interface types
|
|
ArrayList<String> available = intent.getStringArrayListExtra(
|
|
ConnectivityManager.EXTRA_AVAILABLE_TETHER);
|
|
ArrayList<String> active = intent.getStringArrayListExtra(
|
|
ConnectivityManager.EXTRA_ACTIVE_TETHER);
|
|
ArrayList<String> errored = intent.getStringArrayListExtra(
|
|
ConnectivityManager.EXTRA_ERRORED_TETHER);
|
|
updateState(available.toArray(new String[available.size()]),
|
|
active.toArray(new String[active.size()]),
|
|
errored.toArray(new String[errored.size()]));
|
|
} else if (action.equals(Intent.ACTION_MEDIA_SHARED)) {
|
|
mMassStorageActive = true;
|
|
updateState();
|
|
} else if (action.equals(Intent.ACTION_MEDIA_UNSHARED)) {
|
|
mMassStorageActive = false;
|
|
updateState();
|
|
} else if (action.equals(UsbManager.ACTION_USB_STATE)) {
|
|
mUsbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
|
|
updateState();
|
|
} else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
|
|
if (mBluetoothEnableForTether) {
|
|
switch (intent
|
|
.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
|
|
case BluetoothAdapter.STATE_ON:
|
|
startTethering(TETHERING_BLUETOOTH);
|
|
mBluetoothEnableForTether = false;
|
|
break;
|
|
|
|
case BluetoothAdapter.STATE_OFF:
|
|
case BluetoothAdapter.ERROR:
|
|
mBluetoothEnableForTether = false;
|
|
break;
|
|
|
|
default:
|
|
// ignore transition states
|
|
}
|
|
}
|
|
updateState();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStart() {
|
|
super.onStart();
|
|
|
|
if (mUnavailable) {
|
|
if (!isUiRestrictedByOnlyAdmin()) {
|
|
getEmptyTextView().setText(R.string.tethering_settings_not_available);
|
|
}
|
|
getPreferenceScreen().removeAll();
|
|
return;
|
|
}
|
|
|
|
final Activity activity = getActivity();
|
|
|
|
mStartTetheringCallback = new OnStartTetheringCallback(this);
|
|
|
|
mMassStorageActive = Environment.MEDIA_SHARED.equals(Environment.getExternalStorageState());
|
|
mTetherChangeReceiver = new TetherChangeReceiver();
|
|
IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
|
|
Intent intent = activity.registerReceiver(mTetherChangeReceiver, filter);
|
|
|
|
filter = new IntentFilter();
|
|
filter.addAction(UsbManager.ACTION_USB_STATE);
|
|
activity.registerReceiver(mTetherChangeReceiver, filter);
|
|
|
|
filter = new IntentFilter();
|
|
filter.addAction(Intent.ACTION_MEDIA_SHARED);
|
|
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
|
|
filter.addDataScheme("file");
|
|
activity.registerReceiver(mTetherChangeReceiver, filter);
|
|
|
|
filter = new IntentFilter();
|
|
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
|
|
activity.registerReceiver(mTetherChangeReceiver, filter);
|
|
|
|
if (intent != null) mTetherChangeReceiver.onReceive(activity, intent);
|
|
|
|
updateState();
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
super.onStop();
|
|
|
|
if (mUnavailable) {
|
|
return;
|
|
}
|
|
getActivity().unregisterReceiver(mTetherChangeReceiver);
|
|
mTetherChangeReceiver = null;
|
|
mStartTetheringCallback = null;
|
|
}
|
|
|
|
private void updateState() {
|
|
String[] available = mCm.getTetherableIfaces();
|
|
String[] tethered = mCm.getTetheredIfaces();
|
|
String[] errored = mCm.getTetheringErroredIfaces();
|
|
updateState(available, tethered, errored);
|
|
}
|
|
|
|
private void updateState(String[] available, String[] tethered,
|
|
String[] errored) {
|
|
updateUsbState(available, tethered, errored);
|
|
updateBluetoothState();
|
|
}
|
|
|
|
private void updateUsbState(String[] available, String[] tethered,
|
|
String[] errored) {
|
|
boolean usbAvailable = mUsbConnected && !mMassStorageActive;
|
|
int usbError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
|
for (String s : available) {
|
|
for (String regex : mUsbRegexs) {
|
|
if (s.matches(regex)) {
|
|
if (usbError == ConnectivityManager.TETHER_ERROR_NO_ERROR) {
|
|
usbError = mCm.getLastTetherError(s);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
boolean usbTethered = false;
|
|
for (String s : tethered) {
|
|
for (String regex : mUsbRegexs) {
|
|
if (s.matches(regex)) usbTethered = true;
|
|
}
|
|
}
|
|
boolean usbErrored = false;
|
|
for (String s: errored) {
|
|
for (String regex : mUsbRegexs) {
|
|
if (s.matches(regex)) usbErrored = true;
|
|
}
|
|
}
|
|
|
|
if (usbTethered) {
|
|
mUsbTether.setEnabled(!mDataSaverEnabled);
|
|
mUsbTether.setChecked(true);
|
|
} else if (usbAvailable) {
|
|
mUsbTether.setEnabled(!mDataSaverEnabled);
|
|
mUsbTether.setChecked(false);
|
|
} else {
|
|
mUsbTether.setEnabled(false);
|
|
mUsbTether.setChecked(false);
|
|
}
|
|
}
|
|
|
|
private void updateBluetoothState() {
|
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
if (adapter == null) {
|
|
return;
|
|
}
|
|
int btState = adapter.getState();
|
|
if (btState == BluetoothAdapter.STATE_TURNING_OFF) {
|
|
mBluetoothTether.setEnabled(false);
|
|
} else if (btState == BluetoothAdapter.STATE_TURNING_ON) {
|
|
mBluetoothTether.setEnabled(false);
|
|
} else {
|
|
BluetoothPan bluetoothPan = mBluetoothPan.get();
|
|
if (btState == BluetoothAdapter.STATE_ON && bluetoothPan != null
|
|
&& bluetoothPan.isTetheringOn()) {
|
|
mBluetoothTether.setChecked(true);
|
|
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
|
} else {
|
|
mBluetoothTether.setEnabled(!mDataSaverEnabled);
|
|
mBluetoothTether.setChecked(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean isProvisioningNeededButUnavailable(Context context) {
|
|
return (TetherUtil.isProvisioningNeeded(context)
|
|
&& !isIntentAvailable(context));
|
|
}
|
|
|
|
private static boolean isIntentAvailable(Context context) {
|
|
String[] provisionApp = context.getResources().getStringArray(
|
|
com.android.internal.R.array.config_mobile_hotspot_provision_app);
|
|
if (provisionApp.length < 2) {
|
|
return false;
|
|
}
|
|
final PackageManager packageManager = context.getPackageManager();
|
|
Intent intent = new Intent(Intent.ACTION_MAIN);
|
|
intent.setClassName(provisionApp[0], provisionApp[1]);
|
|
|
|
return (packageManager.queryIntentActivities(intent,
|
|
PackageManager.MATCH_DEFAULT_ONLY).size() > 0);
|
|
}
|
|
|
|
private void startTethering(int choice) {
|
|
if (choice == TETHERING_BLUETOOTH) {
|
|
// Turn on Bluetooth first.
|
|
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
|
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
|
|
mBluetoothEnableForTether = true;
|
|
adapter.enable();
|
|
mBluetoothTether.setEnabled(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
mCm.startTethering(choice, true, mStartTetheringCallback, mHandler);
|
|
}
|
|
|
|
@Override
|
|
public boolean onPreferenceTreeClick(Preference preference) {
|
|
if (preference == mUsbTether) {
|
|
if (mUsbTether.isChecked()) {
|
|
startTethering(TETHERING_USB);
|
|
} else {
|
|
mCm.stopTethering(TETHERING_USB);
|
|
}
|
|
} else if (preference == mBluetoothTether) {
|
|
if (mBluetoothTether.isChecked()) {
|
|
startTethering(TETHERING_BLUETOOTH);
|
|
} else {
|
|
mCm.stopTethering(TETHERING_BLUETOOTH);
|
|
}
|
|
}
|
|
|
|
return super.onPreferenceTreeClick(preference);
|
|
}
|
|
|
|
@Override
|
|
public int getHelpResource() {
|
|
return R.string.help_url_tether;
|
|
}
|
|
|
|
private BluetoothProfile.ServiceListener mProfileServiceListener =
|
|
new BluetoothProfile.ServiceListener() {
|
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
|
mBluetoothPan.set((BluetoothPan) proxy);
|
|
}
|
|
public void onServiceDisconnected(int profile) {
|
|
mBluetoothPan.set(null);
|
|
}
|
|
};
|
|
|
|
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
|
|
new BaseSearchIndexProvider() {
|
|
@Override
|
|
public List<SearchIndexableResource> getXmlResourcesToIndex(
|
|
Context context, boolean enabled) {
|
|
final SearchIndexableResource sir = new SearchIndexableResource(context);
|
|
sir.xmlResId = R.xml.tether_prefs;
|
|
return Arrays.asList(sir);
|
|
}
|
|
|
|
@Override
|
|
protected boolean isPageSearchEnabled(Context context) {
|
|
return !context.getResources().getBoolean(
|
|
R.bool.config_show_all_in_one_tether_settings);
|
|
}
|
|
|
|
@Override
|
|
public List<String> getNonIndexableKeys(Context context) {
|
|
final List<String> keys = super.getNonIndexableKeys(context);
|
|
final ConnectivityManager cm =
|
|
context.getSystemService(ConnectivityManager.class);
|
|
|
|
if (!TetherUtil.isTetherAvailable(context)) {
|
|
keys.add(KEY_TETHER_PREFS_SCREEN);
|
|
keys.add(KEY_WIFI_TETHER);
|
|
}
|
|
|
|
final boolean usbAvailable =
|
|
cm.getTetherableUsbRegexs().length != 0;
|
|
if (!usbAvailable || Utils.isMonkeyRunning()) {
|
|
keys.add(KEY_USB_TETHER_SETTINGS);
|
|
}
|
|
|
|
final boolean bluetoothAvailable =
|
|
cm.getTetherableBluetoothRegexs().length != 0;
|
|
if (!bluetoothAvailable) {
|
|
keys.add(KEY_ENABLE_BLUETOOTH_TETHERING);
|
|
}
|
|
return keys;
|
|
}
|
|
};
|
|
|
|
private static final class OnStartTetheringCallback extends
|
|
ConnectivityManager.OnStartTetheringCallback {
|
|
final WeakReference<TetherSettings> mTetherSettings;
|
|
|
|
OnStartTetheringCallback(TetherSettings settings) {
|
|
mTetherSettings = new WeakReference<>(settings);
|
|
}
|
|
|
|
@Override
|
|
public void onTetheringStarted() {
|
|
update();
|
|
}
|
|
|
|
@Override
|
|
public void onTetheringFailed() {
|
|
update();
|
|
}
|
|
|
|
private void update() {
|
|
TetherSettings settings = mTetherSettings.get();
|
|
if (settings != null) {
|
|
settings.updateState();
|
|
}
|
|
}
|
|
}
|
|
}
|