Loading core/res/res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -3471,6 +3471,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> Loading core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1954,6 +1954,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="adb_debugging_notification_channel_tv" /> <java-symbol type="string" name="usb_accessory_notification_title" /> <java-symbol type="string" name="usb_mtp_notification_title" /> Loading services/core/java/com/android/server/connectivity/Tethering.java +60 −4 Original line number Diff line number Diff line Loading @@ -70,6 +70,9 @@ import android.os.Parcel; 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; Loading @@ -89,6 +92,7 @@ import com.android.internal.util.MessageUtils; 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; Loading Loading @@ -236,6 +240,13 @@ public class Tethering extends BaseNetworkObserver { filter.addDataScheme("file"); mContext.registerReceiver(mStateReceiver, filter, null, smHandler); 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(); } Loading Loading @@ -710,6 +721,11 @@ public class Tethering extends BaseNetworkObserver { } private void showTetheredNotification(int id) { showTetheredNotification(id, true); } @VisibleForTesting protected void showTetheredNotification(int id, boolean tetheringOn) { NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { Loading Loading @@ -746,9 +762,16 @@ public class Tethering extends BaseNetworkObserver { 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 = Loading @@ -770,7 +793,8 @@ public class Tethering extends BaseNetworkObserver { 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) { Loading Loading @@ -874,6 +898,38 @@ public class Tethering extends BaseNetworkObserver { } } @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); Loading tests/net/java/com/android/server/connectivity/TetheringTest.java +87 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import static org.mockito.Mockito.times; 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; Loading @@ -59,12 +60,14 @@ import android.net.NetworkRequest; 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; Loading Loading @@ -558,6 +561,90 @@ public class TetheringTest { 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. } Loading
core/res/res/values/strings.xml +7 −0 Original line number Diff line number Diff line Loading @@ -3471,6 +3471,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> Loading
core/res/res/values/symbols.xml +2 −0 Original line number Diff line number Diff line Loading @@ -1954,6 +1954,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="adb_debugging_notification_channel_tv" /> <java-symbol type="string" name="usb_accessory_notification_title" /> <java-symbol type="string" name="usb_mtp_notification_title" /> Loading
services/core/java/com/android/server/connectivity/Tethering.java +60 −4 Original line number Diff line number Diff line Loading @@ -70,6 +70,9 @@ import android.os.Parcel; 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; Loading @@ -89,6 +92,7 @@ import com.android.internal.util.MessageUtils; 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; Loading Loading @@ -236,6 +240,13 @@ public class Tethering extends BaseNetworkObserver { filter.addDataScheme("file"); mContext.registerReceiver(mStateReceiver, filter, null, smHandler); 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(); } Loading Loading @@ -710,6 +721,11 @@ public class Tethering extends BaseNetworkObserver { } private void showTetheredNotification(int id) { showTetheredNotification(id, true); } @VisibleForTesting protected void showTetheredNotification(int id, boolean tetheringOn) { NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) { Loading Loading @@ -746,9 +762,16 @@ public class Tethering extends BaseNetworkObserver { 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 = Loading @@ -770,7 +793,8 @@ public class Tethering extends BaseNetworkObserver { 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) { Loading Loading @@ -874,6 +898,38 @@ public class Tethering extends BaseNetworkObserver { } } @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); Loading
tests/net/java/com/android/server/connectivity/TetheringTest.java +87 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,7 @@ import static org.mockito.Mockito.times; 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; Loading @@ -59,12 +60,14 @@ import android.net.NetworkRequest; 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; Loading Loading @@ -558,6 +561,90 @@ public class TetheringTest { 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. }