Connectivity is becoming a mainline module in S, so ConnectivityManager hidden symbols can not be used for outside components. Besides, most Tethering relevant methods or variables on CM are migrated to TetheringManager. So replace all these methods or variables from ConnectivityManager to TetheringManager on Settings. Bug: 180693313 Test: make RunSettingsRoboTests ROBOTEST_FILTER=<Modified Test> Change-Id: Iba4b121a4ddf3f04252aa0355e0e6494a593682a Merged-In: Iba4b121a4ddf3f04252aa0355e0e6494a593682a
278 lines
10 KiB
Java
278 lines
10 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 static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
|
|
|
|
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestrictionEnforced;
|
|
|
|
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.TetheringManager;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.os.UserHandle;
|
|
import android.provider.Settings;
|
|
import android.util.FeatureFlagUtils;
|
|
|
|
import androidx.annotation.VisibleForTesting;
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.core.FeatureFlags;
|
|
import com.android.settings.core.PreferenceControllerMixin;
|
|
import com.android.settingslib.TetherUtil;
|
|
import com.android.settingslib.core.AbstractPreferenceController;
|
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
import com.android.settingslib.core.lifecycle.events.OnCreate;
|
|
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
|
import com.android.settingslib.core.lifecycle.events.OnPause;
|
|
import com.android.settingslib.core.lifecycle.events.OnResume;
|
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
public class TetherPreferenceController extends AbstractPreferenceController implements
|
|
PreferenceControllerMixin, LifecycleObserver, OnCreate, 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 TetheringManager mTetheringManager;
|
|
@VisibleForTesting
|
|
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();
|
|
mTetheringManager = null;
|
|
}
|
|
|
|
public TetherPreferenceController(Context context, Lifecycle lifecycle) {
|
|
super(context);
|
|
mBluetoothPan = new AtomicReference<>();
|
|
mAdminDisallowedTetherConfig = isTetherConfigDisallowed(context);
|
|
mConnectivityManager =
|
|
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
mTetheringManager = context.getSystemService(TetheringManager.class);
|
|
if (lifecycle != null) {
|
|
lifecycle.addObserver(this);
|
|
}
|
|
}
|
|
|
|
@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));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isAvailable() {
|
|
return TetherUtil.isTetherAvailable(mContext)
|
|
&& !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE);
|
|
}
|
|
|
|
@Override
|
|
public void updateState(Preference preference) {
|
|
updateSummary();
|
|
}
|
|
|
|
@Override
|
|
public String getPreferenceKey() {
|
|
return KEY_TETHER_SETTINGS;
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle savedInstanceState) {
|
|
if (mBluetoothAdapter != null &&
|
|
mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
|
mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
|
|
BluetoothProfile.PAN);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
if (mAirplaneModeObserver == null) {
|
|
mAirplaneModeObserver = new SettingObserver();
|
|
}
|
|
if (mTetherReceiver == null) {
|
|
mTetherReceiver = new TetherBroadcastReceiver();
|
|
}
|
|
mContext.registerReceiver(
|
|
mTetherReceiver, new IntentFilter(TetheringManager.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);
|
|
}
|
|
}
|
|
|
|
public static boolean isTetherConfigDisallowed(Context context) {
|
|
return checkIfRestrictionEnforced(
|
|
context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void updateSummary() {
|
|
if (mPreference == null) {
|
|
// Preference is not ready yet.
|
|
return;
|
|
}
|
|
String[] allTethered = mTetheringManager.getTetheredIfaces();
|
|
String[] wifiTetherRegex = mTetheringManager.getTetherableWifiRegexs();
|
|
String[] bluetoothRegex = mTetheringManager.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 mTetheringManager.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();
|
|
}
|
|
|
|
}
|
|
}
|