Merge changes from topic "am-7f0575b5d09641f7a3715eefeb61bed8"
* changes:
Merge "Add tests for the network blocked status change" am: af0de1a2e9 am: 43d8774f68 am: 9944d7f079
Merge "Add new callback to inform blocking of network on specific uid." am: f709e754ab am: 745952eac8 am: 70626e3b94
diff --git a/api/current.txt b/api/current.txt
index abba447..a78a4e3 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -27447,6 +27447,7 @@
public static class ConnectivityManager.NetworkCallback {
ctor public ConnectivityManager.NetworkCallback();
method public void onAvailable(android.net.Network);
+ method public void onBlockedStatusChanged(android.net.Network, boolean);
method public void onCapabilitiesChanged(android.net.Network, android.net.NetworkCapabilities);
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
@@ -27718,16 +27719,16 @@
public class NetworkInfo implements android.os.Parcelable {
method public int describeContents();
- method public android.net.NetworkInfo.DetailedState getDetailedState();
+ method public deprecated android.net.NetworkInfo.DetailedState getDetailedState();
method public java.lang.String getExtraInfo();
method public deprecated java.lang.String getReason();
method public deprecated android.net.NetworkInfo.State getState();
- method public int getSubtype();
- method public java.lang.String getSubtypeName();
+ method public deprecated int getSubtype();
+ method public deprecated java.lang.String getSubtypeName();
method public deprecated int getType();
method public deprecated java.lang.String getTypeName();
method public deprecated boolean isAvailable();
- method public boolean isConnected();
+ method public deprecated boolean isConnected();
method public deprecated boolean isConnectedOrConnecting();
method public deprecated boolean isFailover();
method public deprecated boolean isRoaming();
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 1fbfa40..4714587 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -2816,10 +2816,11 @@
* @param network The {@link Network} of the satisfying network.
* @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
* @param linkProperties The {@link LinkProperties} of the satisfying network.
+ * @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
- LinkProperties linkProperties) {
+ LinkProperties linkProperties, boolean blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
@@ -2830,6 +2831,7 @@
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
+ onBlockedStatusChanged(network, blocked);
}
/**
@@ -2837,7 +2839,8 @@
* This callback may be called more than once if the {@link Network} that is
* satisfying the request changes. This will always immediately be followed by a
* call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
- * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+ * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}, and a call to
+ * {@link #onBlockedStatusChanged(Network, boolean)}.
*
* @param network The {@link Network} of the satisfying network.
*/
@@ -2916,6 +2919,14 @@
*/
public void onNetworkResumed(Network network) {}
+ /**
+ * Called when access to the specified network is blocked or unblocked.
+ *
+ * @param network The {@link Network} whose blocked status has changed.
+ * @param blocked The blocked status of this {@link Network}.
+ */
+ public void onBlockedStatusChanged(Network network, boolean blocked) {}
+
private NetworkRequest networkRequest;
}
@@ -2962,6 +2973,8 @@
public static final int CALLBACK_SUSPENDED = BASE + 9;
/** @hide */
public static final int CALLBACK_RESUMED = BASE + 10;
+ /** @hide */
+ public static final int CALLBACK_BLK_CHANGED = BASE + 11;
/** @hide */
public static String getCallbackName(int whichCallback) {
@@ -2976,6 +2989,7 @@
case EXPIRE_LEGACY_REQUEST: return "EXPIRE_LEGACY_REQUEST";
case CALLBACK_SUSPENDED: return "CALLBACK_SUSPENDED";
case CALLBACK_RESUMED: return "CALLBACK_RESUMED";
+ case CALLBACK_BLK_CHANGED: return "CALLBACK_BLK_CHANGED";
default:
return Integer.toString(whichCallback);
}
@@ -3022,7 +3036,7 @@
case CALLBACK_AVAILABLE: {
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
LinkProperties lp = getObject(message, LinkProperties.class);
- callback.onAvailable(network, cap, lp);
+ callback.onAvailable(network, cap, lp, message.arg1 != 0);
break;
}
case CALLBACK_LOSING: {
@@ -3055,6 +3069,10 @@
callback.onNetworkResumed(network);
break;
}
+ case CALLBACK_BLK_CHANGED: {
+ boolean blocked = message.arg1 != 0;
+ callback.onBlockedStatusChanged(network, blocked);
+ }
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 12b6f9e..0bdfca7 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -1590,4 +1590,14 @@
Preconditions.checkArgument(isValidCapability(capability),
"NetworkCapability " + capability + "out of range");
}
+
+ /**
+ * Check if this {@code NetworkCapability} instance is metered.
+ *
+ * @return {@code true} if {@code NET_CAPABILITY_NOT_METERED} is not set on this instance.
+ * @hide
+ */
+ public boolean isMetered() {
+ return !hasCapability(NET_CAPABILITY_NOT_METERED);
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index d912dd10..1a1d2d334 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -202,7 +202,9 @@
* Return a network-type-specific integer describing the subtype
* of the network.
* @return the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public int getSubtype() {
synchronized (this) {
return mSubtype;
@@ -243,7 +245,9 @@
/**
* Return a human-readable name describing the subtype of the network.
* @return the name of the network subtype
+ * @deprecated Use {@link android.telephony.TelephonyManager#getDataNetworkType} instead.
*/
+ @Deprecated
public String getSubtypeName() {
synchronized (this) {
return mSubtypeName;
@@ -278,7 +282,15 @@
* connections and pass data.
* <p>Always call this before attempting to perform data transactions.
* @return {@code true} if network connectivity exists, {@code false} otherwise.
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public boolean isConnected() {
synchronized (this) {
return mState == State.CONNECTED;
@@ -411,7 +423,15 @@
/**
* Reports the current fine-grained state of the network.
* @return the fine-grained state
+ * @deprecated Apps should instead use the
+ * {@link android.net.ConnectivityManager.NetworkCallback} API to
+ * learn about connectivity changes. See
+ * {@link ConnectivityManager#registerDefaultNetworkCallback} and
+ * {@link ConnectivityManager#registerNetworkCallback}. These will
+ * give a more accurate picture of the connectivity state of
+ * the device and let apps react more easily and quickly to changes.
*/
+ @Deprecated
public DetailedState getDetailedState() {
synchronized (this) {
return mDetailedState;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b750d79..1c8d99a 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -35,6 +35,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
@@ -189,6 +191,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -257,6 +260,14 @@
@GuardedBy("mVpns")
private LockdownVpnTracker mLockdownTracker;
+ /**
+ * Stale copy of uid rules provided by NPMS. As long as they are accessed only in internal
+ * handler thread, they don't need a lock.
+ */
+ private SparseIntArray mUidRules = new SparseIntArray();
+ /** Flag indicating if background data is restricted. */
+ private boolean mRestrictBackground;
+
final private Context mContext;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -419,6 +430,16 @@
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+ /**
+ * Used to handle onUidRulesChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_UID_RULES_CHANGED = 39;
+
+ /**
+ * Used to handle onRestrictBackgroundChanged event from NetworkPolicyManagerService.
+ */
+ private static final int EVENT_DATA_SAVER_CHANGED = 40;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -780,6 +801,9 @@
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ // To ensure uid rules are synchronized with Network Policy, register for
+ // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
+ // reading existing policy from disk.
try {
mPolicyManager.registerListener(mPolicyListener);
} catch (RemoteException e) {
@@ -910,7 +934,8 @@
registerPrivateDnsSettingsCallbacks();
}
- private Tethering makeTethering() {
+ @VisibleForTesting
+ protected Tethering makeTethering() {
// TODO: Move other elements into @Overridden getters.
final TetheringDependencies deps = new TetheringDependencies() {
@Override
@@ -1116,11 +1141,6 @@
if (ignoreBlocked) {
return false;
}
- // Networks are never blocked for system services
- // TODO: consider moving this check to NetworkPolicyManagerInternal.isUidNetworkingBlocked.
- if (isSystem(uid)) {
- return false;
- }
synchronized (mVpns) {
final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
if (vpn != null && vpn.isBlockingUid(uid)) {
@@ -1150,6 +1170,17 @@
mNetworkInfoBlockingLogs.log(action + " " + uid);
}
+ private void maybeLogBlockedStatusChanged(NetworkRequestInfo nri, Network net,
+ boolean blocked) {
+ if (nri == null || net == null || !LOGD_BLOCKED_NETWORKINFO) {
+ return;
+ }
+ String action = blocked ? "BLOCKED" : "UNBLOCKED";
+ log(String.format("Blocked status changed to %s for %d(%d) on netId %d", blocked,
+ nri.mUid, nri.request.requestId, net.netId));
+ mNetworkInfoBlockingLogs.log(action + " " + nri.mUid);
+ }
+
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
@@ -1651,10 +1682,17 @@
private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
- // TODO: notify UID when it has requested targeted updates
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_RULES_CHANGED, uid, uidRules));
}
@Override
public void onRestrictBackgroundChanged(boolean restrictBackground) {
+ // caller is NPMS, since we only register with them
+ if (LOGD_BLOCKED_NETWORKINFO) {
+ log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
+ }
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_DATA_SAVER_CHANGED, restrictBackground ? 1 : 0, 0));
+
// TODO: relocate this specific callback in Tethering.
if (restrictBackground) {
log("onRestrictBackgroundChanged(true): disabling tethering");
@@ -1663,6 +1701,50 @@
}
};
+ void handleUidRulesChanged(int uid, int newRules) {
+ // skip update when we've already applied rules
+ final int oldRules = mUidRules.get(uid, RULE_NONE);
+ if (oldRules == newRules) return;
+
+ maybeNotifyNetworkBlockedForNewUidRules(uid, newRules);
+
+ if (newRules == RULE_NONE) {
+ mUidRules.delete(uid);
+ } else {
+ mUidRules.put(uid, newRules);
+ }
+ }
+
+ void handleRestrictBackgroundChanged(boolean restrictBackground) {
+ if (mRestrictBackground == restrictBackground) return;
+
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean curMetered = nai.networkCapabilities.isMetered();
+ maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+ restrictBackground);
+ }
+
+ mRestrictBackground = restrictBackground;
+ }
+
+ private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ synchronized (mVpns) {
+ final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
+ // Because the return value of this function depends on the list of UIDs the
+ // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
+ // list all state depending on the return value of this function has to be recomputed.
+ // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
+ // send the necessary onBlockedStatusChanged callbacks.
+ if (vpn != null && vpn.isBlockingUid(uid)) {
+ return true;
+ }
+ }
+
+ return mPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
+ isNetworkMetered, isBackgroundRestricted);
+ }
+
/**
* Require that the caller is either in the same user or has appropriate permission to interact
* across users.
@@ -2118,6 +2200,28 @@
pw.decreaseIndent();
pw.println();
+ pw.print("Restrict background: ");
+ pw.println(mRestrictBackground);
+ pw.println();
+
+ pw.println("Status for known UIDs:");
+ pw.increaseIndent();
+ final int size = mUidRules.size();
+ for (int i = 0; i < size; i++) {
+ // Don't crash if the array is modified while dumping in bugreports.
+ try {
+ final int uid = mUidRules.keyAt(i);
+ final int uidRules = mUidRules.get(uid, RULE_NONE);
+ pw.println("UID=" + uid + " rules=" + uidRulesToString(uidRules));
+ } catch (ArrayIndexOutOfBoundsException e) {
+ pw.println(" ArrayIndexOutOfBoundsException");
+ } catch (ConcurrentModificationException e) {
+ pw.println(" ConcurrentModificationException");
+ }
+ }
+ pw.println();
+ pw.decreaseIndent();
+
pw.println("Network Requests:");
pw.increaseIndent();
dumpNetworkRequests(pw);
@@ -3195,6 +3299,12 @@
handlePrivateDnsValidationUpdate(
(PrivateDnsValidationUpdate) msg.obj);
break;
+ case EVENT_UID_RULES_CHANGED:
+ handleUidRulesChanged(msg.arg1, msg.arg2);
+ break;
+ case EVENT_DATA_SAVER_CHANGED:
+ handleRestrictBackgroundChanged(toBool(msg.arg1));
+ break;
}
}
}
@@ -3783,6 +3893,8 @@
private void setLockdownTracker(LockdownVpnTracker tracker) {
// Shutdown any existing tracker
final LockdownVpnTracker existing = mLockdownTracker;
+ // TODO: Add a trigger when the always-on VPN enable/disable to reevaluate and send the
+ // necessary onBlockedStatusChanged callbacks.
mLockdownTracker = null;
if (existing != null) {
existing.shutdown();
@@ -4893,12 +5005,20 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
- // Report changes that are interesting for network statistics tracking.
if (prevNc != null) {
- final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
- newNc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean oldMetered = prevNc.isMetered();
+ final boolean newMetered = newNc.isMetered();
+ final boolean meteredChanged = oldMetered != newMetered;
+
+ if (meteredChanged) {
+ maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
+ mRestrictBackground);
+ }
+
final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+ // Report changes that are interesting for network statistics tracking.
if (meteredChanged || roamingChanged) {
notifyIfacesChangedForNetworkStats();
}
@@ -5028,6 +5148,8 @@
case ConnectivityManager.CALLBACK_AVAILABLE: {
putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ // For this notification, arg1 contains the blocked status.
+ msg.arg1 = arg1;
break;
}
case ConnectivityManager.CALLBACK_LOSING: {
@@ -5045,6 +5167,10 @@
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
break;
}
+ case ConnectivityManager.CALLBACK_BLK_CHANGED: {
+ msg.arg1 = arg1;
+ break;
+ }
}
msg.what = notificationType;
msg.setData(bundle);
@@ -5600,7 +5726,76 @@
return;
}
- callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+ metered, mRestrictBackground);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
+ }
+
+ /**
+ * Notify of the blocked state apps with a registered callback matching a given NAI.
+ *
+ * Unlike other callbacks, blocked status is different between each individual uid. So for
+ * any given nai, all requests need to be considered according to the uid who filed it.
+ *
+ * @param nai The target NetworkAgentInfo.
+ * @param oldMetered True if the previous network capabilities is metered.
+ * @param newRestrictBackground True if data saver is enabled.
+ */
+ private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
+ boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ final int uidRules = mUidRules.get(nri.mUid);
+ final boolean oldBlocked, newBlocked;
+ // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
+ // between these two calls.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
+ oldRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
+ newRestrictBackground);
+ }
+ if (oldBlocked != newBlocked) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
+ encodeBool(newBlocked));
+ }
+ }
+ }
+
+ /**
+ * Notify apps with a given UID of the new blocked state according to new uid rules.
+ * @param uid The uid for which the rules changed.
+ * @param newRules The new rules to apply.
+ */
+ private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ final boolean metered = nai.networkCapabilities.isMetered();
+ final boolean oldBlocked, newBlocked;
+ // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
+ // rules changed event. And this function actually loop through all connected nai and
+ // its requests. It seems that mVpns lock will be grabbed frequently in this case.
+ // Reduce the number of locking or optimize the use of lock are likely needed in future.
+ synchronized (mVpns) {
+ oldBlocked = isUidNetworkingWithVpnBlocked(
+ uid, mUidRules.get(uid), metered, mRestrictBackground);
+ newBlocked = isUidNetworkingWithVpnBlocked(
+ uid, newRules, metered, mRestrictBackground);
+ }
+ if (oldBlocked == newBlocked) {
+ return;
+ }
+ final int arg = encodeBool(newBlocked);
+ for (int i = 0; i < nai.numNetworkRequests(); i++) {
+ NetworkRequest nr = nai.requestAt(i);
+ NetworkRequestInfo nri = mNetworkRequests.get(nr);
+ if (nri != null && nri.mUid == uid) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
+ }
+ }
+ }
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 452b699..4f4b6bf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -72,6 +72,7 @@
static final int NTWK_ALLOWED_TMP_WHITELIST = 4;
static final int NTWK_BLOCKED_BG_RESTRICT = 5;
static final int NTWK_ALLOWED_DEFAULT = 6;
+ static final int NTWK_ALLOWED_SYSTEM = 7;
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 61d67b7..099671d 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -16,6 +16,8 @@
package com.android.server.net;
+import static com.android.server.net.NetworkPolicyManagerService.isUidNetworkingBlockedInternal;
+
import android.net.Network;
import android.net.NetworkTemplate;
import android.telephony.SubscriptionPlan;
@@ -46,6 +48,28 @@
public abstract boolean isUidNetworkingBlocked(int uid, String ifname);
/**
+ * Figure out if networking is blocked for a given set of conditions.
+ *
+ * This is used by ConnectivityService via passing stale copies of conditions, so it must not
+ * take any locks.
+ *
+ * @param uid The target uid.
+ * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
+ * @param isNetworkMetered True if the network is metered.
+ * @param isBackgroundRestricted True if data saver is enabled.
+ *
+ * @return true if networking is blocked for the UID under the specified conditions.
+ */
+ public static boolean isUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted) {
+ // Log of invoking internal function is disabled because it will be called very
+ // frequently. And metrics are unlikely needed on this method because the callers are
+ // external and this method doesn't take any locks or perform expensive operations.
+ return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, null);
+ }
+
+ /**
* Informs that an appId has been added or removed from the temp-powersave-whitelist so that
* that network rules for that appId can be updated.
*
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 48e09d7..d799642 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -99,6 +99,7 @@
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_DEFAULT;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_NON_METERED;
+import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_SYSTEM;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_TMP_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_ALLOWED_WHITELIST;
import static com.android.server.net.NetworkPolicyLogger.NTWK_BLOCKED_BG_RESTRICT;
@@ -4837,46 +4838,75 @@
final long startTime = mStatLogger.getTime();
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
-
- mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
-
- return ret;
- }
-
- private boolean isUidNetworkingBlockedInternal(int uid, boolean isNetworkMetered) {
final int uidRules;
final boolean isBackgroundRestricted;
synchronized (mUidRulesFirstLock) {
uidRules = mUidRules.get(uid, RULE_NONE);
isBackgroundRestricted = mRestrictBackground;
}
- if (hasRule(uidRules, RULE_REJECT_ALL)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_POWER);
- return true;
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
+
+ mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
+
+ return ret;
+ }
+
+ private static boolean isSystem(int uid) {
+ return uid < Process.FIRST_APPLICATION_UID;
+ }
+
+ static boolean isUidNetworkingBlockedInternal(int uid, int uidRules, boolean isNetworkMetered,
+ boolean isBackgroundRestricted, @Nullable NetworkPolicyLogger logger) {
+ final int reason;
+ // Networks are never blocked for system components
+ if (isSystem(uid)) {
+ reason = NTWK_ALLOWED_SYSTEM;
}
- if (!isNetworkMetered) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_NON_METERED);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_ALL)) {
+ reason = NTWK_BLOCKED_POWER;
}
- if (hasRule(uidRules, RULE_REJECT_METERED)) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BLACKLIST);
- return true;
+ else if (!isNetworkMetered) {
+ reason = NTWK_ALLOWED_NON_METERED;
}
- if (hasRule(uidRules, RULE_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_REJECT_METERED)) {
+ reason = NTWK_BLOCKED_BLACKLIST;
}
- if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
- mLogger.networkBlocked(uid, NTWK_ALLOWED_TMP_WHITELIST);
- return false;
+ else if (hasRule(uidRules, RULE_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_WHITELIST;
}
- if (isBackgroundRestricted) {
- mLogger.networkBlocked(uid, NTWK_BLOCKED_BG_RESTRICT);
- return true;
+ else if (hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED)) {
+ reason = NTWK_ALLOWED_TMP_WHITELIST;
}
- mLogger.networkBlocked(uid, NTWK_ALLOWED_DEFAULT);
- return false;
+ else if (isBackgroundRestricted) {
+ reason = NTWK_BLOCKED_BG_RESTRICT;
+ }
+ else {
+ reason = NTWK_ALLOWED_DEFAULT;
+ }
+
+ final boolean blocked;
+ switch(reason) {
+ case NTWK_ALLOWED_DEFAULT:
+ case NTWK_ALLOWED_NON_METERED:
+ case NTWK_ALLOWED_TMP_WHITELIST:
+ case NTWK_ALLOWED_WHITELIST:
+ case NTWK_ALLOWED_SYSTEM:
+ blocked = false;
+ break;
+ case NTWK_BLOCKED_POWER:
+ case NTWK_BLOCKED_BLACKLIST:
+ case NTWK_BLOCKED_BG_RESTRICT:
+ blocked = true;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ if (logger != null) {
+ logger.networkBlocked(uid, reason);
+ }
+
+ return blocked;
}
private class NetworkPolicyManagerInternalImpl extends NetworkPolicyManagerInternal {
@@ -4918,11 +4948,18 @@
public boolean isUidNetworkingBlocked(int uid, String ifname) {
final long startTime = mStatLogger.getTime();
+ final int uidRules;
+ final boolean isBackgroundRestricted;
+ synchronized (mUidRulesFirstLock) {
+ uidRules = mUidRules.get(uid, RULE_NONE);
+ isBackgroundRestricted = mRestrictBackground;
+ }
final boolean isNetworkMetered;
synchronized (mNetworkPoliciesSecondLock) {
isNetworkMetered = mMeteredIfaces.contains(ifname);
}
- final boolean ret = isUidNetworkingBlockedInternal(uid, isNetworkMetered);
+ final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
+ isBackgroundRestricted, mLogger);
mStatLogger.logDurationStat(Stats.IS_UID_NETWORKING_BLOCKED, startTime);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eb88ba..113ee2d 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -26,13 +26,21 @@
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
+import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.os.Process.SYSTEM_UID;
import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -124,6 +132,7 @@
import android.text.format.Time;
import android.util.DataUnit;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.util.RecurrenceRule;
@@ -171,6 +180,7 @@
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
@@ -1644,6 +1654,76 @@
true);
}
+ /**
+ * Exhaustively test isUidNetworkingBlocked to output the expected results based on external
+ * conditions.
+ */
+ @Test
+ public void testIsUidNetworkingBlocked() {
+ final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
+
+ // Metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Metered network. Data saver off.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+
+ // Non-metered network. Data saver on.
+ expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
+ expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
+ expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
+ verifyNetworkBlockedState(
+ false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
+
+ // Non-metered network. Data saver off. The result is the same as previous case since
+ // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
+ verifyNetworkBlockedState(
+ false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
+ expectedBlockedStates.clear();
+ }
+
+ private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
+ ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
+ final NetworkPolicyManagerInternal npmi = LocalServices
+ .getService(NetworkPolicyManagerInternal.class);
+
+ for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
+ final boolean expectedResult = pair.first;
+ final int rule = pair.second;
+ assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
+ expectedResult,
+ npmi.isUidNetworkingBlocked(UID_A, rule, metered, backgroundRestricted));
+ assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
+ npmi.isUidNetworkingBlocked(SYSTEM_UID, rule, metered, backgroundRestricted));
+ }
+ }
+
+ private String formatBlockedStateError(int uid, int rule, boolean metered,
+ boolean backgroundRestricted) {
+ return String.format(
+ "Unexpected BlockedState: (uid=%d, rule=%s, metered=%b, backgroundRestricted=%b)",
+ uid, uidRulesToString(rule), metered, backgroundRestricted);
+ }
+
private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
return SubscriptionPlan.Builder
.createRecurringMonthly(start)
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 03a617c..6174c6c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -219,7 +219,7 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -247,7 +247,7 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
- any(NetworkCapabilities.class), any(LinkProperties.class));
+ any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
manager.unregisterNetworkCallback(callback);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1c77fcc..17bcea0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -51,6 +51,10 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_NONE;
+import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static com.android.internal.util.TestUtils.waitForIdleLooper;
@@ -92,6 +96,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.InterfaceConfiguration;
@@ -148,6 +153,7 @@
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
+import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -215,11 +221,13 @@
private MockNetworkAgent mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
+ private INetworkPolicyListener mPolicyListener;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
+ @Mock INetworkPolicyManager mNpm;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -934,6 +942,11 @@
}
@Override
+ protected Tethering makeTethering() {
+ return mock(Tethering.class);
+ }
+
+ @Override
protected int reserveNetId() {
while (true) {
final int netId = super.reserveNetId();
@@ -1023,6 +1036,20 @@
public void waitForIdle() {
waitForIdle(TIMEOUT_MS);
}
+
+ public void setUidRulesChanged(int uidRules) {
+ try {
+ mPolicyListener.onUidRulesChanged(Process.myUid(), uidRules);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ public void setRestrictBackgroundChanged(boolean restrictBackground) {
+ try {
+ mPolicyListener.onRestrictBackgroundChanged(restrictBackground);
+ } catch (RemoteException ignored) {
+ }
+ }
}
/**
@@ -1055,12 +1082,18 @@
LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
LocalServices.addService(
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
+
mService = new WrappedConnectivityService(mServiceContext,
mNetworkManagementService,
mStatsService,
- mock(INetworkPolicyManager.class),
+ mNpm,
mock(IpConnectivityLog.class));
+ final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
+ ArgumentCaptor.forClass(INetworkPolicyListener.class);
+ verify(mNpm).registerListener(policyListenerCaptor.capture());
+ mPolicyListener = policyListenerCaptor.getValue();
+
// Create local CM before sending system ready so that we can answer
// getSystemService() correctly.
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
@@ -1441,7 +1474,8 @@
RESUMED,
LOSING,
LOST,
- UNAVAILABLE
+ UNAVAILABLE,
+ BLOCKED_STATUS
}
private static class CallbackInfo {
@@ -1522,6 +1556,11 @@
setLastCallback(CallbackState.LOST, network, null);
}
+ @Override
+ public void onBlockedStatusChanged(Network network, boolean blocked) {
+ setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+ }
+
public Network getLastAvailableNetwork() {
return mLastAvailableNetwork;
}
@@ -1582,6 +1621,7 @@
// - onSuspended, iff the network was suspended when the callbacks fire.
// - onCapabilitiesChanged.
// - onLinkPropertiesChanged.
+ // - onBlockedStatusChanged.
//
// @param agent the network to expect the callbacks on.
// @param expectSuspended whether to expect a SUSPENDED callback.
@@ -1589,7 +1629,7 @@
// onCapabilitiesChanged callback.
// @param timeoutMs how long to wait for the callbacks.
void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended,
- boolean expectValidated, int timeoutMs) {
+ boolean expectValidated, boolean expectBlocked, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1600,19 +1640,28 @@
expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, agent, timeoutMs);
}
expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+ expectBlockedStatusCallback(expectBlocked, agent);
}
// Expects the available callbacks (validated), plus onSuspended.
void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent, boolean expectValidated) {
- expectAvailableCallbacks(agent, true, expectValidated, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, true, expectValidated, false, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksValidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, true, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, true, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksValidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, true, true, TEST_CALLBACK_TIMEOUT_MS);
}
void expectAvailableCallbacksUnvalidated(MockNetworkAgent agent) {
- expectAvailableCallbacks(agent, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ expectAvailableCallbacks(agent, false, false, false, TEST_CALLBACK_TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacksUnvalidatedAndBlocked(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, false, true, TEST_CALLBACK_TIMEOUT_MS);
}
// Expects the available callbacks (where the onCapabilitiesChanged must contain the
@@ -1623,6 +1672,9 @@
expectCallback(CallbackState.AVAILABLE, agent, TEST_CALLBACK_TIMEOUT_MS);
NetworkCapabilities nc1 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
expectCallback(CallbackState.LINK_PROPERTIES, agent, TEST_CALLBACK_TIMEOUT_MS);
+ // Implicitly check the network is allowed to use.
+ // TODO: should we need to consider if network is in blocked status in this case?
+ expectBlockedStatusCallback(false, agent);
NetworkCapabilities nc2 = expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
assertEquals(nc1, nc2);
}
@@ -1665,6 +1717,12 @@
fn.test((NetworkCapabilities) cbi.arg));
}
+ void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent);
+ boolean actualBlocked = (boolean) cbi.arg;
+ assertEquals(expectBlocked, actualBlocked);
+ }
+
void assertNoCallback() {
waitForIdle();
CallbackInfo c = mCallbacks.peek();
@@ -3223,7 +3281,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
// pass timeout and validate that UNAVAILABLE is not called
@@ -3243,7 +3301,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, false,
TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -3802,6 +3860,7 @@
networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
networkAgent);
+ networkCallback.expectCallback(CallbackState.BLOCKED_STATUS, networkAgent);
networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
networkCallback.assertNoCallback();
checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
@@ -4010,6 +4069,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4068,6 +4128,7 @@
mCellNetworkAgent);
CallbackInfo cbi = cellNetworkCallback.expectCallback(
CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.BLOCKED_STATUS, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
@@ -4444,6 +4505,101 @@
mMockVpn.disconnect();
}
+ @Test
+ public void testNetworkBlockedStatus() {
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .build();
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // ConnectivityService should cache it not to invoke the callback again.
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.assertNoCallback();
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+
+ // Restrict the network based on UID rule and NOT_METERED capability change.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED,
+ mCellNetworkAgent);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setUidRulesChanged(RULE_ALLOW_METERED);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ mService.setUidRulesChanged(RULE_NONE);
+ cellNetworkCallback.assertNoCallback();
+
+ // Restrict the network based on BackgroundRestricted.
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
+ mService.setRestrictBackgroundChanged(true);
+ cellNetworkCallback.assertNoCallback();
+ mService.setRestrictBackgroundChanged(false);
+ cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ }
+
+ @Test
+ public void testNetworkBlockedStatusBeforeAndAfterConnect() {
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ // No Networkcallbacks invoked before any network is active.
+ mService.setUidRulesChanged(RULE_REJECT_ALL);
+ mService.setUidRulesChanged(RULE_NONE);
+ mService.setUidRulesChanged(RULE_REJECT_METERED);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ defaultCallback.expectAvailableCallbacksUnvalidatedAndBlocked(mCellNetworkAgent);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mCellNetworkAgent);
+
+ // Allow to use the network after switching to NOT_METERED network.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+ defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+
+ // Switch to METERED network. Restrict the use of the network.
+ mWiFiNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksValidatedAndBlocked(mCellNetworkAgent);
+
+ // Network becomes NOT_METERED.
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ defaultCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_METERED, mCellNetworkAgent);
+ defaultCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
+
+ // Verify there's no Networkcallbacks invoked after data saver on/off.
+ mService.setRestrictBackgroundChanged(true);
+ mService.setRestrictBackgroundChanged(false);
+ defaultCallback.assertNoCallback();
+
+ mCellNetworkAgent.disconnect();
+ defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ defaultCallback.assertNoCallback();
+
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
/**
* Make simulated InterfaceConfig for Nat464Xlat to query clat lower layer info.
*/