[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

@@ -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 =