diff --git a/res/values/strings.xml b/res/values/strings.xml
index e4406f33f6c..6bab5949026 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/WirelessSettings.java b/src/com/android/settings/WirelessSettings.java
index 910e1eb092b..253ed7652d3 100644
--- a/src/com/android/settings/WirelessSettings.java
+++ b/src/com/android/settings/WirelessSettings.java
@@ -253,7 +253,8 @@ public class WirelessSettings extends SettingsPreferenceFragment implements Inde
new NfcPreferenceController(context).updateNonIndexableKeys(result);
new MobilePlanPreferenceController(context, null /* MobilePlanClickHandler */)
.updateNonIndexableKeys(result);
- new MobileNetworkPreferenceController(context).updateNonIndexableKeys(result);
+ new MobileNetworkPreferenceController(context)
+ .updateNonIndexableKeys(result);
// Remove Airplane Mode settings if it's a stationary device such as a TV.
new AirplaneModePreferenceController(context, null /* fragment */)
diff --git a/src/com/android/settings/network/MobileNetworkPreferenceController.java b/src/com/android/settings/network/MobileNetworkPreferenceController.java
index 7c8cd405459..c7abf905cd9 100644
--- a/src/com/android/settings/network/MobileNetworkPreferenceController.java
+++ b/src/com/android/settings/network/MobileNetworkPreferenceController.java
@@ -17,25 +17,39 @@ package com.android.settings.network;
import android.content.Context;
import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
import static android.os.UserHandle.myUserId;
import static android.os.UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS;
import static com.android.settingslib.RestrictedLockUtils.hasBaseUserRestriction;
-public class MobileNetworkPreferenceController extends PreferenceController {
+public class MobileNetworkPreferenceController extends PreferenceController implements
+ LifecycleObserver, OnResume, OnPause {
private static final String KEY_MOBILE_NETWORK_SETTINGS = "mobile_network_settings";
private final UserManager mUserManager;
private final boolean mIsSecondaryUser;
+ private final TelephonyManager mTelephonyManager;
+ private Preference mPreference;
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ PhoneStateListener mPhoneStateListener;
public MobileNetworkPreferenceController(Context context) {
super(context);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mIsSecondaryUser = !mUserManager.isAdminUser();
}
@@ -46,8 +60,40 @@ public class MobileNetworkPreferenceController extends PreferenceController {
&& !hasBaseUserRestriction(mContext, DISALLOW_CONFIG_MOBILE_NETWORKS, myUserId());
}
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ if (isAvailable()) {
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+ }
+
@Override
public String getPreferenceKey() {
return KEY_MOBILE_NETWORK_SETTINGS;
}
+
+ @Override
+ public void onResume() {
+ if (isAvailable()) {
+ if (mPhoneStateListener == null) {
+ mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ if (mPreference != null) {
+ mPreference.setSummary(mTelephonyManager.getNetworkOperatorName());
+ }
+ }
+ };
+ }
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+ }
+
+ @Override
+ public void onPause() {
+ if (mPhoneStateListener != null) {
+ mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
+ }
+ }
}
diff --git a/src/com/android/settings/network/NetworkDashboardFragment.java b/src/com/android/settings/network/NetworkDashboardFragment.java
index d2b0621446a..f52230b61d0 100644
--- a/src/com/android/settings/network/NetworkDashboardFragment.java
+++ b/src/com/android/settings/network/NetworkDashboardFragment.java
@@ -81,16 +81,22 @@ public class NetworkDashboardFragment extends DashboardFragment implements
new MobilePlanPreferenceController(context, this);
final WifiMasterSwitchPreferenceController wifiPreferenceController =
new WifiMasterSwitchPreferenceController(context, mMetricsFeatureProvider);
+ final MobileNetworkPreferenceController mobileNetworkPreferenceController =
+ new MobileNetworkPreferenceController(context);
+ final VpnPreferenceController vpnPreferenceController =
+ new VpnPreferenceController(context);
final Lifecycle lifecycle = getLifecycle();
lifecycle.addObserver(airplaneModePreferenceController);
lifecycle.addObserver(mobilePlanPreferenceController);
lifecycle.addObserver(wifiPreferenceController);
+ lifecycle.addObserver(mobileNetworkPreferenceController);
+ lifecycle.addObserver(vpnPreferenceController);
final List controllers = new ArrayList<>();
controllers.add(airplaneModePreferenceController);
- controllers.add(new MobileNetworkPreferenceController(context));
+ controllers.add(mobileNetworkPreferenceController);
controllers.add(new TetherPreferenceController(context));
- controllers.add(new VpnPreferenceController(context));
+ controllers.add(vpnPreferenceController);
controllers.add(new ProxyPreferenceController(context));
controllers.add(mobilePlanPreferenceController);
controllers.add(wifiPreferenceController);
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/src/com/android/settings/network/VpnPreferenceController.java b/src/com/android/settings/network/VpnPreferenceController.java
index f7e230ffd05..86ff175412d 100644
--- a/src/com/android/settings/network/VpnPreferenceController.java
+++ b/src/com/android/settings/network/VpnPreferenceController.java
@@ -16,38 +16,74 @@
package com.android.settings.network;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
+import android.util.Log;
+import android.util.SparseArray;
+import com.android.internal.net.LegacyVpnInfo;
+import com.android.internal.net.VpnConfig;
+import com.android.settings.R;
import com.android.settings.core.PreferenceController;
+import com.android.settings.core.lifecycle.LifecycleObserver;
+import com.android.settings.core.lifecycle.events.OnPause;
+import com.android.settings.core.lifecycle.events.OnResume;
import com.android.settingslib.RestrictedLockUtils;
+import java.util.List;
-public class VpnPreferenceController extends PreferenceController {
+
+public class VpnPreferenceController extends PreferenceController implements LifecycleObserver,
+ OnResume, OnPause {
private static final String KEY_VPN_SETTINGS = "vpn_settings";
+ private static final NetworkRequest REQUEST = new NetworkRequest.Builder()
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .build();
+ private static final String TAG = "VpnPreferenceController";
private final String mToggleable;
- private final boolean mIsSecondaryUser;
+ private final UserManager mUserManager;
+ private final ConnectivityManager mConnectivityManager;
+ private final IConnectivityManager mConnectivityManagerService;
+ private Preference mPreference;
public VpnPreferenceController(Context context) {
super(context);
mToggleable = Settings.Global.getString(context.getContentResolver(),
Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
- mIsSecondaryUser = !UserManager.get(context).isAdminUser();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ mConnectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mConnectivityManagerService = IConnectivityManager.Stub.asInterface(
+ ServiceManager.getService(Context.CONNECTIVITY_SERVICE));
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
+ mPreference = screen.findPreference(KEY_VPN_SETTINGS);
// Manually set dependencies for Wifi when not toggleable.
if (mToggleable == null || !mToggleable.contains(Settings.Global.RADIO_WIFI)) {
- final Preference pref = screen.findPreference(KEY_VPN_SETTINGS);
- if (pref != null) {
- pref.setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
+ if (mPreference != null) {
+ mPreference.setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
}
}
}
@@ -62,4 +98,96 @@ public class VpnPreferenceController extends PreferenceController {
public String getPreferenceKey() {
return KEY_VPN_SETTINGS;
}
+
+ @Override
+ public void onPause() {
+ if (isAvailable()) {
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ if (isAvailable()) {
+ mConnectivityManager.registerNetworkCallback(REQUEST, mNetworkCallback);
+ }
+ }
+
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ void updateSummary() {
+ if (mPreference == null) {
+ return;
+ }
+ // Copied from SystemUI::SecurityControllerImpl
+ SparseArray vpns = new SparseArray<>();
+ try {
+ final List users = mUserManager.getUsers();
+ for (UserInfo user : users) {
+ VpnConfig cfg = mConnectivityManagerService.getVpnConfig(user.id);
+ if (cfg == null) {
+ continue;
+ } else if (cfg.legacy) {
+ // Legacy VPNs should do nothing if the network is disconnected. Third-party
+ // VPN warnings need to continue as traffic can still go to the app.
+ final LegacyVpnInfo legacyVpn =
+ mConnectivityManagerService.getLegacyVpnInfo(user.id);
+ if (legacyVpn == null || legacyVpn.state != LegacyVpnInfo.STATE_CONNECTED) {
+ continue;
+ }
+ }
+ vpns.put(user.id, cfg);
+ }
+ } catch (RemoteException rme) {
+ // Roll back to previous state
+ Log.e(TAG, "Unable to list active VPNs", rme);
+ return;
+ }
+ final UserInfo userInfo = mUserManager.getUserInfo(UserHandle.myUserId());
+ final int uid;
+ if (userInfo.isRestricted()) {
+ uid = userInfo.restrictedProfileParentId;
+ } else {
+ uid = userInfo.id;
+ }
+ VpnConfig vpn = vpns.get(uid);
+ final String vpnName;
+ if (vpn == null) {
+ vpnName = null;
+ } else {
+ vpnName = getNameForVpnConfig(vpn, UserHandle.of(uid));
+ }
+ new Handler(Looper.getMainLooper()).post(() -> mPreference.setSummary(vpnName));
+ }
+
+ private String getNameForVpnConfig(VpnConfig cfg, UserHandle user) {
+ if (cfg.legacy) {
+ return mContext.getString(R.string.bluetooth_connected);
+ }
+ // The package name for an active VPN is stored in the 'user' field of its VpnConfig
+ final String vpnPackage = cfg.user;
+ try {
+ Context userContext = mContext.createPackageContextAsUser(mContext.getPackageName(),
+ 0 /* flags */, user);
+ return VpnConfig.getVpnLabel(userContext, vpnPackage).toString();
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ Log.e(TAG, "Package " + vpnPackage + " is not present", nnfe);
+ return null;
+ }
+ }
+
+ // Copied from SystemUI::SecurityControllerImpl
+ private final ConnectivityManager.NetworkCallback
+ mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ Log.d(TAG, "onAvailable " + network.netId);
+ updateSummary();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ Log.d(TAG, "onLost " + network.netId);
+ updateSummary();
+ }
+ };
}
diff --git a/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
index d3d6836d180..1ae40aa1294 100644
--- a/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/MobileNetworkPreferenceControllerTest.java
@@ -19,8 +19,15 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
import com.android.settings.SettingsRobolectricTestRunner;
import com.android.settings.TestConfig;
+import com.android.settings.core.lifecycle.Lifecycle;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +39,10 @@ import org.robolectric.annotation.Config;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(SettingsRobolectricTestRunner.class)
@@ -44,15 +55,23 @@ public class MobileNetworkPreferenceControllerTest {
private UserManager mUserManager;
@Mock
private ConnectivityManager mConnectivityManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private PreferenceScreen mScreen;
+ private Lifecycle mLifecycle;
private MobileNetworkPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ mLifecycle = new Lifecycle();
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
.thenReturn(mConnectivityManager);
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .thenReturn(mTelephonyManager);
}
@Test
@@ -78,4 +97,45 @@ public class MobileNetworkPreferenceControllerTest {
mController = new MobileNetworkPreferenceController(mContext);
assertThat(mController.isAvailable()).isFalse();
}
+
+ @Test
+ public void goThroughLifecycle_isAvailable_shouldListenToServiceChange() {
+ mController = spy(new MobileNetworkPreferenceController(mContext));
+ mLifecycle.addObserver(mController);
+ doReturn(true).when(mController).isAvailable();
+
+ mLifecycle.onResume();
+ verify(mTelephonyManager).listen(mController.mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ mLifecycle.onPause();
+ verify(mTelephonyManager).listen(mController.mPhoneStateListener,
+ PhoneStateListener.LISTEN_NONE);
+ }
+
+ @Test
+ public void serviceStateChange_shouldUpdatePrefSummary() {
+ final String testCarrierName = "test";
+ final Preference mPreference = mock(Preference.class);
+ mController = spy(new MobileNetworkPreferenceController(mContext));
+ mLifecycle.addObserver(mController);
+
+ when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
+ doReturn(true).when(mController).isAvailable();
+
+ // Display pref and go through lifecycle to set up listener.
+ mController.displayPreference(mScreen);
+ mLifecycle.onResume();
+ verify(mController).onResume();
+ verify(mTelephonyManager).listen(mController.mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE);
+
+ // Trigger listener update
+ when(mTelephonyManager.getNetworkOperatorName()).thenReturn(testCarrierName);
+ mController.mPhoneStateListener.onServiceStateChanged(null);
+
+ // Carrier name should be set.
+ verify(mPreference).setSummary(testCarrierName);
+ }
+
}
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);
+ }
+
+}
diff --git a/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
new file mode 100644
index 00000000000..2a0b873a726
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/VpnPreferenceControllerTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
+import android.net.NetworkRequest;
+import android.os.IBinder;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settings.SettingsRobolectricTestRunner;
+import com.android.settings.TestConfig;
+import com.android.settings.core.lifecycle.Lifecycle;
+
+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.shadows.ShadowServiceManager;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class VpnPreferenceControllerTest {
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private ConnectivityManager mConnectivityManager;
+ @Mock
+ private IBinder mBinder;
+ @Mock
+ private IConnectivityManager mConnectivityManagerService;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+ private VpnPreferenceController mController;
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
+ .thenReturn(mConnectivityManager);
+ when(mBinder.queryLocalInterface("android.net.IConnectivityManager"))
+ .thenReturn(mConnectivityManagerService);
+ ShadowServiceManager.addService(Context.CONNECTIVITY_SERVICE, mBinder);
+ when(mScreen.findPreference(anyString())).thenReturn(mPreference);
+
+ mController = spy(new VpnPreferenceController(mContext));
+ mLifecycle = new Lifecycle();
+ mLifecycle.addObserver(mController);
+ }
+
+ @Test
+ public void displayPreference_available_shouldSetDependency() {
+
+ doReturn(true).when(mController).isAvailable();
+ mController.displayPreference(mScreen);
+
+ verify(mPreference).setDependency(AirplaneModePreferenceController.KEY_TOGGLE_AIRPLANE);
+ }
+
+ @Test
+ public void goThroughLifecycle_shouldRegisterUnregisterListener() {
+ doReturn(true).when(mController).isAvailable();
+
+ mLifecycle.onResume();
+ verify(mConnectivityManager).registerNetworkCallback(
+ any(NetworkRequest.class), any(ConnectivityManager.NetworkCallback.class));
+
+ mLifecycle.onPause();
+ verify(mConnectivityManager).unregisterNetworkCallback(
+ any(ConnectivityManager.NetworkCallback.class));
+ }
+
+}