Notify the user and turn off tethering when the service is disallowed.
Added UserRestrinctionListener for turning the service off one the
DISALLOW_CONFIG_TETHERING is on into Tethering. Added notification about
tethering being turned off. Also added Unit Tests to test the functionality
of the UserRestrictionListener added.
Bug: 27936525
Merged-In: Ib7ea8885cedc2a842ebd4487c8b366a6666996bc
Test: Turn the tehering service on (either wifi, usb or bluetooth).
Automatically the system should send a notification about the service
being active.Close settings from recents. From TestDPC User Restrictions
switch on DISALLOW_CONFIG_TETHERING. The tethering should be turned off
and a notification should appear informing that the service is inactive.
Change-Id: Ib7ea8885cedc2a842ebd4487c8b366a6666996bc
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0514267..3ddf6e9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3380,6 +3380,13 @@
<string name="tethered_notification_title">Tethering or hotspot active</string>
<string name="tethered_notification_message">Tap to set up.</string>
+ <!-- Strings for tether disabling notification -->
+ <!-- This notification is shown when tethering has been disabled on a user's device.
+ The device is managed by the user's employer. Tethering can't be turned on unless the
+ IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
+ <string name="disable_tether_notification_title">Tethering is disabled</string>
+ <string name="disable_tether_notification_message">Contact your admin for details</string>
+
<!-- Strings for possible PreferenceActivity Back/Next buttons -->
<string name="back_button_label">Back</string>
<string name="next_button_label">Next</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7363a9b..cd6d280 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1904,6 +1904,8 @@
<java-symbol type="string" name="smv_process" />
<java-symbol type="string" name="tethered_notification_message" />
<java-symbol type="string" name="tethered_notification_title" />
+ <java-symbol type="string" name="disable_tether_notification_message" />
+ <java-symbol type="string" name="disable_tether_notification_title" />
<java-symbol type="string" name="usb_accessory_notification_title" />
<java-symbol type="string" name="usb_mtp_notification_title" />
<java-symbol type="string" name="usb_charging_notification_title" />
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3327bec..720b750 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -67,6 +67,9 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
@@ -83,6 +86,7 @@
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.server.LocalServices;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.OffloadController;
@@ -229,6 +233,12 @@
filter.addDataScheme("file");
mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
+ UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+
+ // this check is useful only for some unit tests; example: ConnectivityServiceTest
+ if (userManager != null) {
+ userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+ }
// load device config info
updateConfiguration();
}
@@ -697,6 +707,11 @@
}
private void showTetheredNotification(int icon) {
+ showTetheredNotification(icon, true);
+ }
+
+ @VisibleForTesting
+ protected void showTetheredNotification(int icon, boolean tetheringOn) {
NotificationManager notificationManager =
(NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
@@ -720,9 +735,16 @@
null, UserHandle.CURRENT);
Resources r = Resources.getSystem();
- CharSequence title = r.getText(com.android.internal.R.string.tethered_notification_title);
- CharSequence message = r.getText(com.android.internal.R.string.
- tethered_notification_message);
+ final CharSequence title;
+ final CharSequence message;
+
+ if (tetheringOn) {
+ title = r.getText(com.android.internal.R.string.tethered_notification_title);
+ message = r.getText(com.android.internal.R.string.tethered_notification_message);
+ } else {
+ title = r.getText(com.android.internal.R.string.disable_tether_notification_title);
+ message = r.getText(com.android.internal.R.string.disable_tether_notification_message);
+ }
if (mTetheredNotificationBuilder == null) {
mTetheredNotificationBuilder = new Notification.Builder(mContext);
@@ -743,7 +765,8 @@
mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL);
}
- private void clearTetheredNotification() {
+ @VisibleForTesting
+ protected void clearTetheredNotification() {
NotificationManager notificationManager =
(NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mLastNotificationId != 0) {
@@ -821,6 +844,38 @@
}
}
+ @VisibleForTesting
+ protected static class TetheringUserRestrictionListener implements UserRestrictionsListener {
+ private final Tethering mWrapper;
+
+ public TetheringUserRestrictionListener(Tethering wrapper) {
+ mWrapper = wrapper;
+ }
+
+ public void onUserRestrictionsChanged(int userId,
+ Bundle newRestrictions,
+ Bundle prevRestrictions) {
+ final boolean newlyDisallowed =
+ newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
+ final boolean previouslyDisallowed =
+ prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
+ final boolean tetheringDisallowedChanged = (newlyDisallowed != previouslyDisallowed);
+
+ if (!tetheringDisallowedChanged) {
+ return;
+ }
+
+ mWrapper.clearTetheredNotification();
+ final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
+
+ if (newlyDisallowed && isTetheringActiveOnDevice) {
+ mWrapper.showTetheredNotification(
+ com.android.internal.R.drawable.stat_sys_tether_general, false);
+ mWrapper.untetherAll();
+ }
+ }
+ }
+
private void disableWifiIpServingLocked(String ifname, int apState) {
mLog.log("Canceling WiFi tethering request - AP_STATE=" + apState);
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 5b4e901..652c54e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -35,6 +35,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -54,12 +55,14 @@
import android.net.util.SharedLog;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.test.TestLooper;
import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -479,6 +482,89 @@
verifyNoMoreInteractions(mNMService);
}
+ private void userRestrictionsListenerBehaviour(
+ boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList,
+ int expectedInteractionsWithShowNotification) throws Exception {
+ final int userId = 0;
+ final Bundle currRestrictions = new Bundle();
+ final Bundle newRestrictions = new Bundle();
+ Tethering tethering = mock(Tethering.class);
+ Tethering.TetheringUserRestrictionListener turl =
+ new Tethering.TetheringUserRestrictionListener(tethering);
+
+ currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow);
+ newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow);
+ when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList);
+
+ turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions);
+
+ verify(tethering, times(expectedInteractionsWithShowNotification))
+ .showTetheredNotification(anyInt(), eq(false));
+
+ verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll();
+ }
+
+ @Test
+ public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
+ final String[] emptyActiveIfacesList = new String[]{};
+ final boolean currDisallow = false;
+ final boolean nextDisallow = true;
+ final int expectedInteractionsWithShowNotification = 0;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+ }
+
+ @Test
+ public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
+ final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final boolean currDisallow = false;
+ final boolean nextDisallow = true;
+ final int expectedInteractionsWithShowNotification = 1;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+ }
+
+ @Test
+ public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
+ final String[] nonEmptyActiveIfacesList = new String[]{};
+ final boolean currDisallow = true;
+ final boolean nextDisallow = false;
+ final int expectedInteractionsWithShowNotification = 0;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+ }
+
+ @Test
+ public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
+ final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final boolean currDisallow = true;
+ final boolean nextDisallow = false;
+ final int expectedInteractionsWithShowNotification = 0;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+ }
+
+ @Test
+ public void testDisallowTetheringUnchanged() throws Exception {
+ final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+ final int expectedInteractionsWithShowNotification = 0;
+ boolean currDisallow = true;
+ boolean nextDisallow = true;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+
+ currDisallow = false;
+ nextDisallow = false;
+
+ userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+ expectedInteractionsWithShowNotification);
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}