Merge "[TNU04] Add tethering restricted notification"

This commit is contained in:
Paul Hu
2020-03-25 11:30:41 +00:00
committed by Gerrit Code Review
4 changed files with 95 additions and 25 deletions

View File

@@ -303,7 +303,8 @@ public class Tethering {
final UserManager userManager = (UserManager) mContext.getSystemService( final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE); Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(userManager, this); mTetheringRestriction = new UserRestrictionActionListener(
userManager, this, mNotificationUpdater);
mExecutor = new TetheringThreadExecutor(mHandler); mExecutor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor); mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
@@ -1000,11 +1001,14 @@ public class Tethering {
protected static class UserRestrictionActionListener { protected static class UserRestrictionActionListener {
private final UserManager mUserManager; private final UserManager mUserManager;
private final Tethering mWrapper; private final Tethering mWrapper;
private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering; public boolean mDisallowTethering;
public UserRestrictionActionListener(UserManager um, Tethering wrapper) { public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
@NonNull TetheringNotificationUpdater updater) {
mUserManager = um; mUserManager = um;
mWrapper = wrapper; mWrapper = wrapper;
mNotificationUpdater = updater;
mDisallowTethering = false; mDisallowTethering = false;
} }
@@ -1023,13 +1027,21 @@ public class Tethering {
return; return;
} }
// TODO: Add user restrictions notification. if (!newlyDisallowed) {
final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0); // Clear the restricted notification when user is allowed to have tethering
// function.
if (newlyDisallowed && isTetheringActiveOnDevice) { mNotificationUpdater.tetheringRestrictionLifted();
mWrapper.untetherAll(); return;
// TODO(b/148139325): send tetheringSupported on restriction change
} }
// Restricted notification is shown when tethering function is disallowed on
// user's device.
mNotificationUpdater.notifyTetheringDisabledByRestriction();
// Untether from all downstreams since tethering is disallowed.
mWrapper.untetherAll();
// TODO(b/148139325): send tetheringSupported on restriction change
} }
} }

View File

@@ -36,6 +36,7 @@ import android.util.SparseArray;
import androidx.annotation.ArrayRes; import androidx.annotation.ArrayRes;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange; import androidx.annotation.IntRange;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -61,7 +62,9 @@ public class TetheringNotificationUpdater {
private static final boolean NOTIFY_DONE = true; private static final boolean NOTIFY_DONE = true;
private static final boolean NO_NOTIFY = false; private static final boolean NO_NOTIFY = false;
// Id to update and cancel tethering notification. Must be unique within the tethering app. // 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 @VisibleForTesting
static final int NO_ICON_ID = 0; static final int NO_ICON_ID = 0;
@VisibleForTesting @VisibleForTesting
@@ -85,6 +88,9 @@ public class TetheringNotificationUpdater {
// INVALID_SUBSCRIPTION_ID. // INVALID_SUBSCRIPTION_ID.
private volatile int mActiveDataSubId = SubscriptionManager.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) { public TetheringNotificationUpdater(@NonNull final Context context) {
mContext = context; mContext = context;
mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0) mNotificationManager = (NotificationManager) context.createContextAsUser(UserHandle.ALL, 0)
@@ -100,14 +106,14 @@ public class TetheringNotificationUpdater {
public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) { public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
if (mDownstreamTypesMask == downstreamTypesMask) return; if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask; mDownstreamTypesMask = downstreamTypesMask;
updateNotification(); updateEnableNotification();
} }
/** Called when active data subscription id changed */ /** Called when active data subscription id changed */
public void onActiveDataSubscriptionIdChanged(final int subId) { public void onActiveDataSubscriptionIdChanged(final int subId) {
if (mActiveDataSubId == subId) return; if (mActiveDataSubId == subId) return;
mActiveDataSubId = subId; mActiveDataSubId = subId;
updateNotification(); updateEnableNotification();
} }
@VisibleForTesting @VisibleForTesting
@@ -115,16 +121,31 @@ public class TetheringNotificationUpdater {
return SubscriptionManager.getResourcesForSubId(c, subId); return SubscriptionManager.getResourcesForSubId(c, subId);
} }
private void updateNotification() { private void updateEnableNotification() {
final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE; final boolean tetheringInactive = mDownstreamTypesMask <= DOWNSTREAM_NONE;
if (tetheringInactive || setupNotification() == NO_NOTIFY) { if (tetheringInactive || setupNotification() == NO_NOTIFY) {
clearNotification(); clearNotification(ENABLE_NOTIFICATION_ID);
} }
} }
private void clearNotification() { @VisibleForTesting
mNotificationManager.cancel(null /* tag */, NOTIFY_ID); 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);
} }
/** /**
@@ -195,12 +216,12 @@ public class TetheringNotificationUpdater {
final String title = res.getString(R.string.tethering_notification_title); final String title = res.getString(R.string.tethering_notification_title);
final String message = res.getString(R.string.tethering_notification_message); final String message = res.getString(R.string.tethering_notification_message);
showNotification(iconId, title, message); showNotification(iconId, title, message, ENABLE_NOTIFICATION_ID);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
private void showNotification(@DrawableRes final int iconId, @NonNull final String title, 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 Intent intent = new Intent(Settings.ACTION_TETHER_SETTINGS);
final PendingIntent pi = PendingIntent.getActivity( final PendingIntent pi = PendingIntent.getActivity(
mContext.createContextAsUser(UserHandle.CURRENT, 0), mContext.createContextAsUser(UserHandle.CURRENT, 0),
@@ -218,6 +239,6 @@ public class TetheringNotificationUpdater {
.setContentIntent(pi) .setContentIntent(pi)
.build(); .build();
mNotificationManager.notify(null /* tag */, NOTIFY_ID, notification); mNotificationManager.notify(null /* tag */, id, notification);
} }
} }

View File

@@ -25,7 +25,7 @@ import android.net.ConnectivityManager.TETHERING_USB
import android.net.ConnectivityManager.TETHERING_WIFI import android.net.ConnectivityManager.TETHERING_WIFI
import android.os.UserHandle import android.os.UserHandle
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID 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.filters.SmallTest
import androidx.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import com.android.internal.util.test.BroadcastInterceptingContext import com.android.internal.util.test.BroadcastInterceptingContext
@@ -114,7 +114,7 @@ class TetheringNotificationUpdaterTest {
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
val context = TestContext(InstrumentationRegistry.getContext()) val context = TestContext(InstrumentationRegistry.getInstrumentation().context)
doReturn(notificationManager).`when`(mockContext) doReturn(notificationManager).`when`(mockContext)
.getSystemService(Context.NOTIFICATION_SERVICE) .getSystemService(Context.NOTIFICATION_SERVICE)
notificationUpdater = WrappedNotificationUpdater(context) notificationUpdater = WrappedNotificationUpdater(context)
@@ -128,7 +128,8 @@ class TetheringNotificationUpdaterTest {
verify(notificationManager, never()).cancel(any(), anyInt()) verify(notificationManager, never()).cancel(any(), anyInt())
val notificationCaptor = ArgumentCaptor.forClass(Notification::class.java) 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() val notification = notificationCaptor.getValue()
assertEquals(iconId, notification.smallIcon.resId) assertEquals(iconId, notification.smallIcon.resId)
@@ -224,4 +225,38 @@ class TetheringNotificationUpdaterTest {
assertEquals(WIFI_MASK or USB_MASK, assertEquals(WIFI_MASK or USB_MASK,
notificationUpdater.getDownstreamTypesMask("1|2|USB|WIFI|BLUETOOTH||")) 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)
}
} }

View File

@@ -1072,13 +1072,15 @@ public class TetheringTest {
when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions); when(mUserManager.getUserRestrictions()).thenReturn(newRestrictions);
final Tethering.UserRestrictionActionListener ural = final Tethering.UserRestrictionActionListener ural =
new Tethering.UserRestrictionActionListener(mUserManager, mockTethering); new Tethering.UserRestrictionActionListener(
mUserManager, mockTethering, mNotificationUpdater);
ural.mDisallowTethering = currentDisallow; ural.mDisallowTethering = currentDisallow;
ural.onUserRestrictionsChanged(); ural.onUserRestrictionsChanged();
verify(mockTethering, times(expectedInteractionsWithShowNotification)) verify(mNotificationUpdater, times(expectedInteractionsWithShowNotification))
.untetherAll(); .notifyTetheringDisabledByRestriction();
verify(mockTethering, times(expectedInteractionsWithShowNotification)).untetherAll();
} }
@Test @Test
@@ -1086,7 +1088,7 @@ public class TetheringTest {
final String[] emptyActiveIfacesList = new String[]{}; final String[] emptyActiveIfacesList = new String[]{};
final boolean currDisallow = false; final boolean currDisallow = false;
final boolean nextDisallow = true; final boolean nextDisallow = true;
final int expectedInteractionsWithShowNotification = 0; final int expectedInteractionsWithShowNotification = 1;
runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList, runUserRestrictionsChange(currDisallow, nextDisallow, emptyActiveIfacesList,
expectedInteractionsWithShowNotification); expectedInteractionsWithShowNotification);