diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9c3a8106e7b..2494e3a0171 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2941,6 +2941,12 @@
Tethering
Hotspot & Tethering
+
+ Hotspot on, tethering
+
+ Hotspot on
+
+ Tethering
"Can\u2019t tether or use portable hotspots while Data Saver is on"
diff --git a/src/com/android/settings/network/TetherPreferenceController.java b/src/com/android/settings/network/TetherPreferenceController.java
index e24c873b10b..236fa96a282 100644
--- a/src/com/android/settings/network/TetherPreferenceController.java
+++ b/src/com/android/settings/network/TetherPreferenceController.java
@@ -15,17 +15,23 @@
*/
package com.android.settings.network;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.net.ConnectivityManager;
import android.os.UserHandle;
import android.os.UserManager;
+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 java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
import static com.android.settingslib.RestrictedLockUtils.checkIfRestrictionEnforced;
@@ -36,28 +42,60 @@ public class TetherPreferenceController extends PreferenceController {
private static final String KEY_TETHER_SETTINGS = "tether_settings";
private final boolean mAdminDisallowedTetherConfig;
+ private final AtomicReference mBluetoothPan;
private final ConnectivityManager mConnectivityManager;
+ private final BluetoothAdapter mBluetoothAdapter;
private final UserManager mUserManager;
+ 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 Preference mPreference;
+
+ @VisibleForTesting(otherwise = VisibleForTesting.NONE)
+ TetherPreferenceController() {
+ super(null);
+ mAdminDisallowedTetherConfig = false;
+ mBluetoothPan = null;
+ mConnectivityManager = null;
+ mBluetoothAdapter = null;
+ mUserManager = null;
+ }
+
public TetherPreferenceController(Context context) {
super(context);
+ mBluetoothPan = new AtomicReference<>();
mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
mConnectivityManager =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter != null) {
+ mBluetoothAdapter.getProfileProxy(context, mBtProfileServiceListener,
+ BluetoothProfile.PAN);
+ }
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
- final Preference preference = screen.findPreference(KEY_TETHER_SETTINGS);
- if (preference != null && !mAdminDisallowedTetherConfig) {
- preference.setTitle(
+ 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.
- preference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
+ mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
}
}
@@ -70,6 +108,11 @@ public class TetherPreferenceController extends PreferenceController {
return !isBlocked;
}
+ @Override
+ public void updateState(Preference preference) {
+ updateSummary();
+ }
+
@Override
public void updateNonIndexableKeys(List keys) {
if (!mUserManager.isAdminUser() || !mConnectivityManager.isTetheringSupported()) {
@@ -81,4 +124,60 @@ public class TetherPreferenceController extends PreferenceController {
public String getPreferenceKey() {
return KEY_TETHER_SETTINGS;
}
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ 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);
+ }
+ }
}
diff --git a/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java
new file mode 100644
index 00000000000..3d6a2335172
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/TetherPreferenceControllerTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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.content.Context;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+import android.support.v7.preference.Preference;
+
+import com.android.settings.R;
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class TetherPreferenceControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private ConnectivityManager mConnectivityManager;
+ @Mock
+ private BluetoothAdapter mBluetoothAdapter;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private Preference mPreference;
+
+ private TetherPreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mController = spy(TetherPreferenceController.class);
+ ReflectionHelpers.setField(mController, "mContext", mContext);
+ ReflectionHelpers.setField(mController, "mConnectivityManager", mConnectivityManager);
+ ReflectionHelpers.setField(mController, "mBluetoothAdapter", mBluetoothAdapter);
+ ReflectionHelpers.setField(mController, "mUserManager", mUserManager);
+ }
+
+ @Test
+ public void updateSummary_noPreference_noInteractionWithConnectivityManager() {
+ mController.updateSummary();
+ verifyNoMoreInteractions(mConnectivityManager);
+ }
+
+ @Test
+ public void updateSummary_wifiTethered_shouldShowHotspotMessage() {
+ ReflectionHelpers.setField(mController, "mPreference", mPreference);
+ when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{"123"});
+ when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"123"});
+
+ mController.updateSummary();
+ verify(mPreference).setSummary(R.string.tether_settings_summary_hotspot_on_tether_off);
+ }
+
+ @Test
+ public void updateSummary_btThetherOn_shouldShowTetherMessage() {
+ ReflectionHelpers.setField(mController, "mPreference", mPreference);
+ when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{"123"});
+ when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{"123"});
+
+ mController.updateSummary();
+ verify(mPreference).setSummary(R.string.tether_settings_summary_hotspot_off_tether_on);
+ }
+
+ @Test
+ public void updateSummary_tetherOff_shouldShowTetherOffMessage() {
+ ReflectionHelpers.setField(mController, "mPreference", mPreference);
+ when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{"123"});
+ when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"456"});
+
+ mController.updateSummary();
+ verify(mPreference).setSummary(R.string.switch_off_text);
+ }
+
+ @Test
+ public void updateSummary_wifiBtTetherOn_shouldShowHotspotAndTetherMessage() {
+ ReflectionHelpers.setField(mController, "mPreference", mPreference);
+ when(mConnectivityManager.getTetheredIfaces()).thenReturn(new String[]{"123", "456"});
+ when(mConnectivityManager.getTetherableWifiRegexs()).thenReturn(new String[]{"456"});
+ when(mConnectivityManager.getTetherableBluetoothRegexs()).thenReturn(new String[]{"23"});
+
+ mController.updateSummary();
+ verify(mPreference).setSummary(R.string.tether_settings_summary_hotspot_on_tether_on);
+ }
+
+}