Merge "[TNU06] Add roaming notification" am: 565160c302
Change-Id: I8f66563665375bdde40b59c997b9aba197f04dbd
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -206,4 +206,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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user