From 02dfed684670142d56f5ac6fc3e3a096f9c5eeec Mon Sep 17 00:00:00 2001 From: Zhen Zhang Date: Mon, 13 Jan 2020 14:57:23 -0800 Subject: [PATCH] Use TetherEnabler in AllInOneTetherSettings to manage master switch In AllInOneTetherSettings, we use TetherEnabler to hanle all behavior related to tethering switch on/off. In TetherEnbler, add WifiManager.WIFI_AP_STATE_CHANGED_ACTION to cover all possible tethering state change. TetherEnablerTest is modified accordingly. Bug: 147322704 Test: make RunSettingsRoboTests ROBOTEST_FILTER=CodeInspectionTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=TetherEnablerTest Test: make RunSettingsRoboTests ROBOTEST_FILTER=AllInOneTetherSettingsTest Change-Id: I505b3825f79260983fff9d3935ba834ad8f9f690 --- .../settings/AllInOneTetherSettings.java | 76 ++++---- .../BluetoothTetherPreferenceController.java | 3 +- .../settings/network/TetherEnabler.java | 167 +++++++++++++----- .../UsbTetherPreferenceController.java | 3 +- .../settings/network/TetherEnablerTest.java | 4 +- 5 files changed, 175 insertions(+), 78 deletions(-) diff --git a/src/com/android/settings/AllInOneTetherSettings.java b/src/com/android/settings/AllInOneTetherSettings.java index d6366b21280..fbf0280239e 100644 --- a/src/com/android/settings/AllInOneTetherSettings.java +++ b/src/com/android/settings/AllInOneTetherSettings.java @@ -20,21 +20,20 @@ import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.TETHERING_WIFI; import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION; -import static com.android.settings.network.WifiTetherDisablePreferenceController - .KEY_ENABLE_WIFI_TETHERING; +import static com.android.settings.network.WifiTetherDisablePreferenceController.KEY_ENABLE_WIFI_TETHERING; 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.SharedPreferences; -import android.net.ConnectivityManager; import android.net.wifi.SoftApConfiguration; import android.net.wifi.WifiManager; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; import android.os.UserManager; import android.text.TextUtils; import android.util.Log; @@ -47,6 +46,8 @@ import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.datausage.DataSaverBackend; import com.android.settings.network.TetherEnabler; import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settings.widget.SwitchBar; +import com.android.settings.widget.SwitchBarController; import com.android.settings.wifi.tether.WifiTetherApBandPreferenceController; import com.android.settings.wifi.tether.WifiTetherAutoOffPreferenceController; import com.android.settings.wifi.tether.WifiTetherBasePreferenceController; @@ -59,10 +60,10 @@ import com.android.settingslib.search.SearchIndexable; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicReference; /** - * Displays preferences for Tethering. - * TODO(b/147322704): Use TetherEnabler in this fragment to manage tethering switch on/off. + * Displays preferences for all Tethering options. * TODO(b/147323306): Add tether option preferences into this fragment after controllers created. */ @SearchIndexable @@ -96,6 +97,7 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment private WifiManager mWifiManager; private boolean mRestartWifiApAfterConfigChange; + private final AtomicReference mBluetoothPan = new AtomicReference<>(); private WifiTetherSSIDPreferenceController mSSIDPreferenceController; private WifiTetherPasswordPreferenceController mPasswordPreferenceController; @@ -103,8 +105,8 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment private WifiTetherSecurityPreferenceController mSecurityPreferenceController; private PreferenceGroup mWifiTetherGroup; private SharedPreferences mSharedPreferences; - private ConnectivityManager mConnectivityManager; private boolean mWifiTetherChosen; + private TetherEnabler mTetherEnabler; private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { @Override @@ -118,19 +120,30 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_DISABLED && mRestartWifiApAfterConfigChange) { mRestartWifiApAfterConfigChange = false; - startTether(); + mTetherEnabler.startTethering(TETHERING_WIFI); } } else if (TextUtils.equals(action, WIFI_AP_STATE_CHANGED_ACTION)) { int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, 0); if (state == WifiManager.WIFI_AP_STATE_DISABLED && mRestartWifiApAfterConfigChange) { mRestartWifiApAfterConfigChange = false; - startTether(); + mTetherEnabler.startTethering(TETHERING_WIFI); } } } }; + private final 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); + } + }; + @Override public int getMetricsCategory() { return SettingsEnums.TETHER; @@ -144,8 +157,6 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment public void onAttach(Context context) { super.onAttach(context); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mConnectivityManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); mSharedPreferences = context.getSharedPreferences(TetherEnabler.SHARED_PREF, Context.MODE_PRIVATE); @@ -181,6 +192,27 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment // TODO(b/147325229): Hide advanced settings like security and ap band. } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (mUnavailable) { + return; + } + // Assume we are in a SettingsActivity. This is only safe because we currently use + // SettingsActivity as base for all preference fragments. + final SettingsActivity activity = (SettingsActivity) getActivity(); + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null) { + adapter.getProfileProxy(activity.getApplicationContext(), mProfileServiceListener, + BluetoothProfile.PAN); + } + final SwitchBar switchBar = activity.getSwitchBar(); + mTetherEnabler = new TetherEnabler(activity, + new SwitchBarController(switchBar), mBluetoothPan); + getSettingsLifecycle().addObserver(mTetherEnabler); + switchBar.show(); + } + @Override public void onStart() { super.onStart(); @@ -297,8 +329,7 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment Log.d(TAG, "Wifi AP config changed while enabled, stop and restart"); } mRestartWifiApAfterConfigChange = true; - // TODO(b/147322704): Use TethetEnabler to stop tethering. - mConnectivityManager.stopTethering(TETHERING_WIFI); + mTetherEnabler.stopTethering(TETHERING_WIFI); } if (controller instanceof WifiTetherSecurityPreferenceController) { @@ -335,23 +366,6 @@ public final class AllInOneTetherSettings extends RestrictedDashboardFragment } } - private void startTether() { - // TODO(b/147322704): Use TetherEnabler to start tethering. - if (mWifiManager.isWifiApEnabled()) { - return; - } - mConnectivityManager.startTethering(ConnectivityManager.TETHERING_WIFI, - true /*showProvisioningUi*/, - new ConnectivityManager.OnStartTetheringCallback() { - @Override - public void onTetheringFailed() { - super.onTetheringFailed(); - // Do nothing. There is no UI to update at this point. - } - }, - new Handler(Looper.getMainLooper())); - } - private void reConfigInitialExpandedChildCount() { getPreferenceScreen().setInitialExpandedChildrenCount(getInitialExpandedChildCount()); } diff --git a/src/com/android/settings/network/BluetoothTetherPreferenceController.java b/src/com/android/settings/network/BluetoothTetherPreferenceController.java index 10849a0d235..327b6ac37a8 100644 --- a/src/com/android/settings/network/BluetoothTetherPreferenceController.java +++ b/src/com/android/settings/network/BluetoothTetherPreferenceController.java @@ -47,8 +47,7 @@ public final class BluetoothTetherPreferenceController extends AbstractPreferenc private static final String TAG = "BluetoothTetherPreferenceController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - @VisibleForTesting - static final String PREF_KEY = "enable_bluetooth_tethering"; + public static final String PREF_KEY = "enable_bluetooth_tethering"; private final ConnectivityManager mCm; private int mBluetoothState; private Preference mPreference; diff --git a/src/com/android/settings/network/TetherEnabler.java b/src/com/android/settings/network/TetherEnabler.java index 9021a268cca..e2b4c682168 100644 --- a/src/com/android/settings/network/TetherEnabler.java +++ b/src/com/android/settings/network/TetherEnabler.java @@ -42,6 +42,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.settings.datausage.DataSaverBackend; import com.android.settings.widget.SwitchWidgetController; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; @@ -78,14 +79,7 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang private final Context mContext; @VisibleForTesting - final ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback = - new ConnectivityManager.OnStartTetheringCallback() { - @Override - public void onTetheringFailed() { - super.onTetheringFailed(); - mSwitchWidgetController.setChecked(false); - } - }; + ConnectivityManager.OnStartTetheringCallback mOnStartTetheringCallback; private final AtomicReference mBluetoothPan; private final SharedPreferences mSharedPreferences; private boolean mBluetoothEnableForTether; @@ -110,11 +104,15 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang mDataSaverBackend.addListener(this); mSwitchWidgetController.setListener(this); mSwitchWidgetController.startListening(); - IntentFilter filter = new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED); + + final IntentFilter filter = new IntentFilter( + ConnectivityManager.ACTION_TETHER_STATE_CHANGED); + filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION); filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mContext.registerReceiver(mTetherChangeReceiver, filter); - mSwitchWidgetController.setChecked(isTethering()); - setSwitchWidgetEnabled(true); + + mOnStartTetheringCallback = new OnStartTetheringCallback(this); + updateState(); } @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @@ -134,8 +132,14 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang mContext.unregisterReceiver(mTetherChangeReceiver); } - private void setSwitchWidgetEnabled(boolean enabled) { - mSwitchWidgetController.setEnabled(enabled && !mDataSaverEnabled); + private void updateState() { + mSwitchWidgetController.setChecked(isTethering()); + mSwitchWidgetController.setEnabled(!mDataSaverEnabled); + } + + private void updateState(String[] tethered) { + mSwitchWidgetController.setChecked(isTethering(tethered)); + mSwitchWidgetController.setEnabled(!mDataSaverEnabled); } private boolean isTethering() { @@ -148,6 +152,10 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang return true; } + if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { + return true; + } + final BluetoothPan pan = mBluetoothPan.get(); return pan != null && pan.isTetheringOn(); @@ -155,31 +163,41 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang @Override public boolean onSwitchToggled(boolean isChecked) { - if (isChecked) { + if (isChecked && !isTethering()) { startTether(); - } else { + } + + if (!isChecked && isTethering()) { stopTether(); } return true; } - @VisibleForTesting - void stopTether() { + private void stopTether() { // Wi-Fi tether is selected by default. if (mSharedPreferences.getBoolean(WIFI_TETHER_KEY, true)) { - mConnectivityManager.stopTethering(TETHERING_WIFI); + stopTethering(TETHERING_WIFI); } if (mSharedPreferences.getBoolean(USB_TETHER_KEY, false)) { - mConnectivityManager.stopTethering(TETHERING_USB); + stopTethering(TETHERING_USB); } if (mSharedPreferences.getBoolean(BLUETOOTH_TETHER_KEY, false)) { - mConnectivityManager.stopTethering(TETHERING_BLUETOOTH); + stopTethering(TETHERING_BLUETOOTH); } } + /** + * Use this method to stop a single choice of tethering. + * + * @param choice The choice of tethering to stop. + */ + public void stopTethering(int choice) { + mConnectivityManager.stopTethering(choice); + } + @VisibleForTesting void startTether() { @@ -197,8 +215,16 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang } } - @VisibleForTesting - void startTethering(int choice) { + /** + * Use this method to start a single choice of tethering. + * For bluetooth tethering, it will first turn on bluetooth if bluetooth is off. + * For Wi-Fi tethering, it will be no-op if Wi-Fi tethering already active. + * + * @param choice The choice of tethering to start. + */ + public void startTethering(int choice) { + mSwitchWidgetController.setEnabled(false); + if (choice == TETHERING_WIFI && mWifiManager.isWifiApEnabled()) { if (DEBUG) { Log.d(TAG, "Wifi tether already active!"); @@ -224,36 +250,67 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang private final BroadcastReceiver mTetherChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); + final String action = intent.getAction(); + ArrayList active = null; + boolean shouldUpdateState = false; if (TextUtils.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED, action)) { - ArrayList active = intent.getStringArrayListExtra( - ConnectivityManager.EXTRA_ACTIVE_TETHER); - mSwitchWidgetController.setChecked( - isTethering(active.toArray(new String[active.size()]))); + active = intent.getStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER); + shouldUpdateState = true; + } else 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)) { - switch (intent - .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) { - case BluetoothAdapter.STATE_ON: - if (mBluetoothEnableForTether) { - startTethering(TETHERING_BLUETOOTH); - } - // Fall through. - case BluetoothAdapter.STATE_OFF: - // Fall through. - case BluetoothAdapter.ERROR: - mBluetoothEnableForTether = false; - break; - default: - // ignore transition states + shouldUpdateState = handleBluetoothStateChanged(intent + .getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)); + } + + if (shouldUpdateState) { + if (active != null) { + updateState(active.toArray(new String[0])); + } else { + updateState(); } } } }; + 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; - setSwitchWidgetEnabled(!isDataSaving); + mSwitchWidgetController.setEnabled(!isDataSaving); } @Override @@ -291,4 +348,30 @@ public final class TetherEnabler implements SwitchWidgetController.OnSwitchChang } } } + + private static final class OnStartTetheringCallback extends + ConnectivityManager.OnStartTetheringCallback { + final WeakReference 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(); + } + } + } } diff --git a/src/com/android/settings/network/UsbTetherPreferenceController.java b/src/com/android/settings/network/UsbTetherPreferenceController.java index 74e8be8abac..572248075f7 100644 --- a/src/com/android/settings/network/UsbTetherPreferenceController.java +++ b/src/com/android/settings/network/UsbTetherPreferenceController.java @@ -49,8 +49,7 @@ public final class UsbTetherPreferenceController extends AbstractPreferenceContr private static final String TAG = "UsbTetherPrefController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - @VisibleForTesting - static final String PREF_KEY = "enable_usb_tethering"; + public static final String PREF_KEY = "enable_usb_tethering"; private final ConnectivityManager mCm; private boolean mUsbConnected; diff --git a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java index 9dcfa8914d0..a2d55dc2dee 100644 --- a/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java +++ b/tests/robotests/src/com/android/settings/network/TetherEnablerTest.java @@ -109,8 +109,10 @@ public class TetherEnablerTest { @Test public void startTether_fail_resetSwitchBar() { when(mNetworkPolicyManager.getRestrictBackground()).thenReturn(false); - + mEnabler.onStart(); mEnabler.startTether(); + + when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[0]); mEnabler.mOnStartTetheringCallback.onTetheringFailed(); assertThat(mSwitchBar.isChecked()).isFalse();