Merge "Add new callback to inform blocking of network on specific uid."

This commit is contained in:
Junyu Lai
2018-10-19 15:20:48 +00:00
committed by Gerrit Code Review
4 changed files with 255 additions and 13 deletions

View File

@@ -2816,10 +2816,11 @@ public class ConnectivityManager {
* @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 @@ public class ConnectivityManager {
}
onCapabilitiesChanged(network, networkCapabilities);
onLinkPropertiesChanged(network, linkProperties);
onBlockedStatusChanged(network, blocked);
}
/**
@@ -2837,7 +2839,8 @@ public class ConnectivityManager {
* 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 class ConnectivityManager {
*/
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 class ConnectivityManager {
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 @@ public class ConnectivityManager {
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 @@ public class ConnectivityManager {
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 @@ public class ConnectivityManager {
callback.onNetworkResumed(network);
break;
}
case CALLBACK_BLK_CHANGED: {
boolean blocked = message.arg1 != 0;
callback.onBlockedStatusChanged(network, blocked);
}
}
}

View File

@@ -1589,4 +1589,14 @@ public final class NetworkCapabilities implements Parcelable {
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);
}
}

View File

@@ -202,7 +202,9 @@ public class NetworkInfo implements Parcelable {
* 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 @@ public class NetworkInfo implements Parcelable {
/**
* 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 @@ public class NetworkInfo implements Parcelable {
* 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 @@ public class NetworkInfo implements Parcelable {
/**
* 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;

View File

@@ -35,6 +35,8 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
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.ArrayList;
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 @@ public class ConnectivityService extends IConnectivityManager.Stub
@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 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 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 @@ public class ConnectivityService extends IConnectivityManager.Stub
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) {
@@ -1116,11 +1140,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +1169,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +1681,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +1700,50 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
};
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 +2199,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +3298,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +3892,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +5004,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +5147,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
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 +5166,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
break;
}
case ConnectivityManager.CALLBACK_BLK_CHANGED: {
msg.arg1 = arg1;
break;
}
}
msg.what = notificationType;
msg.setData(bundle);
@@ -5600,7 +5725,76 @@ public class ConnectivityService extends IConnectivityManager.Stub
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) {