User can navigates to the page and turn hotspot on or off from the quick settings. Need to listen to the tether state updates to refresh the summary for the Hotspot & tethering preference accordingly. Change-Id: I7c6869b909306b09556e19cf7b7543ce9dcd890e Fix: 38452559 Test: make RunSettingsRoboTests
264 lines
9.9 KiB
Java
264 lines
9.9 KiB
Java
/*
|
|
* Copyright (C) 2016 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.network;
|
|
|
|
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.database.ContentObserver;
|
|
import android.net.ConnectivityManager;
|
|
import android.net.Uri;
|
|
import android.os.Handler;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.support.annotation.VisibleForTesting;
|
|
import android.support.v7.preference.Preference;
|
|
import android.support.v7.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.TetherSettings;
|
|
import com.android.settings.core.PreferenceController;
|
|
import com.android.settings.core.lifecycle.Lifecycle;
|
|
import com.android.settings.core.lifecycle.LifecycleObserver;
|
|
import com.android.settings.core.lifecycle.events.OnDestroy;
|
|
import com.android.settings.core.lifecycle.events.OnPause;
|
|
import com.android.settings.core.lifecycle.events.OnResume;
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
|
|
import static com.android.settingslib.RestrictedLockUtils.checkIfRestrictionEnforced;
|
|
import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction;
|
|
|
|
public class TetherPreferenceController extends PreferenceController
|
|
implements LifecycleObserver, OnResume, OnPause, OnDestroy {
|
|
|
|
private static final String KEY_TETHER_SETTINGS = "tether_settings";
|
|
|
|
private final boolean mAdminDisallowedTetherConfig;
|
|
private final AtomicReference<BluetoothPan> mBluetoothPan;
|
|
private final ConnectivityManager mConnectivityManager;
|
|
private final BluetoothAdapter mBluetoothAdapter;
|
|
private final BluetoothProfile.ServiceListener mBtProfileServiceListener =
|
|
new android.bluetooth.BluetoothProfile.ServiceListener() {
|
|
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
|
mBluetoothPan.set((BluetoothPan) proxy);
|
|
updateSummary();
|
|
}
|
|
|
|
public void onServiceDisconnected(int profile) {
|
|
mBluetoothPan.set(null);
|
|
}
|
|
};
|
|
|
|
private SettingObserver mAirplaneModeObserver;
|
|
private Preference mPreference;
|
|
private TetherBroadcastReceiver mTetherReceiver;
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
TetherPreferenceController() {
|
|
super(null);
|
|
mAdminDisallowedTetherConfig = false;
|
|
mBluetoothPan = new AtomicReference<>();
|
|
mConnectivityManager = null;
|
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
}
|
|
|
|
public TetherPreferenceController(Context context, Lifecycle lifecycle) {
|
|
super(context);
|
|
mBluetoothPan = new AtomicReference<>();
|
|
mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
|
|
context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
|
|
mConnectivityManager =
|
|
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
if (lifecycle != null) {
|
|
lifecycle.addObserver(this);
|
|
}
|
|
if (mBluetoothAdapter != null) {
|
|
mBluetoothAdapter.getProfileProxy(context, mBtProfileServiceListener,
|
|
BluetoothProfile.PAN);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void displayPreference(PreferenceScreen screen) {
|
|
super.displayPreference(screen);
|
|
mPreference = screen.findPreference(KEY_TETHER_SETTINGS);
|
|
if (mPreference != null && !mAdminDisallowedTetherConfig) {
|
|
mPreference.setTitle(
|
|
com.android.settingslib.Utils.getTetheringLabel(mConnectivityManager));
|
|
|
|
// Grey out if provisioning is not available.
|
|
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isAvailable() {
|
|
final boolean isBlocked =
|
|
(!mConnectivityManager.isTetheringSupported() && !mAdminDisallowedTetherConfig)
|
|
|| hasBaseUserRestriction(mContext, DISALLOW_CONFIG_TETHERING,
|
|
UserHandle.myUserId());
|
|
return !isBlocked;
|
|
}
|
|
|
|
@Override
|
|
public void updateState(Preference preference) {
|
|
updateSummary();
|
|
}
|
|
|
|
@Override
|
|
public String getPreferenceKey() {
|
|
return KEY_TETHER_SETTINGS;
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
if (mAirplaneModeObserver == null) {
|
|
mAirplaneModeObserver = new SettingObserver();
|
|
}
|
|
if (mTetherReceiver == null) {
|
|
mTetherReceiver = new TetherBroadcastReceiver();
|
|
}
|
|
mContext.registerReceiver(
|
|
mTetherReceiver, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
|
|
mContext.getContentResolver()
|
|
.registerContentObserver(mAirplaneModeObserver.uri, false, mAirplaneModeObserver);
|
|
}
|
|
|
|
@Override
|
|
public void onPause() {
|
|
if (mAirplaneModeObserver != null) {
|
|
mContext.getContentResolver().unregisterContentObserver(mAirplaneModeObserver);
|
|
}
|
|
if (mTetherReceiver != null) {
|
|
mContext.unregisterReceiver(mTetherReceiver);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
|
|
if (profile != null && mBluetoothAdapter != null) {
|
|
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateSummary() {
|
|
if (mPreference == null) {
|
|
// Preference is not ready yet.
|
|
return;
|
|
}
|
|
String[] allTethered = mConnectivityManager.getTetheredIfaces();
|
|
String[] wifiTetherRegex = mConnectivityManager.getTetherableWifiRegexs();
|
|
String[] bluetoothRegex = mConnectivityManager.getTetherableBluetoothRegexs();
|
|
|
|
boolean hotSpotOn = false;
|
|
boolean tetherOn = false;
|
|
if (allTethered != null) {
|
|
if (wifiTetherRegex != null) {
|
|
for (String tethered : allTethered) {
|
|
for (String regex : wifiTetherRegex) {
|
|
if (tethered.matches(regex)) {
|
|
hotSpotOn = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (allTethered.length > 1) {
|
|
// We have more than 1 tethered connection
|
|
tetherOn = true;
|
|
} else if (allTethered.length == 1) {
|
|
// We have more than 1 tethered, it's either wifiTether (hotspot), or other type of
|
|
// tether.
|
|
tetherOn = !hotSpotOn;
|
|
} else {
|
|
// No tethered connection.
|
|
tetherOn = false;
|
|
}
|
|
}
|
|
if (!tetherOn
|
|
&& bluetoothRegex != null && bluetoothRegex.length > 0
|
|
&& mBluetoothAdapter != null
|
|
&& mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
|
// Check bluetooth state. It's not included in mConnectivityManager.getTetheredIfaces.
|
|
final BluetoothPan pan = mBluetoothPan.get();
|
|
tetherOn = pan != null && pan.isTetheringOn();
|
|
}
|
|
if (!hotSpotOn && !tetherOn) {
|
|
// Both off
|
|
mPreference.setSummary(R.string.switch_off_text);
|
|
} else if (hotSpotOn && tetherOn) {
|
|
// Both on
|
|
mPreference.setSummary(R.string.tether_settings_summary_hotspot_on_tether_on);
|
|
} else if (hotSpotOn) {
|
|
mPreference.setSummary(R.string.tether_settings_summary_hotspot_on_tether_off);
|
|
} else {
|
|
mPreference.setSummary(R.string.tether_settings_summary_hotspot_off_tether_on);
|
|
}
|
|
}
|
|
|
|
private void updateSummaryToOff() {
|
|
if (mPreference == null) {
|
|
// Preference is not ready yet.
|
|
return;
|
|
}
|
|
mPreference.setSummary(R.string.switch_off_text);
|
|
}
|
|
|
|
class SettingObserver extends ContentObserver {
|
|
|
|
public final Uri uri;
|
|
|
|
public SettingObserver() {
|
|
super(new Handler());
|
|
uri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
|
|
}
|
|
|
|
@Override
|
|
public void onChange(boolean selfChange, Uri uri) {
|
|
super.onChange(selfChange, uri);
|
|
if (this.uri.equals(uri)) {
|
|
boolean isAirplaneMode = Settings.Global.getInt(mContext.getContentResolver(),
|
|
Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
|
|
if (isAirplaneMode) {
|
|
// Airplane mode is on. Update summary to say tether is OFF directly. We cannot
|
|
// go through updateSummary() because turning off tether takes time, and we
|
|
// might still get "ON" status when rerun updateSummary(). So, just say it's off
|
|
updateSummaryToOff();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
class TetherBroadcastReceiver extends BroadcastReceiver {
|
|
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
updateSummary();
|
|
}
|
|
|
|
}
|
|
}
|