Loading packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +20 −8 Original line number Diff line number Diff line Loading @@ -303,7 +303,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); mTetheringRestriction = new UserRestrictionActionListener( userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); Loading Loading @@ -997,11 +998,14 @@ public class Tethering { protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; public UserRestrictionActionListener(UserManager um, Tethering wrapper) { public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, @NonNull TetheringNotificationUpdater updater) { mUserManager = um; mWrapper = wrapper; mNotificationUpdater = updater; mDisallowTethering = false; } Loading @@ -1020,15 +1024,23 @@ public class Tethering { return; } // TODO: Add user restrictions notification. final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); if (!newlyDisallowed) { // Clear the restricted notification when user is allowed to have tethering // function. mNotificationUpdater.tetheringRestrictionLifted(); return; } // Restricted notification is shown when tethering function is disallowed on // user's device. mNotificationUpdater.notifyTetheringDisabledByRestriction(); if (newlyDisallowed && isTetheringActiveOnDevice) { // Untether from all downstreams since tethering is disallowed. mWrapper.untetherAll(); // TODO(b/148139325): send tetheringSupported on restriction change } } } private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) { mLog.log("Canceling WiFi tethering request -" Loading packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +31 −10 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.util.SparseArray; import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; Loading @@ -61,7 +62,9 @@ public class TetheringNotificationUpdater { private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. private static final int NOTIFY_ID = 20191115; private static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. private static final int RESTRICTED_NOTIFICATION_ID = 1001; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting Loading @@ -85,6 +88,9 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) @interface NotificationId {} public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) Loading @@ -100,14 +106,14 @@ public class TetheringNotificationUpdater { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; updateNotification(); updateEnableNotification(); } /** Called when active data subscription id changed */ public void onActiveDataSubscriptionIdChanged(final int subId) { if (mActiveDataSubId == subId) return; mActiveDataSubId = subId; updateNotification(); updateEnableNotification(); } @VisibleForTesting Loading @@ -115,16 +121,31 @@ public class TetheringNotificationUpdater { return SubscriptionManager.getResourcesForSubId(c, subId); } private void updateNotification() { private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { clearNotification(); clearNotification(ENABLE_NOTIFICATION_ID); } } private void clearNotification() { mNotificationManager.cancel(null /* tag */, NOTIFY_ID); @VisibleForTesting void tetheringRestrictionLifted() { clearNotification(RESTRICTED_NOTIFICATION_ID); } private void clearNotification(@NotificationId final int id) { mNotificationManager.cancel(null /* tag */, id); } @VisibleForTesting void notifyTetheringDisabledByRestriction() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final String title = res.getString(R.string.disable_tether_notification_title); final String message = res.getString(R.string.disable_tether_notification_message); showNotification(R.drawable.stat_sys_tether_general, title, message, RESTRICTED_NOTIFICATION_ID); } /** Loading Loading @@ -195,12 +216,12 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); showNotification(iconId, title, message); showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, @NonNull final String message) { @NonNull final String message, @NotificationId final int id) { final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0), Loading @@ -218,6 +239,6 @@ public class TetheringNotificationUpdater { .setContentIntent(pi) .build(); mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); mNotificationManager.notify(null /* tag */, id, notification); } } packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt +38 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.test.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext Loading Loading @@ -114,7 +114,7 @@ class TetheringNotificationUpdaterTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) val context = TestContext(InstrumentationRegistry.getContext()) val context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) notificationUpdater = WrappedNotificationUpdater(context) Loading @@ -128,7 +128,8 @@ class TetheringNotificationUpdaterTest { verify(notificationManager, never()).cancel(any(), anyInt()) val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) verify(notificationManager, times(1)).notify(any(), anyInt(), notificationCaptor.capture()) verify(notificationManager, times(1)) .notify(any(), anyInt(), notificationCaptor.capture()) val notification = notificationCaptor.getValue() assertEquals(iconId, notification.smallIcon.resId) Loading Loading @@ -224,4 +225,38 @@ class TetheringNotificationUpdaterTest { assertEquals(WIFI_MASK or USB_MASK, notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) } @Test fun testSetupRestrictedNotification() { val title = InstrumentationRegistry.getInstrumentation().context.resources .getString(R.string.disable_tether_notification_title) val message = InstrumentationRegistry.getInstrumentation().context.resources .getString(R.string.disable_tether_notification_message) val disallowTitle = "Tether function is disallowed" val disallowMessage = "Please contact your admin" doReturn(title).`when`(defaultResources) .getString(R.string.disable_tether_notification_title) doReturn(message).`when`(defaultResources) .getString(R.string.disable_tether_notification_message) doReturn(disallowTitle).`when`(testResources) .getString(R.string.disable_tether_notification_title) doReturn(disallowMessage).`when`(testResources) .getString(R.string.disable_tether_notification_message) // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, title, message) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() verifyNoNotification() // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) verifyNoNotification() // User restrictions on again. Show restricted notification with test resource. notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) } } No newline at end of file packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +6 −4 Original line number Diff line number Diff line Loading @@ -1072,13 +1072,15 @@ public class TetheringTest { when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); new Tethering.UserRestrictionActionListener( mUserManager, mockTethering, mNotificationUpdater); ural.mDisallowTethering = currentDisallow; ural.onUserRestrictionsChanged(); verify(mockTethering, times(expectedInteractionsWithShowNotification)) .untetherAll(); verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) .notifyTetheringDisabledByRestriction(); verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); } @Test Loading @@ -1086,7 +1088,7 @@ public class TetheringTest { final String[] emptyActiveIfacesList = new String[]{}; final boolean currDisallow = false; final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 0; final int expectedInteractionsWithShowNotification = 1; runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); Loading Loading
packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +20 −8 Original line number Diff line number Diff line Loading @@ -303,7 +303,8 @@ public class Tethering { final UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); mTetheringRestriction = new UserRestrictionActionListener(userManager, this); mTetheringRestriction = new UserRestrictionActionListener( userManager, this, mNotificationUpdater); mExecutor = new TetheringThreadExecutor(mHandler); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); Loading Loading @@ -997,11 +998,14 @@ public class Tethering { protected static class UserRestrictionActionListener { private final UserManager mUserManager; private final Tethering mWrapper; private final TetheringNotificationUpdater mNotificationUpdater; public boolean mDisallowTethering; public UserRestrictionActionListener(UserManager um, Tethering wrapper) { public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper, @NonNull TetheringNotificationUpdater updater) { mUserManager = um; mWrapper = wrapper; mNotificationUpdater = updater; mDisallowTethering = false; } Loading @@ -1020,15 +1024,23 @@ public class Tethering { return; } // TODO: Add user restrictions notification. final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); if (!newlyDisallowed) { // Clear the restricted notification when user is allowed to have tethering // function. mNotificationUpdater.tetheringRestrictionLifted(); return; } // Restricted notification is shown when tethering function is disallowed on // user's device. mNotificationUpdater.notifyTetheringDisabledByRestriction(); if (newlyDisallowed && isTetheringActiveOnDevice) { // Untether from all downstreams since tethering is disallowed. mWrapper.untetherAll(); // TODO(b/148139325): send tetheringSupported on restriction change } } } private void disableWifiIpServingLockedCommon(int tetheringType, String ifname, int apState) { mLog.log("Canceling WiFi tethering request -" Loading
packages/Tethering/src/com/android/server/connectivity/tethering/TetheringNotificationUpdater.java +31 −10 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.util.SparseArray; import androidx.annotation.ArrayRes; import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.IntRange; import androidx.annotation.NonNull; Loading @@ -61,7 +62,9 @@ public class TetheringNotificationUpdater { private static final boolean NOTIFY_DONE = true; private static final boolean NO_NOTIFY = false; // Id to update and cancel tethering notification. Must be unique within the tethering app. private static final int NOTIFY_ID = 20191115; private static final int ENABLE_NOTIFICATION_ID = 1000; // Id to update and cancel restricted notification. Must be unique within the tethering app. private static final int RESTRICTED_NOTIFICATION_ID = 1001; @VisibleForTesting static final int NO_ICON_ID = 0; @VisibleForTesting Loading @@ -85,6 +88,9 @@ public class TetheringNotificationUpdater { // INVALID_SUBSCRIPTION_ID. private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID}) @interface NotificationId {} public TetheringNotificationUpdater(@NonNull final Context context) { mContext = context; mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) Loading @@ -100,14 +106,14 @@ public class TetheringNotificationUpdater { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { if (mDownstreamTypesMask == downstreamTypesMask) return; mDownstreamTypesMask = downstreamTypesMask; updateNotification(); updateEnableNotification(); } /** Called when active data subscription id changed */ public void onActiveDataSubscriptionIdChanged(final int subId) { if (mActiveDataSubId == subId) return; mActiveDataSubId = subId; updateNotification(); updateEnableNotification(); } @VisibleForTesting Loading @@ -115,16 +121,31 @@ public class TetheringNotificationUpdater { return SubscriptionManager.getResourcesForSubId(c, subId); } private void updateNotification() { private void updateEnableNotification() { final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; if (tetheringInactive || setupNotification() == NO_NOTIFY) { clearNotification(); clearNotification(ENABLE_NOTIFICATION_ID); } } private void clearNotification() { mNotificationManager.cancel(null /* tag */, NOTIFY_ID); @VisibleForTesting void tetheringRestrictionLifted() { clearNotification(RESTRICTED_NOTIFICATION_ID); } private void clearNotification(@NotificationId final int id) { mNotificationManager.cancel(null /* tag */, id); } @VisibleForTesting void notifyTetheringDisabledByRestriction() { final Resources res = getResourcesForSubId(mContext, mActiveDataSubId); final String title = res.getString(R.string.disable_tether_notification_title); final String message = res.getString(R.string.disable_tether_notification_message); showNotification(R.drawable.stat_sys_tether_general, title, message, RESTRICTED_NOTIFICATION_ID); } /** Loading Loading @@ -195,12 +216,12 @@ public class TetheringNotificationUpdater { final String title = res.getString(R.string.tethering_notification_title); final String message = res.getString(R.string.tethering_notification_message); showNotification(iconId, title, message); showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID); return NOTIFY_DONE; } private void showNotification(@DrawableRes final int iconId, @NonNull final String title, @NonNull final String message) { @NonNull final String message, @NotificationId final int id) { final Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS); final PendingIntent pi = PendingIntent.getActivity( mContext.createContextAsUser(UserHandle.CURRENT, 0), Loading @@ -218,6 +239,6 @@ public class TetheringNotificationUpdater { .setContentIntent(pi) .build(); mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); mNotificationManager.notify(null /* tag */, id, notification); } }
packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringNotificationUpdaterTest.kt +38 −3 Original line number Diff line number Diff line Loading @@ -25,7 +25,7 @@ import android.net.ConnectivityManager.TETHERING_USB import android.net.ConnectivityManager.TETHERING_WIFI import android.os.UserHandle import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import androidx.test.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import com.android.internal.util.test.BroadcastInterceptingContext Loading Loading @@ -114,7 +114,7 @@ class TetheringNotificationUpdaterTest { @Before fun setUp() { MockitoAnnotations.initMocks(this) val context = TestContext(InstrumentationRegistry.getContext()) val context = TestContext(InstrumentationRegistry.getInstrumentation().context) doReturn(notificationManager).`when`(mockContext) .getSystemService(Context.NOTIFICATION_SERVICE) notificationUpdater = WrappedNotificationUpdater(context) Loading @@ -128,7 +128,8 @@ class TetheringNotificationUpdaterTest { verify(notificationManager, never()).cancel(any(), anyInt()) val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) verify(notificationManager, times(1)).notify(any(), anyInt(), notificationCaptor.capture()) verify(notificationManager, times(1)) .notify(any(), anyInt(), notificationCaptor.capture()) val notification = notificationCaptor.getValue() assertEquals(iconId, notification.smallIcon.resId) Loading Loading @@ -224,4 +225,38 @@ class TetheringNotificationUpdaterTest { assertEquals(WIFI_MASK or USB_MASK, notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) } @Test fun testSetupRestrictedNotification() { val title = InstrumentationRegistry.getInstrumentation().context.resources .getString(R.string.disable_tether_notification_title) val message = InstrumentationRegistry.getInstrumentation().context.resources .getString(R.string.disable_tether_notification_message) val disallowTitle = "Tether function is disallowed" val disallowMessage = "Please contact your admin" doReturn(title).`when`(defaultResources) .getString(R.string.disable_tether_notification_title) doReturn(message).`when`(defaultResources) .getString(R.string.disable_tether_notification_message) doReturn(disallowTitle).`when`(testResources) .getString(R.string.disable_tether_notification_title) doReturn(disallowMessage).`when`(testResources) .getString(R.string.disable_tether_notification_message) // User restrictions on. Show restricted notification. notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, title, message) // User restrictions off. Clear notification. notificationUpdater.tetheringRestrictionLifted() verifyNoNotification() // Set test sub id. No notification. notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID) verifyNoNotification() // User restrictions on again. Show restricted notification with test resource. notificationUpdater.notifyTetheringDisabledByRestriction() verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage) } } No newline at end of file
packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +6 −4 Original line number Diff line number Diff line Loading @@ -1072,13 +1072,15 @@ public class TetheringTest { when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); final Tethering.UserRestrictionActionListener ural = new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); new Tethering.UserRestrictionActionListener( mUserManager, mockTethering, mNotificationUpdater); ural.mDisallowTethering = currentDisallow; ural.onUserRestrictionsChanged(); verify(mockTethering, times(expectedInteractionsWithShowNotification)) .untetherAll(); verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification)) .notifyTetheringDisabledByRestriction(); verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll(); } @Test Loading @@ -1086,7 +1088,7 @@ public class TetheringTest { final String[] emptyActiveIfacesList = new String[]{}; final boolean currDisallow = false; final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 0; final int expectedInteractionsWithShowNotification = 1; runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, expectedInteractionsWithShowNotification); Loading