Merge "Policy and rules work for ConnectivityManager."

This commit is contained in:
Jeff Sharkey
2011-06-01 17:18:15 -07:00
committed by Android (Google) Code Review
11 changed files with 311 additions and 96 deletions

View File

@@ -16,6 +16,11 @@
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.content.ContentResolver;
import android.content.Context;
@@ -26,11 +31,13 @@ import android.net.ConnectivityManager;
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
import android.net.LinkAddress;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.LinkProperties;
import android.net.MobileDataStateTracker;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkStateTracker;
import android.net.NetworkUtils;
import android.net.Proxy;
@@ -54,6 +61,7 @@ import android.provider.Settings;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseIntArray;
import com.android.internal.telephony.Phone;
import com.android.server.connectivity.Tethering;
@@ -62,13 +70,12 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @hide
@@ -78,6 +85,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private static final boolean DBG = true;
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
private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000;
// system property that can override the above value
@@ -91,6 +100,9 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Tethering mTethering;
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
* 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 INetworkManagementService mNetd;
private INetworkPolicyManager mPolicyManager;
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -250,14 +263,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
RadioAttributes[] mRadioAttributes;
public static synchronized ConnectivityService getInstance(Context context) {
if (sServiceInstance == null) {
sServiceInstance = new ConnectivityService(context);
}
return sServiceInstance;
}
private ConnectivityService(Context context) {
public ConnectivityService(
Context context, INetworkManagementService netd, INetworkPolicyManager policyManager) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
@@ -290,9 +297,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
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);
mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
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.
* 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
* active
*/
@Override
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) {
enforceAccessPermission();
if (ConnectivityManager.isNetworkTypeValid(networkType)) {
NetworkStateTracker t = mNetTrackers[networkType];
if (t != null)
return t.getNetworkInfo();
}
return null;
final int uid = Binder.getCallingUid();
return getNetworkInfo(networkType, uid);
}
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() {
enforceAccessPermission();
NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
final int uid = Binder.getCallingUid();
final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
int i = 0;
for (NetworkStateTracker t : mNetTrackers) {
if(t != null) result[i++] = t.getNetworkInfo();
synchronized (mUidRules) {
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;
}
@@ -574,15 +651,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
* @return the ip properties for the active network, or {@code null} if
* none is active
*/
@Override
public LinkProperties getActiveLinkProperties() {
return getLinkProperties(mActiveDefaultNetwork);
}
@Override
public LinkProperties getLinkProperties(int networkType) {
enforceAccessPermission();
if (ConnectivityManager.isNetworkTypeValid(networkType)) {
NetworkStateTracker t = mNetTrackers[networkType];
if (t != null) return t.getLinkProperties();
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
return tracker.getLinkProperties();
}
}
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)
*/
@@ -1284,9 +1389,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
void systemReady() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNetd = INetworkManagementService.Stub.asInterface(b);
synchronized(this) {
mSystemReady = true;
if (mInitialBroadcast != null) {
@@ -2255,4 +2357,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
return networkType;
}
private static <T> T checkNotNull(T value, String message) {
if (value == null) {
throw new NullPointerException(message);
}
return value;
}
}

View File

@@ -16,19 +16,6 @@
package com.android.server;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
import android.accounts.AccountManagerService;
import android.app.ActivityManagerNative;
import android.bluetooth.BluetoothAdapter;
@@ -41,25 +28,34 @@ import android.content.pm.IPackageManager;
import android.content.res.Configuration;
import android.database.ContentObserver;
import android.media.AudioService;
import android.os.Build;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Contacts.People;
import android.provider.Settings;
import android.server.BluetoothA2dpService;
import android.server.BluetoothService;
import android.server.search.SearchManagerService;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.WindowManager;
import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.am.ActivityManagerService;
import com.android.server.net.NetworkPolicyManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.usb.UsbService;
import com.android.server.wm.WindowManagerService;
import dalvik.system.VMRuntime;
import dalvik.system.Zygote;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
@@ -120,6 +116,7 @@ class ServerThread extends Thread {
LightsService lights = null;
PowerManagerService power = null;
BatteryService battery = null;
NetworkManagementService networkManagement = null;
NetworkPolicyManagerService networkPolicy = null;
ConnectivityService connectivity = null;
IPackageManager pm = null;
@@ -294,16 +291,15 @@ class ServerThread extends Thread {
try {
Slog.i(TAG, "NetworkManagement Service");
ServiceManager.addService(
Context.NETWORKMANAGEMENT_SERVICE,
NetworkManagementService.create(context));
networkManagement = NetworkManagementService.create(context);
ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting NetworkManagement Service", e);
}
try {
Slog.i(TAG, "Connectivity Service");
connectivity = ConnectivityService.getInstance(context);
connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Connectivity Service", e);

View File

@@ -19,8 +19,9 @@ package com.android.server.net;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
import android.app.IActivityManager;
import android.app.IProcessObserver;
@@ -28,8 +29,11 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.os.IPowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import android.util.Slog;
@@ -40,6 +44,10 @@ import android.util.SparseIntArray;
/**
* Service that maintains low-level network policy rules and collects usage
* statistics to drive those rules.
* <p>
* Derives active rules by combining a given policy with other system status,
* and delivers to listeners, such as {@link ConnectivityManager}, for
* enforcement.
*/
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG = "NetworkPolicy";
@@ -51,19 +59,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private Object mRulesLock = new Object();
private boolean mScreenOn = false;
private boolean mScreenOn;
/** Current network policy for each UID. */
private SparseIntArray mUidPolicy = new SparseIntArray();
/** Current derived network rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
SparseBooleanArray>();
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
INetworkPolicyListener>();
// TODO: periodically poll network stats and write to disk
// TODO: save/restore policy information from disk
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
public NetworkPolicyManagerService(
Context context, IActivityManager activityManager, IPowerManager powerManager) {
mContext = checkNotNull(context, "missing context");
@@ -158,12 +174,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
@Override
public void setUidPolicy(int uid, int policy) {
// TODO: create permission for modifying data policy
mContext.enforceCallingOrSelfPermission(
UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
final int oldPolicy;
synchronized (mRulesLock) {
oldPolicy = getUidPolicy(uid);
mUidPolicy.put(uid, policy);
}
// TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
}
@Override
@@ -173,6 +194,36 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
@Override
public void registerListener(INetworkPolicyListener listener) {
mListeners.register(listener);
synchronized (mRulesLock) {
// dispatch any existing rules to new listeners
final int size = mUidRules.size();
for (int i = 0; i < size; i++) {
final int uid = mUidRules.keyAt(i);
final int uidRules = mUidRules.valueAt(i);
if (uidRules != RULE_ALLOW_ALL) {
try {
listener.onRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
}
}
}
@Override
public void unregisterListener(INetworkPolicyListener listener) {
mListeners.unregister(listener);
}
private boolean isUidForegroundL(int uid) {
// only really in foreground when screen is also on
return mUidForeground.get(uid, false) && mScreenOn;
}
/**
* Foreground for PID changed; recompute foreground at UID level. If
* changed, will trigger {@link #updateRulesForUidL(int)}.
@@ -223,22 +274,33 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
private void updateRulesForUidL(int uid) {
// only really in foreground when screen on
final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn;
final int uidPolicy = getUidPolicy(uid);
final boolean uidForeground = isUidForegroundL(uid);
if (LOGD) {
Log.d(TAG, "updateRulesForUid(uid=" + uid + ") found foreground=" + uidForeground
+ " and policy=" + uidPolicy);
// derive active rules based on policy and active state
int uidRules = RULE_ALLOW_ALL;
if (!uidForeground && (uidPolicy & POLICY_REJECT_PAID_BACKGROUND) != 0) {
// uid in background, and policy says to block paid data
uidRules = RULE_REJECT_PAID;
}
if (!uidForeground && (uidPolicy & POLICY_REJECT_BACKGROUND) != 0) {
// TODO: build updated rules and push to NMS
} else if ((uidPolicy & POLICY_REJECT_PAID) != 0) {
// TODO: build updated rules and push to NMS
} else {
// TODO: build updated rules and push to NMS
// TODO: only dispatch when rules actually change
// record rule locally to dispatch to new listeners
mUidRules.put(uid, uidRules);
// dispatch changed rule to existing listeners
final int length = mListeners.beginBroadcast();
for (int i = 0; i < length; i++) {
final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
if (listener != null) {
try {
listener.onRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
}
mListeners.finishBroadcast();
}
private static <T> T checkNotNull(T value, String message) {