Files
app_Settings/src/com/android/settings/network/TetherEnabler.java
paulhu 6151c227e0 Replace ConnectivityManager hidden symbols on Settings
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
2021-02-24 06:17:17 +00:00

406 lines
15 KiB
Java

/*
* Copyright (C) 2019 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.net.ConnectivityManager.TETHERING_BLUETOOTH;
import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.ConnectivityManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_ETHERNET;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.annotation.IntDef;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.TetheringManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleObserver;
import androidx.lifecycle.OnLifecycleEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.datausage.DataSaverBackend;
import com.android.settings.widget.SwitchWidgetController;
import java.lang.annotation.Retention;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* TetherEnabler is a helper to manage Tethering switch on/off state. It offers helper functions to
* turn on/off different types of tethering interfaces and ensures tethering state updated by data
* saver state.
*
* This class is not designed for extending. It's extendable solely for the test purpose.
*/
public class TetherEnabler implements SwitchWidgetController.OnSwitchChangeListener,
DataSaverBackend.Listener, LifecycleObserver {
/**
* Interface definition for a callback to be invoked when the tethering has been updated.
*/
public interface OnTetherStateUpdateListener {
/**
* Called when the tethering state has changed.
*
* @param state The new tethering state.
*/
void onTetherStateUpdated(@TetheringState int state);
}
private static final String TAG = "TetherEnabler";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@Retention(SOURCE)
@IntDef(
flag = true,
value = {
TETHERING_OFF,
TETHERING_WIFI_ON,
TETHERING_USB_ON,
TETHERING_BLUETOOTH_ON,
TETHERING_ETHERNET_ON
}
)
@interface TetheringState {}
public static final int TETHERING_OFF = 0;
public static final int TETHERING_WIFI_ON = 1 << TETHERING_WIFI;
public static final int TETHERING_USB_ON = 1 << TETHERING_USB;
public static final int TETHERING_BLUETOOTH_ON = 1 << TETHERING_BLUETOOTH;
public static final int TETHERING_ETHERNET_ON = 1 << TETHERING_ETHERNET;
@VisibleForTesting
final List<OnTetherStateUpdateListener> mListeners;
private final Handler mMainThreadHandler;
private final SwitchWidgetController mSwitchWidgetController;
private final WifiManager mWifiManager;
private final ConnectivityManager mConnectivityManager;
private final TetheringManager mTetheringManager;
private final UserManager mUserManager;
private final String mEthernetRegex;
private final DataSaverBackend mDataSaverBackend;
private boolean mDataSaverEnabled;
@VisibleForTesting
boolean mBluetoothTetheringStoppedByUser;
private final Context mContext;
@VisibleForTesting
TetheringManager.TetheringEventCallback mTetheringEventCallback;
@VisibleForTesting
ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback;
private final AtomicReference<BluetoothPan> mBluetoothPan;
private boolean mBluetoothEnableForTether;
private final BluetoothAdapter mBluetoothAdapter;
public TetherEnabler(Context context, SwitchWidgetController switchWidgetController,
AtomicReference<BluetoothPan> bluetoothPan) {
mContext = context;
mSwitchWidgetController = switchWidgetController;
mDataSaverBackend = new DataSaverBackend(context);
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mTetheringManager = (TetheringManager) context.getSystemService(Context.TETHERING_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothPan = bluetoothPan;
mEthernetRegex =
context.getString(com.android.internal.R.string.config_ethernet_iface_regex);
mDataSaverEnabled = mDataSaverBackend.isDataSaverEnabled();
mListeners = new ArrayList<>();
mMainThreadHandler = new Handler(Looper.getMainLooper());
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onStart() {
mDataSaverBackend.addListener(this);
mSwitchWidgetController.setListener(this);
mSwitchWidgetController.startListening();
final IntentFilter filter = new IntentFilter(
TetheringManager.ACTION_TETHER_STATE_CHANGED);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
mContext.registerReceiver(mTetherChangeReceiver, filter);
mTetheringEventCallback =
new TetheringManager.TetheringEventCallback() {
@Override
public void onTetheredInterfacesChanged(List<String> interfaces) {
updateState(interfaces.toArray(new String[interfaces.size()]));
}
};
mTetheringManager.registerTetheringEventCallback(new HandlerExecutor(mMainThreadHandler),
mTetheringEventCallback);
mOnStartTetheringCallback = new OnStartTetheringCallback(this);
updateState(null/*tethered*/);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onStop() {
mBluetoothTetheringStoppedByUser = false;
mDataSaverBackend.remListener(this);
mSwitchWidgetController.stopListening();
mContext.unregisterReceiver(mTetherChangeReceiver);
mTetheringManager.unregisterTetheringEventCallback(mTetheringEventCallback);
mTetheringEventCallback = null;
}
public void addListener(OnTetherStateUpdateListener listener) {
if (listener != null && !mListeners.contains(listener)) {
listener.onTetherStateUpdated(getTetheringState(null /* tethered */));
mListeners.add(listener);
}
}
public void removeListener(OnTetherStateUpdateListener listener) {
if (listener != null) {
mListeners.remove(listener);
}
}
private void setSwitchEnabled(boolean enabled) {
mSwitchWidgetController.setEnabled(
enabled && !mDataSaverEnabled && mUserManager.isAdminUser());
}
@VisibleForTesting
void updateState(@Nullable String[] tethered) {
int state = getTetheringState(tethered);
if (DEBUG) {
Log.d(TAG, "updateState: " + state);
}
setSwitchCheckedInternal(state != TETHERING_OFF);
setSwitchEnabled(true);
for (int i = 0, size = mListeners.size(); i < size; ++i) {
mListeners.get(i).onTetherStateUpdated(state);
}
}
private void setSwitchCheckedInternal(boolean checked) {
try {
mSwitchWidgetController.stopListening();
} catch (IllegalStateException e) {
Log.e(TAG, "failed to stop switch widget listener when set check internally");
return;
}
mSwitchWidgetController.setChecked(checked);
mSwitchWidgetController.startListening();
}
@VisibleForTesting
@TetheringState
int getTetheringState(@Nullable String[] tethered) {
int tetherState = TETHERING_OFF;
if (tethered == null) {
tethered = mTetheringManager.getTetheredIfaces();
}
if (mWifiManager.isWifiApEnabled()) {
tetherState |= TETHERING_WIFI_ON;
}
// Only check bluetooth tethering state if not stopped by user already.
if (!mBluetoothTetheringStoppedByUser) {
final BluetoothPan pan = mBluetoothPan.get();
if (mBluetoothAdapter != null &&
mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON
&& pan != null && pan.isTetheringOn()) {
tetherState |= TETHERING_BLUETOOTH_ON;
}
}
String[] usbRegexs = mTetheringManager.getTetherableUsbRegexs();
for (String s : tethered) {
for (String regex : usbRegexs) {
if (s.matches(regex)) {
tetherState |= TETHERING_USB_ON;
}
}
if (s.matches(mEthernetRegex)) {
tetherState |= TETHERING_ETHERNET_ON;
}
}
return tetherState;
}
public static boolean isTethering(@TetheringState int state, int choice) {
return (state & (1 << choice)) != TETHERING_OFF;
}
@Override
public boolean onSwitchToggled(boolean isChecked) {
if (isChecked) {
startTethering(TETHERING_WIFI);
} else {
stopTethering(TETHERING_USB);
stopTethering(TETHERING_WIFI);
stopTethering(TETHERING_BLUETOOTH);
stopTethering(TETHERING_ETHERNET);
}
return true;
}
public void stopTethering(int choice) {
int state = getTetheringState(null /* tethered */);
if (isTethering(state, choice)) {
setSwitchEnabled(false);
mConnectivityManager.stopTethering(choice);
if (choice == TETHERING_BLUETOOTH) {
// Stop bluetooth tether won't invoke tether state changed callback, so we need this
// boolean to remember the user action and update UI state immediately.
mBluetoothTetheringStoppedByUser = true;
updateState(null /* tethered */);
}
}
}
public void startTethering(int choice) {
if (choice == TETHERING_BLUETOOTH) {
mBluetoothTetheringStoppedByUser = false;
}
int state = getTetheringState(null /* tethered */);
if (isTethering(state, choice)) {
return;
}
if (choice == TETHERING_BLUETOOTH && mBluetoothAdapter != null
&& mBluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) {
if (DEBUG) {
Log.d(TAG, "Turn on bluetooth first.");
}
mBluetoothEnableForTether = true;
mBluetoothAdapter.enable();
return;
}
setSwitchEnabled(false);
mConnectivityManager.startTethering(choice, true /* showProvisioningUi */,
mOnStartTetheringCallback, mMainThreadHandler);
}
private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
boolean shouldUpdateState = false;
if (TextUtils.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION, action)) {
shouldUpdateState = handleWifiApStateChanged(intent.getIntExtra(
WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED));
} else if (TextUtils.equals(BluetoothAdapter.ACTION_STATE_CHANGED, action)) {
shouldUpdateState = handleBluetoothStateChanged(intent
.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR));
}
if (shouldUpdateState) {
updateState(null /* tethered */);
}
}
};
private boolean handleBluetoothStateChanged(int state) {
switch (state) {
case BluetoothAdapter.STATE_ON:
if (mBluetoothEnableForTether) {
startTethering(TETHERING_BLUETOOTH);
}
// Fall through.
case BluetoothAdapter.STATE_OFF:
// Fall through.
case BluetoothAdapter.ERROR:
mBluetoothEnableForTether = false;
return true;
default:
// Return false for transition states.
return false;
}
}
private boolean handleWifiApStateChanged(int state) {
switch (state) {
case WifiManager.WIFI_AP_STATE_FAILED:
Log.e(TAG, "Wifi AP is failed!");
// fall through
case WifiManager.WIFI_AP_STATE_ENABLED:
// fall through
case WifiManager.WIFI_AP_STATE_DISABLED:
return true;
default:
// return false for transition state
return false;
}
}
@Override
public void onDataSaverChanged(boolean isDataSaving) {
mDataSaverEnabled = isDataSaving;
setSwitchEnabled(true);
}
@Override
public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
// we don't care, since we just want to read the value
}
@Override
public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
// we don't care, since we just want to read the value
}
private static final class OnStartTetheringCallback extends
ConnectivityManager.OnStartTetheringCallback {
final WeakReference<TetherEnabler> mTetherEnabler;
OnStartTetheringCallback(TetherEnabler enabler) {
mTetherEnabler = new WeakReference<>(enabler);
}
@Override
public void onTetheringStarted() {
update();
}
@Override
public void onTetheringFailed() {
update();
}
private void update() {
TetherEnabler enabler = mTetherEnabler.get();
if (enabler != null) {
enabler.updateState(null/*tethered*/);
}
}
}
}