Files
packages_apps_Settings/src/com/android/settings/TetherSettings.java
Chilun d3358d8a6f Add index for Hotspot settings page into search
Added index provider into TetherSettings and WifiTetherSettings pages.
Added robotests to check the keys returned by getNonIndexableKeys.
Move repeated code into utility class.

Bug: 74777873
Test: make -j RunSettingsRoboTests
Test: atest UniquePreferenceTest
Change-Id: I6cb09d735afa4867d25a79de733ce8c59634e49b
2018-05-09 14:05:15 +08:00

512 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.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.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.datausage.DataSaverBackend;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
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 MetricsEvent.TETHER;
}
public TetherSettings() {
super(UserManager.DISALLOW_CONFIG_TETHERING);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mWifiTetherPreferenceController =
new WifiTetherPreferenceController(context, getLifecycle());
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferencesFromResource(R.xml.tether_prefs);
mFooterPreferenceMixin.createFooterPreference()
.setTitle(R.string.tethering_footer_info);
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.setSummary(R.string.bluetooth_turning_on);
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);
// No ACTION_TETHER_STATE_CHANGED is fired or bluetooth unless a device is
// connected. Need to update state manually.
updateState();
}
}
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 Indexable.SearchIndexProvider 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
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();
}
}
}
}