Policy and rules work for ConnectivityManager.

Teach ConnectivityManager about UID-specific rules derived from policy,
such as rejecting network traffic on "paid" interfaces.  Calls that
return NetworkInfo now filter based on any REJECT rules in effect for
the calling UID.  (Added uid parameter if callers that still want all
interfaces.)

Changed NetworkPolicyManager to derive rules based on current policy
combined with PowerManager and ActivityManager status, which it passes
to ConnectivityService for eventual enforcement through netd.  When
rules change the usability of a NetworkInfo for a specific UID, it also
dispatches CONNECTIVITY_ACTION broadcasts to that UID.  Combined paid
and background policy together to match current working definition.

Change-Id: I797ea49439fcc487cfe2cbc16703d4b91ceb9af6
This commit is contained in:
Jeff Sharkey
2011-05-19 17:12:49 -07:00
parent 0d5916c21e
commit 921ebf2ee5
4 changed files with 178 additions and 51 deletions

View File

@@ -22,7 +22,6 @@ import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException;
/** /**
* Class that answers queries about the state of network connectivity. It also * Class that answers queries about the state of network connectivity. It also
@@ -40,8 +39,9 @@ import java.net.UnknownHostException;
* state of the available networks</li> * state of the available networks</li>
* </ol> * </ol>
*/ */
public class ConnectivityManager public class ConnectivityManager {
{ private static final String TAG = "ConnectivityManager";
/** /**
* A change in network connectivity has occurred. A connection has either * A change in network connectivity has occurred. A connection has either
* been established or lost. The NetworkInfo for the affected network is * been established or lost. The NetworkInfo for the affected network is
@@ -109,7 +109,7 @@ public class ConnectivityManager
* The lookup key for an int that provides information about * The lookup key for an int that provides information about
* our connection to the internet at large. 0 indicates no connection, * our connection to the internet at large. 0 indicates no connection,
* 100 indicates a great connection. Retrieve it with * 100 indicates a great connection. Retrieve it with
* {@link android.content.Intent@getIntExtra(String)}. * {@link android.content.Intent#getIntExtra(String, int)}.
* {@hide} * {@hide}
*/ */
public static final String EXTRA_INET_CONDITION = "inetCondition"; public static final String EXTRA_INET_CONDITION = "inetCondition";
@@ -120,13 +120,12 @@ public class ConnectivityManager
* <p> * <p>
* If an application uses the network in the background, it should listen * If an application uses the network in the background, it should listen
* for this broadcast and stop using the background data if the value is * for this broadcast and stop using the background data if the value is
* false. * {@code false}.
*/ */
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED =
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
/** /**
* Broadcast Action: The network connection may not be good * Broadcast Action: The network connection may not be good
* uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and * uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and
@@ -255,7 +254,7 @@ public class ConnectivityManager
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI; public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
private IConnectivityManager mService; private final IConnectivityManager mService;
static public boolean isNetworkTypeValid(int networkType) { static public boolean isNetworkTypeValid(int networkType) {
return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
@@ -284,6 +283,15 @@ public class ConnectivityManager
} }
} }
/** {@hide} */
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
try {
return mService.getActiveNetworkInfoForUid(uid);
} catch (RemoteException e) {
return null;
}
}
public NetworkInfo getNetworkInfo(int networkType) { public NetworkInfo getNetworkInfo(int networkType) {
try { try {
return mService.getNetworkInfo(networkType); return mService.getNetworkInfo(networkType);
@@ -300,7 +308,7 @@ public class ConnectivityManager
} }
} }
/** @hide */ /** {@hide} */
public LinkProperties getActiveLinkProperties() { public LinkProperties getActiveLinkProperties() {
try { try {
return mService.getActiveLinkProperties(); return mService.getActiveLinkProperties();
@@ -309,7 +317,7 @@ public class ConnectivityManager
} }
} }
/** @hide */ /** {@hide} */
public LinkProperties getLinkProperties(int networkType) { public LinkProperties getLinkProperties(int networkType) {
try { try {
return mService.getLinkProperties(networkType); return mService.getLinkProperties(networkType);
@@ -478,20 +486,12 @@ public class ConnectivityManager
} }
} }
/**
* Don't allow use of default constructor.
*/
@SuppressWarnings({"UnusedDeclaration"})
private ConnectivityManager() {
}
/** /**
* {@hide} * {@hide}
*/ */
public ConnectivityManager(IConnectivityManager service) { public ConnectivityManager(IConnectivityManager service) {
if (service == null) { if (service == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException("missing IConnectivityManager");
"ConnectivityManager() cannot be constructed with null service");
} }
mService = service; mService = service;
} }

View File

@@ -33,13 +33,11 @@ interface IConnectivityManager
int getNetworkPreference(); int getNetworkPreference();
NetworkInfo getActiveNetworkInfo(); NetworkInfo getActiveNetworkInfo();
NetworkInfo getActiveNetworkInfoForUid(int uid);
NetworkInfo getNetworkInfo(int networkType); NetworkInfo getNetworkInfo(int networkType);
NetworkInfo[] getAllNetworkInfo(); NetworkInfo[] getAllNetworkInfo();
LinkProperties getActiveLinkProperties(); LinkProperties getActiveLinkProperties();
LinkProperties getLinkProperties(int networkType); LinkProperties getLinkProperties(int networkType);
boolean setRadios(boolean onOff); boolean setRadios(boolean onOff);

View File

@@ -74,7 +74,9 @@ public class NetworkInfo implements Parcelable {
/** IP traffic not available. */ /** IP traffic not available. */
DISCONNECTED, DISCONNECTED,
/** Attempt to connect failed. */ /** Attempt to connect failed. */
FAILED FAILED,
/** Access to this network is blocked. */
BLOCKED
} }
/** /**
@@ -96,6 +98,7 @@ public class NetworkInfo implements Parcelable {
stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING); stateMap.put(DetailedState.DISCONNECTING, State.DISCONNECTING);
stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED); stateMap.put(DetailedState.DISCONNECTED, State.DISCONNECTED);
stateMap.put(DetailedState.FAILED, State.DISCONNECTED); stateMap.put(DetailedState.FAILED, State.DISCONNECTED);
stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED);
} }
private int mNetworkType; private int mNetworkType;
@@ -138,6 +141,23 @@ public class NetworkInfo implements Parcelable {
mIsRoaming = false; mIsRoaming = false;
} }
/** {@hide} */
public NetworkInfo(NetworkInfo source) {
if (source != null) {
mNetworkType = source.mNetworkType;
mSubtype = source.mSubtype;
mTypeName = source.mTypeName;
mSubtypeName = source.mSubtypeName;
mState = source.mState;
mDetailedState = source.mDetailedState;
mReason = source.mReason;
mExtraInfo = source.mExtraInfo;
mIsFailover = source.mIsFailover;
mIsRoaming = source.mIsRoaming;
mIsAvailable = source.mIsAvailable;
}
}
/** /**
* Reports the type of network (currently mobile or Wi-Fi) to which the * Reports the type of network (currently mobile or Wi-Fi) to which the
* info in this object pertains. * info in this object pertains.

View File

@@ -16,6 +16,11 @@
package com.android.server; package com.android.server;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
import android.bluetooth.BluetoothTetheringDataTracker; import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -26,11 +31,13 @@ import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker; import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker; import android.net.EthernetDataTracker;
import android.net.IConnectivityManager; import android.net.IConnectivityManager;
import android.net.LinkAddress; import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.MobileDataStateTracker; import android.net.MobileDataStateTracker;
import android.net.NetworkConfig; import android.net.NetworkConfig;
import android.net.NetworkInfo; import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkStateTracker; import android.net.NetworkStateTracker;
import android.net.NetworkUtils; import android.net.NetworkUtils;
import android.net.Proxy; import android.net.Proxy;
@@ -54,6 +61,7 @@ import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.EventLog; import android.util.EventLog;
import android.util.Slog; import android.util.Slog;
import android.util.SparseIntArray;
import com.android.internal.telephony.Phone; import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Tethering;
@@ -62,13 +70,12 @@ import java.io.FileDescriptor;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* @hide * @hide
@@ -78,6 +85,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final boolean DBG = true; private static final boolean DBG = true;
private static final String TAG = "ConnectivityService"; private static final String TAG = "ConnectivityService";
private static final boolean LOGD_RULES = false;
// how long to wait before switching back to a radio's default network // how long to wait before switching back to a radio's default network
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000; private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
// system property that can override the above value // system property that can override the above value
@@ -91,6 +100,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering; private Tethering mTethering;
private boolean mTetheringConfigValid = false; private boolean mTetheringConfigValid = false;
/** Currently active network rules by UID. */
private SparseIntArray mUidRules = new SparseIntArray();
/** /**
* Sometimes we want to refer to the individual network state * Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them * trackers separately, and sometimes we just want to treat them
@@ -128,6 +140,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true); private AtomicBoolean mBackgroundDataEnabled = new AtomicBoolean(true);
private INetworkManagementService mNetd; private INetworkManagementService mNetd;
private INetworkPolicyManager mPolicyManager;
private static final int ENABLED = 1; private static final int ENABLED = 1;
private static final int DISABLED = 0; private static final int DISABLED = 0;
@@ -250,14 +263,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} }
RadioAttributes[] mRadioAttributes; RadioAttributes[] mRadioAttributes;
public static synchronized ConnectivityService getInstance(Context context) { public ConnectivityService(
if (sServiceInstance == null) { Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
sServiceInstance = new ConnectivityService(context);
}
return sServiceInstance;
}
private ConnectivityService(Context context) {
if (DBG) log("ConnectivityService starting up"); if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread"); HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -290,9 +297,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
loge("Error setting defaultDns using " + dns); loge("Error setting defaultDns using " + dns);
} }
mContext = context; mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netd, "missing INetworkManagementService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); try {
mPolicyManager.registerListener(mPolicyListener);
} catch (RemoteException e) {
// ouch, no rules updates means some processes may never get network
Slog.e(TAG, "unable to register INetworkPolicyListener", e);
}
final PowerManager powerManager = (PowerManager) context.getSystemService(
Context.POWER_SERVICE);
mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
com.android.internal.R.integer.config_networkTransitionTimeout); com.android.internal.R.integer.config_networkTransitionTimeout);
@@ -535,6 +552,32 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} }
} }
/**
* Check if UID is blocked from using the given {@link NetworkInfo}.
*/
private boolean isNetworkBlocked(NetworkInfo info, int uid) {
synchronized (mUidRules) {
return isNetworkBlockedLocked(info, uid);
}
}
/**
* Check if UID is blocked from using the given {@link NetworkInfo}.
*/
private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
// TODO: expand definition of "paid" network to cover tethered or paid
// hotspot use cases.
final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
return true;
}
// no restrictive rules; network is visible
return false;
}
/** /**
* Return NetworkInfo for the active (i.e., connected) network interface. * Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more * It is assumed that at most one network is active at a time. If more
@@ -542,26 +585,60 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return the info for the active network, or {@code null} if none is * @return the info for the active network, or {@code null} if none is
* active * active
*/ */
@Override
public NetworkInfo getActiveNetworkInfo() { public NetworkInfo getActiveNetworkInfo() {
return getNetworkInfo(mActiveDefaultNetwork); enforceAccessPermission();
final int uid = Binder.getCallingUid();
return getNetworkInfo(mActiveDefaultNetwork, uid);
} }
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid) {
enforceConnectivityInternalPermission();
return getNetworkInfo(mActiveDefaultNetwork, uid);
}
@Override
public NetworkInfo getNetworkInfo(int networkType) { public NetworkInfo getNetworkInfo(int networkType) {
enforceAccessPermission(); enforceAccessPermission();
if (ConnectivityManager.isNetworkTypeValid(networkType)) { final int uid = Binder.getCallingUid();
NetworkStateTracker t = mNetTrackers[networkType]; return getNetworkInfo(networkType, uid);
if (t != null)
return t.getNetworkInfo();
}
return null;
} }
private NetworkInfo getNetworkInfo(int networkType, int uid) {
NetworkInfo info = null;
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
info = tracker.getNetworkInfo();
if (isNetworkBlocked(info, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
}
}
}
return info;
}
@Override
public NetworkInfo[] getAllNetworkInfo() { public NetworkInfo[] getAllNetworkInfo() {
enforceAccessPermission(); enforceAccessPermission();
NetworkInfo[] result = new NetworkInfo[mNetworksDefined]; final int uid = Binder.getCallingUid();
final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
int i = 0; int i = 0;
for (NetworkStateTracker t : mNetTrackers) { synchronized (mUidRules) {
if(t != null) result[i++] = t.getNetworkInfo(); for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
NetworkInfo info = tracker.getNetworkInfo();
if (isNetworkBlockedLocked(info, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
}
result[i++] = info;
}
}
} }
return result; return result;
} }
@@ -574,15 +651,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return the ip properties for the active network, or {@code null} if * @return the ip properties for the active network, or {@code null} if
* none is active * none is active
*/ */
@Override
public LinkProperties getActiveLinkProperties() { public LinkProperties getActiveLinkProperties() {
return getLinkProperties(mActiveDefaultNetwork); return getLinkProperties(mActiveDefaultNetwork);
} }
@Override
public LinkProperties getLinkProperties(int networkType) { public LinkProperties getLinkProperties(int networkType) {
enforceAccessPermission(); enforceAccessPermission();
if (ConnectivityManager.isNetworkTypeValid(networkType)) { if (isNetworkTypeValid(networkType)) {
NetworkStateTracker t = mNetTrackers[networkType]; final NetworkStateTracker tracker = mNetTrackers[networkType];
if (t != null) return t.getLinkProperties(); if (tracker != null) {
return tracker.getLinkProperties();
}
} }
return null; return null;
} }
@@ -1027,6 +1108,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} }
} }
private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
@Override
public void onRulesChanged(int uid, int uidRules) {
// only someone like NPMS should only be calling us
// TODO: create permission for modifying data policy
mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
if (LOGD_RULES) {
Slog.d(TAG, "onRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
}
synchronized (mUidRules) {
// skip update when we've already applied rules
final int oldRules = mUidRules.get(uid, RULE_ALLOW_ALL);
if (oldRules == uidRules) return;
mUidRules.put(uid, uidRules);
}
// TODO: dispatch into NMS to push rules towards kernel module
// TODO: notify UID when it has requested targeted updates
}
};
/** /**
* @see ConnectivityManager#setMobileDataEnabled(boolean) * @see ConnectivityManager#setMobileDataEnabled(boolean)
*/ */
@@ -1284,9 +1389,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} }
void systemReady() { void systemReady() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNetd = INetworkManagementService.Stub.asInterface(b);
synchronized(this) { synchronized(this) {
mSystemReady = true; mSystemReady = true;
if (mInitialBroadcast != null) { if (mInitialBroadcast != null) {
@@ -2255,4 +2357,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
} }
return networkType; return networkType;
} }
private static <T> T checkNotNull(T value, String message) {
if (value == null) {
throw new NullPointerException(message);
}
return value;
}
} }