[TNU06] Add roaming notification

Warn user of potential data charges if the backhaul is
cellular and user is roaming.

Bug: 145629001
Test: atest TetheringTests
Change-Id: I74b4f87c2f6aad09e05d3f2a779f880396885953
Merged-In: I74b4f87c2f6aad09e05d3f2a779f880396885953
(cherry picked from commit 1af69e5b8f339bde5b70886d80960ce22c847245, aosp/1237026)
This commit is contained in:
Chalard Jean
2020-04-17 17:12:44 +00:00
committed by Paul Hu
parent 5e9c40c012
commit 4580c83253
7 changed files with 247 additions and 94 deletions

View File

@@ -17,4 +17,7 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"0" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
<!-- Config for showing upstream roaming notification. -->
<bool name="config_upstream_roaming_notification">true</bool>
</resources>

View File

@@ -17,4 +17,7 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"0" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">5000</integer>
<!-- Config for showing upstream roaming notification. -->
<bool name="config_upstream_roaming_notification">true</bool>
</resources>

View File

@@ -208,4 +208,9 @@
<!-- Delay(millisecond) to show no upstream notification after there's no Backhaul. Set delay to
"-1" for disable this feature. -->
<integer name="delay_to_show_no_upstream_after_no_backhaul">-1</integer>
<!-- Cellular roaming notification is shown when upstream is cellular network and in roaming
state. -->
<!-- Config for showing upstream roaming notification. -->
<bool name="config_upstream_roaming_notification">false</bool>
</resources>

View File

@@ -81,6 +81,7 @@ import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
import android.net.TetheredClient;
@@ -1476,7 +1477,7 @@ public class Tethering {
if (mTetherUpstream != newUpstream) {
mTetherUpstream = newUpstream;
mUpstreamNetworkMonitor.setCurrentUpstream(mTetherUpstream);
reportUpstreamChanged(mTetherUpstream);
reportUpstreamChanged(ns);
}
}
@@ -1598,7 +1599,8 @@ public class Tethering {
}
}
private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
@VisibleForTesting
void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
if (arg1 == UpstreamNetworkMonitor.NOTIFY_LOCAL_PREFIXES) {
mOffload.sendOffloadExemptPrefixes((Set<IpPrefix>) o);
return;
@@ -1624,6 +1626,9 @@ public class Tethering {
switch (arg1) {
case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
if (ns.network.equals(mTetherUpstream)) {
mNotificationUpdater.onUpstreamCapabilitiesChanged(ns.networkCapabilities);
}
handleNewUpstreamNetworkState(ns);
break;
case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
@@ -2009,8 +2014,10 @@ public class Tethering {
});
}
private void reportUpstreamChanged(Network network) {
private void reportUpstreamChanged(UpstreamNetworkState ns) {
final int length = mTetheringEventCallbacks.beginBroadcast();
final Network network = (ns != null) ? ns.network : null;
final NetworkCapabilities capabilities = (ns != null) ? ns.networkCapabilities : null;
try {
for (int i = 0; i < length; i++) {
try {
@@ -2022,7 +2029,9 @@ public class Tethering {
} finally {
mTetheringEventCallbacks.finishBroadcast();
}
mNotificationUpdater.onUpstreamNetworkChanged(network);
// Need to notify capabilities change after upstream network changed because new network's
// capabilities should be checked every time.
mNotificationUpdater.onUpstreamCapabilitiesChanged(capabilities);
}
private void reportConfigurationChanged(TetheringConfigurationParcel config) {

View File

@@ -16,6 +16,7 @@
package com.android.networkstack.tethering;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -30,7 +31,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -50,6 +51,9 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* A class to display tethering-related notifications.
*
@@ -82,6 +86,9 @@ public class TetheringNotificationUpdater {
// Id to update and cancel no upstream notification. Must be unique within the tethering app.
@VisibleForTesting
static final int NO_UPSTREAM_NOTIFICATION_ID = 1002;
// Id to update and cancel roaming notification. Must be unique within the tethering app.
@VisibleForTesting
static final int ROAMING_NOTIFICATION_ID = 1003;
@VisibleForTesting
static final int NO_ICON_ID = 0;
@VisibleForTesting
@@ -95,13 +102,14 @@ public class TetheringNotificationUpdater {
private final Handler mHandler;
// WARNING : the constructor is called on a different thread. Thread safety therefore
// relies on these values being initialized to 0 or false, and not any other value. If you need
// to change this, you will need to change the thread where the constructor is invoked,
// or to introduce synchronization.
// relies on these values being initialized to 0, false or null, and not any other value. If you
// need to change this, you will need to change the thread where the constructor is invoked, or
// to introduce synchronization.
// Downstream type is one of ConnectivityManager.TETHERING_* constants, 0 1 or 2.
// This value has to be made 1 2 and 4, and OR'd with the others.
private int mDownstreamTypesMask = DOWNSTREAM_NONE;
private boolean mNoUpstream = false;
private boolean mRoaming = false;
// WARNING : this value is not able to being initialized to 0 and must have volatile because
// telephony service is not guaranteed that is up before tethering service starts. If telephony
@@ -110,7 +118,13 @@ public class TetheringNotificationUpdater {
// INVALID_SUBSCRIPTION_ID.
private volatile int mActiveDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@IntDef({ENABLE_NOTIFICATION_ID, RESTRICTED_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID})
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
ENABLE_NOTIFICATION_ID,
RESTRICTED_NOTIFICATION_ID,
NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID
})
@interface NotificationId {}
private static final class MccMncOverrideInfo {
@@ -160,26 +174,22 @@ public class TetheringNotificationUpdater {
/** Called when downstream has changed */
public void onDownstreamChanged(@IntRange(from = 0, to = 7) final int downstreamTypesMask) {
if (mDownstreamTypesMask == downstreamTypesMask) return;
mDownstreamTypesMask = downstreamTypesMask;
updateEnableNotification();
updateNoUpstreamNotification();
updateActiveNotifications(
mActiveDataSubId, downstreamTypesMask, mNoUpstream, mRoaming);
}
/** Called when active data subscription id changed */
public void onActiveDataSubscriptionIdChanged(final int subId) {
if (mActiveDataSubId == subId) return;
mActiveDataSubId = subId;
updateEnableNotification();
updateNoUpstreamNotification();
updateActiveNotifications(subId, mDownstreamTypesMask, mNoUpstream, mRoaming);
}
/** Called when upstream network changed */
public void onUpstreamNetworkChanged(@Nullable final Network network) {
final boolean isNoUpstream = (network == null);
if (mNoUpstream == isNoUpstream) return;
mNoUpstream = isNoUpstream;
updateNoUpstreamNotification();
/** Called when upstream network capabilities changed */
public void onUpstreamCapabilitiesChanged(@Nullable final NetworkCapabilities capabilities) {
final boolean isNoUpstream = (capabilities == null);
final boolean isRoaming = capabilities != null
&& !capabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
updateActiveNotifications(
mActiveDataSubId, mDownstreamTypesMask, isNoUpstream, isRoaming);
}
@NonNull
@@ -208,6 +218,25 @@ public class TetheringNotificationUpdater {
return res;
}
private void updateActiveNotifications(final int subId, final int downstreamTypes,
final boolean noUpstream, final boolean isRoaming) {
final boolean tetheringActiveChanged =
(downstreamTypes == DOWNSTREAM_NONE) != (mDownstreamTypesMask == DOWNSTREAM_NONE);
final boolean subIdChanged = subId != mActiveDataSubId;
final boolean downstreamChanged = downstreamTypes != mDownstreamTypesMask;
final boolean upstreamChanged = noUpstream != mNoUpstream;
final boolean roamingChanged = isRoaming != mRoaming;
final boolean updateAll = tetheringActiveChanged || subIdChanged;
mActiveDataSubId = subId;
mDownstreamTypesMask = downstreamTypes;
mNoUpstream = noUpstream;
mRoaming = isRoaming;
if (updateAll || downstreamChanged) updateEnableNotification();
if (updateAll || upstreamChanged) updateNoUpstreamNotification();
if (updateAll || roamingChanged) updateRoamingNotification();
}
private void updateEnableNotification() {
final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
@@ -219,14 +248,20 @@ public class TetheringNotificationUpdater {
private void updateNoUpstreamNotification() {
final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
if (tetheringInactive
|| !mNoUpstream
|| setupNoUpstreamNotification() == NO_NOTIFY) {
if (tetheringInactive || !mNoUpstream || setupNoUpstreamNotification() == NO_NOTIFY) {
clearNotification(NO_UPSTREAM_NOTIFICATION_ID);
mHandler.removeMessages(EVENT_SHOW_NO_UPSTREAM);
}
}
private void updateRoamingNotification() {
final boolean tetheringInactive = mDownstreamTypesMask == DOWNSTREAM_NONE;
if (tetheringInactive || !mRoaming || setupRoamingNotification() == NO_NOTIFY) {
clearNotification(ROAMING_NOTIFICATION_ID);
}
}
@VisibleForTesting
void tetheringRestrictionLifted() {
clearNotification(RESTRICTED_NOTIFICATION_ID);
@@ -333,6 +368,29 @@ public class TetheringNotificationUpdater {
return icons;
}
private boolean setupRoamingNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final boolean upstreamRoamingNotification =
res.getBoolean(R.bool.config_upstream_roaming_notification);
if (!upstreamRoamingNotification) return NO_NOTIFY;
final String title = res.getString(R.string.upstream_roaming_notification_title);
final String message = res.getString(R.string.upstream_roaming_notification_message);
if (isEmpty(title) || isEmpty(message)) return NO_NOTIFY;
final PendingIntent pi = PendingIntent.getActivity(
mContext.createContextAsUser(UserHandle.CURRENT, 0 /* flags */),
0 /* requestCode */,
new Intent(Settings.ACTION_TETHER_SETTINGS),
Intent.FLAG_ACTIVITY_NEW_TASK,
null /* options */);
showNotification(R.drawable.stat_sys_tether_general, title, message,
ROAMING_NOTIFICATION_ID, pi, new Action[0]);
return NOTIFY_DONE;
}
private boolean setupNoUpstreamNotification() {
final Resources res = getResourcesForSubId(mContext, mActiveDataSubId);
final int delayToShowUpstreamNotification =

View File

@@ -23,10 +23,11 @@ import android.content.res.Resources
import android.net.ConnectivityManager.TETHERING_BLUETOOTH
import android.net.ConnectivityManager.TETHERING_USB
import android.net.ConnectivityManager.TETHERING_WIFI
import android.net.Network
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
import android.os.UserHandle
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager
@@ -39,6 +40,7 @@ import com.android.networkstack.tethering.TetheringNotificationUpdater.ENABLE_NO
import com.android.networkstack.tethering.TetheringNotificationUpdater.EVENT_SHOW_NO_UPSTREAM
import com.android.networkstack.tethering.TetheringNotificationUpdater.NO_UPSTREAM_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.RESTRICTED_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.ROAMING_NOTIFICATION_ID
import com.android.networkstack.tethering.TetheringNotificationUpdater.VERIZON_CARRIER_ID
import com.android.testutils.waitForIdle
import org.junit.After
@@ -75,6 +77,8 @@ const val TEST_MESSAGE = "Tap to set up hotspot."
const val TEST_NO_UPSTREAM_TITLE = "Hotspot has no internet access"
const val TEST_NO_UPSTREAM_MESSAGE = "Device cannot connect to internet."
const val TEST_NO_UPSTREAM_BUTTON = "Turn off hotspot"
const val TEST_ROAMING_TITLE = "Hotspot is on"
const val TEST_ROAMING_MESSAGE = "Additional charges may apply while roaming."
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -98,6 +102,11 @@ class TetheringNotificationUpdaterTest {
"WIFI|BT;android.test:drawable/general", "WIFI|USB;android.test:drawable/general",
"USB|BT;android.test:drawable/general", "WIFI|USB|BT;android.test:drawable/general")
private val ROAMING_CAPABILITIES = NetworkCapabilities()
private val HOME_CAPABILITIES = NetworkCapabilities().addCapability(NET_CAPABILITY_NOT_ROAMING)
private val NOTIFICATION_ICON_ID = R.drawable.stat_sys_tether_general
private val TIMEOUT_MS = 500L
private inner class TestContext(c: Context) : BroadcastInterceptingContext(c) {
override fun createContextAsUser(user: UserHandle, flags: Int) =
if (user == UserHandle.ALL) mockContext else this
@@ -123,6 +132,8 @@ class TetheringNotificationUpdaterTest {
.getStringArray(R.array.tethering_notification_icons)
doReturn(5).`when`(testResources)
.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
doReturn(true).`when`(testResources)
.getBoolean(R.bool.config_upstream_roaming_notification)
doReturn(TITLE).`when`(defaultResources).getString(R.string.tethering_notification_title)
doReturn(MESSAGE).`when`(defaultResources)
.getString(R.string.tethering_notification_message)
@@ -135,6 +146,10 @@ class TetheringNotificationUpdaterTest {
.getString(R.string.no_upstream_notification_message)
doReturn(TEST_NO_UPSTREAM_BUTTON).`when`(testResources)
.getString(R.string.no_upstream_notification_disable_button)
doReturn(TEST_ROAMING_TITLE).`when`(testResources)
.getString(R.string.upstream_roaming_notification_title)
doReturn(TEST_ROAMING_MESSAGE).`when`(testResources)
.getString(R.string.upstream_roaming_notification_message)
doReturn(USB_ICON_ID).`when`(defaultResources)
.getIdentifier(eq("android.test:drawable/usb"), any(), any())
doReturn(BT_ICON_ID).`when`(defaultResources)
@@ -176,41 +191,25 @@ class TetheringNotificationUpdaterTest {
assertEquals(iconId, notification.smallIcon.resId)
assertEquals(title, notification.title())
assertEquals(text, notification.text())
}
private fun verifyNotificationCancelled(id: Int) =
verify(notificationManager, times(1)).cancel(any(), eq(id))
private val tetheringActiveNotifications =
listOf(NO_UPSTREAM_NOTIFICATION_ID, ENABLE_NOTIFICATION_ID)
private fun verifyCancelAllTetheringActiveNotifications() {
tetheringActiveNotifications.forEach {
verifyNotificationCancelled(it)
}
reset(notificationManager)
}
private fun verifyOnlyTetheringActiveNotification(
notifyId: Int,
iconId: Int,
title: String,
text: String
private fun verifyNotificationCancelled(
notificationIds: List<Int>,
resetAfterVerified: Boolean = true
) {
tetheringActiveNotifications.forEach {
when (it) {
notifyId -> verifyNotification(iconId, title, text, notifyId)
else -> verifyNotificationCancelled(it)
notificationIds.forEach {
verify(notificationManager, times(1)).cancel(any(), eq(it))
}
}
reset(notificationManager)
if (resetAfterVerified) reset(notificationManager)
}
@Test
fun testNotificationWithDownstreamChanged() {
// Wifi downstream. No notification.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID))
// Same downstream changed. Nothing happened.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
@@ -218,23 +217,23 @@ class TetheringNotificationUpdaterTest {
// Wifi and usb downstreams. Show enable notification
notificationUpdater.onDownstreamChanged(WIFI_MASK or USB_MASK)
verifyOnlyTetheringActiveNotification(
ENABLE_NOTIFICATION_ID, GENERAL_ICON_ID, TITLE, MESSAGE)
verifyNotification(GENERAL_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// Usb downstream. Still show enable notification.
notificationUpdater.onDownstreamChanged(USB_MASK)
verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
}
@Test
fun testNotificationWithActiveDataSubscriptionIdChanged() {
// Usb downstream. Showed enable notification with default resource.
notificationUpdater.onDownstreamChanged(USB_MASK)
verifyOnlyTetheringActiveNotification(ENABLE_NOTIFICATION_ID, USB_ICON_ID, TITLE, MESSAGE)
verifyNotification(USB_ICON_ID, TITLE, MESSAGE, ENABLE_NOTIFICATION_ID)
// Same subId changed. Nothing happened.
notificationUpdater.onActiveDataSubscriptionIdChanged(INVALID_SUBSCRIPTION_ID)
@@ -242,16 +241,17 @@ class TetheringNotificationUpdaterTest {
// Set test sub id. Clear notification with test resource.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
verifyOnlyTetheringActiveNotification(
ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
}
private fun assertIconNumbers(number: Int, configs: Array<String?>) {
@@ -305,24 +305,21 @@ class TetheringNotificationUpdaterTest {
// User restrictions on. Show restricted notification.
notificationUpdater.notifyTetheringDisabledByRestriction()
verifyNotification(R.drawable.stat_sys_tether_general, title, message,
RESTRICTED_NOTIFICATION_ID)
reset(notificationManager)
verifyNotification(NOTIFICATION_ICON_ID, title, message, RESTRICTED_NOTIFICATION_ID)
// User restrictions off. Clear notification.
notificationUpdater.tetheringRestrictionLifted()
verifyNotificationCancelled(RESTRICTED_NOTIFICATION_ID)
reset(notificationManager)
verifyNotificationCancelled(listOf(RESTRICTED_NOTIFICATION_ID))
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// User restrictions on again. Show restricted notification with test resource.
notificationUpdater.notifyTetheringDisabledByRestriction()
verifyNotification(R.drawable.stat_sys_tether_general, disallowTitle, disallowMessage,
verifyNotification(NOTIFICATION_ICON_ID, disallowTitle, disallowMessage,
RESTRICTED_NOTIFICATION_ID)
reset(notificationManager)
}
val MAX_BACKOFF_MS = 200L
@@ -359,51 +356,53 @@ class TetheringNotificationUpdaterTest {
}
@Test
fun testNotificationWithUpstreamNetworkChanged() {
fun testNotificationWithUpstreamCapabilitiesChanged_NoUpstream() {
// Set test sub id. No notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
verifyOnlyTetheringActiveNotification(
ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// There is no upstream. Show no upstream notification.
notificationUpdater.onUpstreamNetworkChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
reset(notificationManager)
notificationUpdater.onUpstreamCapabilitiesChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
NO_UPSTREAM_NOTIFICATION_ID)
// Same upstream network changed. Nothing happened.
notificationUpdater.onUpstreamNetworkChanged(null)
// Same capabilities changed. Nothing happened.
notificationUpdater.onUpstreamCapabilitiesChanged(null)
verifyZeroInteractions(notificationManager)
// Upstream come back. Clear no upstream notification.
notificationUpdater.onUpstreamNetworkChanged(Network(1000))
verifyNotificationCancelled(NO_UPSTREAM_NOTIFICATION_ID)
reset(notificationManager)
notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
// No upstream again. Show no upstream notification.
notificationUpdater.onUpstreamNetworkChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
verifyNotification(R.drawable.stat_sys_tether_general, TEST_NO_UPSTREAM_TITLE,
TEST_NO_UPSTREAM_MESSAGE, NO_UPSTREAM_NOTIFICATION_ID)
reset(notificationManager)
notificationUpdater.onUpstreamCapabilitiesChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
NO_UPSTREAM_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
verifyCancelAllTetheringActiveNotifications()
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// Set R.integer.delay_to_show_no_upstream_after_no_backhaul to 0 and have wifi downstream
// again. Show enable notification only.
// Put up enable notification with wifi downstream and home capabilities.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// Set R.integer.delay_to_show_no_upstream_after_no_backhaul to -1 and change to no upstream
// again. Don't put up no upstream notification.
doReturn(-1).`when`(testResources)
.getInteger(R.integer.delay_to_show_no_upstream_after_no_backhaul)
notificationUpdater.onDownstreamChanged(WIFI_MASK)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, 500L)
verifyOnlyTetheringActiveNotification(
ENABLE_NOTIFICATION_ID, WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE)
notificationUpdater.onUpstreamCapabilitiesChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID))
}
@Test
@@ -428,4 +427,57 @@ class TetheringNotificationUpdaterTest {
assertEquals(311, res.configuration.mcc)
assertEquals(480, res.configuration.mnc)
}
@Test
fun testNotificationWithUpstreamCapabilitiesChanged_Roaming() {
// Set test sub id. Clear notification.
notificationUpdater.onActiveDataSubscriptionIdChanged(TEST_SUBID)
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// Wifi downstream. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// Upstream capabilities changed to roaming state. Show roaming notification.
notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
ROAMING_NOTIFICATION_ID)
// Same capabilities change. Nothing happened.
notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
verifyZeroInteractions(notificationManager)
// Upstream capabilities changed to home state. Clear roaming notification.
notificationUpdater.onUpstreamCapabilitiesChanged(HOME_CAPABILITIES)
verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID))
// Upstream capabilities changed to roaming state again. Show roaming notification.
notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
verifyNotification(NOTIFICATION_ICON_ID, TEST_ROAMING_TITLE, TEST_ROAMING_MESSAGE,
ROAMING_NOTIFICATION_ID)
// No upstream. Clear roaming notification and show no upstream notification.
notificationUpdater.onUpstreamCapabilitiesChanged(null)
notificationUpdater.handler.waitForDelayedMessage(EVENT_SHOW_NO_UPSTREAM, TIMEOUT_MS)
verifyNotificationCancelled(listOf(ROAMING_NOTIFICATION_ID), false)
verifyNotification(NOTIFICATION_ICON_ID, TEST_NO_UPSTREAM_TITLE, TEST_NO_UPSTREAM_MESSAGE,
NO_UPSTREAM_NOTIFICATION_ID)
// No downstream. No notification.
notificationUpdater.onDownstreamChanged(DOWNSTREAM_NONE)
verifyNotificationCancelled(listOf(ENABLE_NOTIFICATION_ID, NO_UPSTREAM_NOTIFICATION_ID,
ROAMING_NOTIFICATION_ID))
// Wifi downstream again. Show enable notification with test resource.
notificationUpdater.onDownstreamChanged(WIFI_MASK)
verifyNotification(WIFI_ICON_ID, TEST_TITLE, TEST_MESSAGE, ENABLE_NOTIFICATION_ID)
// Set R.bool.config_upstream_roaming_notification to false and change upstream
// network to roaming state again. No roaming notification.
doReturn(false).`when`(testResources)
.getBoolean(R.bool.config_upstream_roaming_notification)
notificationUpdater.onUpstreamCapabilitiesChanged(ROAMING_CAPABILITIES)
verifyNotificationCancelled(listOf(NO_UPSTREAM_NOTIFICATION_ID, ROAMING_NOTIFICATION_ID))
}
}

View File

@@ -48,6 +48,7 @@ import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -1700,7 +1701,29 @@ public class TetheringTest {
stateMachine.chooseUpstreamType(true);
verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(eq(upstreamState.network));
verify(mNotificationUpdater, times(1)).onUpstreamNetworkChanged(eq(upstreamState.network));
verify(mNotificationUpdater, times(1)).onUpstreamCapabilitiesChanged(any());
}
@Test
public void testUpstreamCapabilitiesChanged() {
final Tethering.TetherMasterSM stateMachine = (Tethering.TetherMasterSM)
mTetheringDependencies.mUpstreamNetworkMonitorMasterSM;
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())).thenReturn(upstreamState);
stateMachine.chooseUpstreamType(true);
stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState);
// Should have two onUpstreamCapabilitiesChanged().
// One is called by reportUpstreamChanged(). One is called by EVENT_ON_CAPABILITIES.
verify(mNotificationUpdater, times(2)).onUpstreamCapabilitiesChanged(any());
reset(mNotificationUpdater);
// Verify that onUpstreamCapabilitiesChanged won't be called if not current upstream network
// capabilities changed.
final UpstreamNetworkState upstreamState2 = new UpstreamNetworkState(
upstreamState.linkProperties, upstreamState.networkCapabilities, new Network(101));
stateMachine.handleUpstreamNetworkMonitorCallback(EVENT_ON_CAPABILITIES, upstreamState2);
verify(mNotificationUpdater, never()).onUpstreamCapabilitiesChanged(any());
}
// TODO: Test that a request for hotspot mode doesn't interfere with an