Add onBlockedStatusChanged(Network, int) to NetworkCallback.
This is similar to onBlockedStatusChanged(Network, boolean) but it allows the callback holder to know the exact reason why networking was blocked. It is useful to privileged system components such as JobScheduler that are able to ignore some blocked reasons but not others. Also add a new BLOCKED_REASON_LOCKDOWN_VPN that is used when networking is blocked because an always-on VPN is in lockdown mode. Also move BLOCKED_METERED_REASON_MASK to ConnectivityManager. This is necessary because ConnectivityService must ensure that the blocked status callbacks are correctly sent when meteredness changes (e.g., a UID that is blocked on metered networks will become unblocked on a network that becomes unmetered). In order to do this it needs to know which reasons apply only on metered networks. Bug: 165835257 Test: unit tests in subsequent CLs in the stack Change-Id: I647db4f5a01280be220288e73ffa85c15bec9370
This commit is contained in:
@@ -28,10 +28,12 @@ package android.net {
|
||||
method public void systemReady();
|
||||
field public static final int BLOCKED_METERED_REASON_ADMIN_DISABLED = 262144; // 0x40000
|
||||
field public static final int BLOCKED_METERED_REASON_DATA_SAVER = 65536; // 0x10000
|
||||
field public static final int BLOCKED_METERED_REASON_MASK = -65536; // 0xffff0000
|
||||
field public static final int BLOCKED_METERED_REASON_USER_RESTRICTED = 131072; // 0x20000
|
||||
field public static final int BLOCKED_REASON_APP_STANDBY = 4; // 0x4
|
||||
field public static final int BLOCKED_REASON_BATTERY_SAVER = 1; // 0x1
|
||||
field public static final int BLOCKED_REASON_DOZE = 2; // 0x2
|
||||
field public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; // 0x10
|
||||
field public static final int BLOCKED_REASON_NONE = 0; // 0x0
|
||||
field public static final int BLOCKED_REASON_RESTRICTED_MODE = 8; // 0x8
|
||||
field public static final String PRIVATE_DNS_MODE_OFF = "off";
|
||||
@@ -41,6 +43,10 @@ package android.net {
|
||||
field public static final int PROFILE_NETWORK_PREFERENCE_ENTERPRISE = 1; // 0x1
|
||||
}
|
||||
|
||||
public static class ConnectivityManager.NetworkCallback {
|
||||
method public void onBlockedStatusChanged(@NonNull android.net.Network, int);
|
||||
}
|
||||
|
||||
public class ConnectivitySettingsManager {
|
||||
method public static void clearGlobalProxy(@NonNull android.content.Context);
|
||||
method @Nullable public static String getCaptivePortalHttpUrl(@NonNull android.content.Context);
|
||||
|
||||
@@ -38,7 +38,9 @@ import android.annotation.SuppressLint;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -871,6 +873,17 @@ public class ConnectivityManager {
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public static final int BLOCKED_REASON_RESTRICTED_MODE = 1 << 3;
|
||||
|
||||
/**
|
||||
* Flag to indicate that an app is blocked because it is subject to an always-on VPN but the VPN
|
||||
* is not currently connected.
|
||||
*
|
||||
* @see DevicePolicyManager#setAlwaysOnVpnPackage(ComponentName, String, boolean)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public static final int BLOCKED_REASON_LOCKDOWN_VPN = 1 << 4;
|
||||
|
||||
/**
|
||||
* Flag to indicate that an app is subject to Data saver restrictions that would
|
||||
* result in its metered network access being blocked.
|
||||
@@ -914,6 +927,14 @@ public class ConnectivityManager {
|
||||
})
|
||||
public @interface BlockedReason {}
|
||||
|
||||
/**
|
||||
* Set of blocked reasons that are only applicable on metered networks.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
|
||||
public static final int BLOCKED_METERED_REASON_MASK = 0xffff0000;
|
||||
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562)
|
||||
private final IConnectivityManager mService;
|
||||
|
||||
@@ -3442,12 +3463,30 @@ public class ConnectivityManager {
|
||||
* @param blocked Whether access to the {@link Network} is blocked due to system policy.
|
||||
* @hide
|
||||
*/
|
||||
public void onAvailable(@NonNull Network network,
|
||||
public final void onAvailable(@NonNull Network network,
|
||||
@NonNull NetworkCapabilities networkCapabilities,
|
||||
@NonNull LinkProperties linkProperties, boolean blocked) {
|
||||
@NonNull LinkProperties linkProperties, @BlockedReason int 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.
|
||||
onAvailable(network, networkCapabilities, linkProperties, blocked != 0);
|
||||
onBlockedStatusChanged(network, blocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy variant of onAvailable that takes a boolean blocked reason.
|
||||
*
|
||||
* This method has never been public API, but it's not final, so there may be apps that
|
||||
* implemented it and rely on it being called. Do our best not to break them.
|
||||
* Note: such apps will also get a second call to onBlockedStatusChanged immediately after
|
||||
* this method is called. There does not seem to be a way to avoid this.
|
||||
* TODO: add a compat check to move apps off this method, and eventually stop calling it.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void onAvailable(@NonNull Network network,
|
||||
@NonNull NetworkCapabilities networkCapabilities,
|
||||
@NonNull LinkProperties linkProperties, boolean blocked) {
|
||||
onAvailable(network);
|
||||
if (!networkCapabilities.hasCapability(
|
||||
NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
|
||||
@@ -3455,7 +3494,7 @@ public class ConnectivityManager {
|
||||
}
|
||||
onCapabilitiesChanged(network, networkCapabilities);
|
||||
onLinkPropertiesChanged(network, linkProperties);
|
||||
onBlockedStatusChanged(network, blocked);
|
||||
// No call to onBlockedStatusChanged here. That is done by the caller.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3619,6 +3658,26 @@ public class ConnectivityManager {
|
||||
*/
|
||||
public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {}
|
||||
|
||||
/**
|
||||
* Called when access to the specified network is blocked or unblocked.
|
||||
*
|
||||
* If a NetworkCallback object implements this method,
|
||||
* {@link #onBlockedStatusChanged(Network, boolean)} will not be called.
|
||||
*
|
||||
* <p>Do NOT call {@link #getNetworkCapabilities(Network)} or
|
||||
* {@link #getLinkProperties(Network)} or other synchronous ConnectivityManager methods in
|
||||
* this callback as this is prone to race conditions : calling these methods while in a
|
||||
* callback may return an outdated or even a null object.
|
||||
*
|
||||
* @param network The {@link Network} whose blocked status has changed.
|
||||
* @param blocked The blocked status of this {@link Network}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi(client = MODULE_LIBRARIES)
|
||||
public void onBlockedStatusChanged(@NonNull Network network, @BlockedReason int blocked) {
|
||||
onBlockedStatusChanged(network, blocked != 0);
|
||||
}
|
||||
|
||||
private NetworkRequest networkRequest;
|
||||
private final int mFlags;
|
||||
}
|
||||
@@ -3733,7 +3792,7 @@ public class ConnectivityManager {
|
||||
case CALLBACK_AVAILABLE: {
|
||||
NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
|
||||
LinkProperties lp = getObject(message, LinkProperties.class);
|
||||
callback.onAvailable(network, cap, lp, message.arg1 != 0);
|
||||
callback.onAvailable(network, cap, lp, message.arg1);
|
||||
break;
|
||||
}
|
||||
case CALLBACK_LOSING: {
|
||||
@@ -3767,8 +3826,7 @@ public class ConnectivityManager {
|
||||
break;
|
||||
}
|
||||
case CALLBACK_BLK_CHANGED: {
|
||||
boolean blocked = message.arg1 != 0;
|
||||
callback.onBlockedStatusChanged(network, blocked);
|
||||
callback.onBlockedStatusChanged(network, message.arg1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTI
|
||||
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
|
||||
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
|
||||
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
|
||||
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
|
||||
import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
|
||||
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
|
||||
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
|
||||
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
|
||||
@@ -108,6 +110,7 @@ import android.net.ConnectionInfo;
|
||||
import android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
|
||||
import android.net.ConnectivityDiagnosticsManager.DataStallReport;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.ConnectivityManager.BlockedReason;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
import android.net.ConnectivityManager.RestrictBackgroundStatus;
|
||||
import android.net.ConnectivityResources;
|
||||
@@ -2338,15 +2341,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
private final NetworkPolicyCallback mPolicyCallback = new NetworkPolicyCallback() {
|
||||
@Override
|
||||
public void onUidBlockedReasonChanged(int uid, int blockedReasons) {
|
||||
public void onUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UID_BLOCKED_REASON_CHANGED,
|
||||
uid, blockedReasons));
|
||||
}
|
||||
};
|
||||
|
||||
void handleUidBlockedReasonChanged(int uid, int blockedReasons) {
|
||||
private void handleUidBlockedReasonChanged(int uid, @BlockedReason int blockedReasons) {
|
||||
maybeNotifyNetworkBlockedForNewState(uid, blockedReasons);
|
||||
mUidBlockedReasons.put(uid, blockedReasons);
|
||||
setUidBlockedReasons(uid, blockedReasons);
|
||||
}
|
||||
|
||||
private boolean checkAnyPermissionOf(String... permissions) {
|
||||
@@ -8052,12 +8055,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
|
||||
final boolean metered = nai.networkCapabilities.isMetered();
|
||||
boolean blocked;
|
||||
blocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
|
||||
blocked |= NetworkPolicyManager.isUidBlocked(
|
||||
mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE), metered);
|
||||
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
|
||||
final boolean vpnBlocked = isUidBlockedByVpn(nri.mAsUid, mVpnBlockedUidRanges);
|
||||
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE,
|
||||
getBlockedState(blockedReasons, metered, vpnBlocked));
|
||||
}
|
||||
|
||||
// Notify the requests on this NAI that the network is now lingered.
|
||||
@@ -8066,6 +8068,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
|
||||
}
|
||||
|
||||
private static int getBlockedState(int reasons, boolean metered, boolean vpnBlocked) {
|
||||
if (!metered) reasons &= ~BLOCKED_METERED_REASON_MASK;
|
||||
return vpnBlocked
|
||||
? reasons | BLOCKED_REASON_LOCKDOWN_VPN
|
||||
: reasons & ~BLOCKED_REASON_LOCKDOWN_VPN;
|
||||
}
|
||||
|
||||
private void setUidBlockedReasons(int uid, @BlockedReason int blockedReasons) {
|
||||
if (blockedReasons == BLOCKED_REASON_NONE) {
|
||||
mUidBlockedReasons.delete(uid);
|
||||
} else {
|
||||
mUidBlockedReasons.put(uid, blockedReasons);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify of the blocked state apps with a registered callback matching a given NAI.
|
||||
*
|
||||
@@ -8073,7 +8090,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
* 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 oldMetered True if the previous network capabilities were metered.
|
||||
* @param newMetered True if the current network capabilities are metered.
|
||||
* @param oldBlockedUidRanges list of UID ranges previously blocked by lockdown VPN.
|
||||
* @param newBlockedUidRanges list of UID ranges blocked by lockdown VPN.
|
||||
*/
|
||||
private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
|
||||
boolean newMetered, List<UidRange> oldBlockedUidRanges,
|
||||
@@ -8082,22 +8102,18 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
for (int i = 0; i < nai.numNetworkRequests(); i++) {
|
||||
NetworkRequest nr = nai.requestAt(i);
|
||||
NetworkRequestInfo nri = mNetworkRequests.get(nr);
|
||||
final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
|
||||
|
||||
oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
|
||||
newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
|
||||
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
|
||||
final boolean oldVpnBlocked = isUidBlockedByVpn(nri.mAsUid, oldBlockedUidRanges);
|
||||
final boolean newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
|
||||
? isUidBlockedByVpn(nri.mAsUid, newBlockedUidRanges)
|
||||
: oldVpnBlocked;
|
||||
|
||||
final int blockedReasons = mUidBlockedReasons.get(nri.mAsUid, BLOCKED_REASON_NONE);
|
||||
oldBlocked = oldVpnBlocked || NetworkPolicyManager.isUidBlocked(
|
||||
blockedReasons, oldMetered);
|
||||
newBlocked = newVpnBlocked || NetworkPolicyManager.isUidBlocked(
|
||||
blockedReasons, newMetered);
|
||||
|
||||
if (oldBlocked != newBlocked) {
|
||||
final int oldBlockedState = getBlockedState(blockedReasons, oldMetered, oldVpnBlocked);
|
||||
final int newBlockedState = getBlockedState(blockedReasons, newMetered, newVpnBlocked);
|
||||
if (oldBlockedState != newBlockedState) {
|
||||
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
|
||||
encodeBool(newBlocked));
|
||||
newBlockedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8107,25 +8123,23 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
* @param uid The uid for which the rules changed.
|
||||
* @param blockedReasons The reasons for why an uid is blocked.
|
||||
*/
|
||||
private void maybeNotifyNetworkBlockedForNewState(int uid, int blockedReasons) {
|
||||
private void maybeNotifyNetworkBlockedForNewState(int uid, @BlockedReason int blockedReasons) {
|
||||
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
|
||||
final boolean metered = nai.networkCapabilities.isMetered();
|
||||
final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
|
||||
final boolean oldBlocked, newBlocked;
|
||||
|
||||
oldBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
|
||||
mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered);
|
||||
newBlocked = vpnBlocked || NetworkPolicyManager.isUidBlocked(
|
||||
blockedReasons, metered);
|
||||
if (oldBlocked == newBlocked) {
|
||||
final int oldBlockedState = getBlockedState(
|
||||
mUidBlockedReasons.get(uid, BLOCKED_REASON_NONE), metered, vpnBlocked);
|
||||
final int newBlockedState = getBlockedState(blockedReasons, metered, vpnBlocked);
|
||||
if (oldBlockedState == newBlockedState) {
|
||||
continue;
|
||||
}
|
||||
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.mAsUid == uid) {
|
||||
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED, arg);
|
||||
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
|
||||
newBlockedState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,10 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyBoolean;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
@@ -7301,9 +7301,8 @@ public class ConnectivityServiceTest {
|
||||
assertNetworkInfo(TYPE_MOBILE, DetailedState.BLOCKED);
|
||||
assertExtraInfoFromCmBlocked(mCellNetworkAgent);
|
||||
|
||||
// ConnectivityService should cache it not to invoke the callback again.
|
||||
setBlockedReasonChanged(BLOCKED_METERED_REASON_USER_RESTRICTED);
|
||||
cellNetworkCallback.assertNoCallback();
|
||||
cellNetworkCallback.expectBlockedStatusCallback(true, mCellNetworkAgent);
|
||||
|
||||
setBlockedReasonChanged(BLOCKED_REASON_NONE);
|
||||
cellNetworkCallback.expectBlockedStatusCallback(false, mCellNetworkAgent);
|
||||
|
||||
Reference in New Issue
Block a user