diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | core/java/android/net/ConnectivityManager.java | 36 | ||||
-rw-r--r-- | core/java/android/net/IConnectivityManager.aidl | 4 | ||||
-rw-r--r-- | core/java/android/net/INetworkPolicyListener.aidl | 24 | ||||
-rw-r--r-- | core/java/android/net/INetworkPolicyManager.aidl | 5 | ||||
-rw-r--r-- | core/java/android/net/NetworkInfo.java | 22 | ||||
-rw-r--r-- | core/java/android/net/NetworkPolicyManager.java | 19 | ||||
-rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 165 | ||||
-rw-r--r-- | services/java/com/android/server/SystemServer.java | 38 | ||||
-rw-r--r-- | services/java/com/android/server/net/NetworkPolicyManagerService.java | 90 |
11 files changed, 310 insertions, 95 deletions
@@ -110,6 +110,7 @@ LOCAL_SRC_FILES += \ core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/IThrottleManager.aidl \ + core/java/android/net/INetworkPolicyListener.aidl \ core/java/android/net/INetworkPolicyManager.aidl \ core/java/android/nfc/ILlcpConnectionlessSocket.aidl \ core/java/android/nfc/ILlcpServiceSocket.aidl \ diff --git a/api/current.txt b/api/current.txt index 9e87e9c..a1d4c4a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11041,6 +11041,7 @@ package android.net { method public static android.net.NetworkInfo.DetailedState valueOf(java.lang.String); method public static final android.net.NetworkInfo.DetailedState[] values(); enum_constant public static final android.net.NetworkInfo.DetailedState AUTHENTICATING; + enum_constant public static final android.net.NetworkInfo.DetailedState BLOCKED; enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTED; enum_constant public static final android.net.NetworkInfo.DetailedState CONNECTING; enum_constant public static final android.net.NetworkInfo.DetailedState DISCONNECTED; diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 419288b..c72c4b0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -22,7 +22,6 @@ import android.os.Binder; import android.os.RemoteException; import java.net.InetAddress; -import java.net.UnknownHostException; /** * 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> * </ol> */ -public class ConnectivityManager -{ +public class ConnectivityManager { + private static final String TAG = "ConnectivityManager"; + /** * A change in network connectivity has occurred. A connection has either * 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 * our connection to the internet at large. 0 indicates no connection, * 100 indicates a great connection. Retrieve it with - * {@link android.content.Intent@getIntExtra(String)}. + * {@link android.content.Intent#getIntExtra(String, int)}. * {@hide} */ public static final String EXTRA_INET_CONDITION = "inetCondition"; @@ -120,13 +120,12 @@ public class ConnectivityManager * <p> * 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 - * false. + * {@code false}. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; - /** * Broadcast Action: The network connection may not be good * uses {@code ConnectivityManager.EXTRA_INET_CONDITION} and @@ -255,7 +254,7 @@ public class ConnectivityManager public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI; - private IConnectivityManager mService; + private final IConnectivityManager mService; static public boolean isNetworkTypeValid(int networkType) { 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) { try { return mService.getNetworkInfo(networkType); @@ -300,7 +308,7 @@ public class ConnectivityManager } } - /** @hide */ + /** {@hide} */ public LinkProperties getActiveLinkProperties() { try { return mService.getActiveLinkProperties(); @@ -309,7 +317,7 @@ public class ConnectivityManager } } - /** @hide */ + /** {@hide} */ public LinkProperties getLinkProperties(int networkType) { try { return mService.getLinkProperties(networkType); @@ -479,19 +487,11 @@ public class ConnectivityManager } /** - * Don't allow use of default constructor. - */ - @SuppressWarnings({"UnusedDeclaration"}) - private ConnectivityManager() { - } - - /** * {@hide} */ public ConnectivityManager(IConnectivityManager service) { if (service == null) { - throw new IllegalArgumentException( - "ConnectivityManager() cannot be constructed with null service"); + throw new IllegalArgumentException("missing IConnectivityManager"); } mService = service; } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 8be492c..647a60a 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -33,13 +33,11 @@ interface IConnectivityManager int getNetworkPreference(); NetworkInfo getActiveNetworkInfo(); - + NetworkInfo getActiveNetworkInfoForUid(int uid); NetworkInfo getNetworkInfo(int networkType); - NetworkInfo[] getAllNetworkInfo(); LinkProperties getActiveLinkProperties(); - LinkProperties getLinkProperties(int networkType); boolean setRadios(boolean onOff); diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl new file mode 100644 index 0000000..9230151 --- /dev/null +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +/** {@hide} */ +oneway interface INetworkPolicyListener { + + void onRulesChanged(int uid, int uidRules); + +} diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index d9351ee..24f2283 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -16,6 +16,8 @@ package android.net; +import android.net.INetworkPolicyListener; + /** * Interface that creates and modifies network policy rules. * @@ -26,6 +28,9 @@ interface INetworkPolicyManager { void setUidPolicy(int uid, int policy); int getUidPolicy(int uid); + void registerListener(INetworkPolicyListener listener); + void unregisterListener(INetworkPolicyListener listener); + // TODO: build API to surface stats details for settings UI } diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java index 5f5e11c..537750a 100644 --- a/core/java/android/net/NetworkInfo.java +++ b/core/java/android/net/NetworkInfo.java @@ -74,7 +74,9 @@ public class NetworkInfo implements Parcelable { /** IP traffic not available. */ DISCONNECTED, /** 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.DISCONNECTED, State.DISCONNECTED); stateMap.put(DetailedState.FAILED, State.DISCONNECTED); + stateMap.put(DetailedState.BLOCKED, State.DISCONNECTED); } private int mNetworkType; @@ -138,6 +141,23 @@ public class NetworkInfo implements Parcelable { 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 * info in this object pertains. diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 1913aa7..0851b12 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -28,12 +28,13 @@ public class NetworkPolicyManager { /** No specific network policy, use system default. */ public static final int POLICY_NONE = 0x0; - /** Reject network usage when application in background. */ - public static final int POLICY_REJECT_BACKGROUND = 0x1; - /** Reject network usage on paid network connections. */ - public static final int POLICY_REJECT_PAID = 0x2; - /** Application should conserve data. */ - public static final int POLICY_CONSERVE_DATA = 0x4; + /** Reject network usage on paid networks when application in background. */ + public static final int POLICY_REJECT_PAID_BACKGROUND = 0x1; + + /** All network traffic should be allowed. */ + public static final int RULE_ALLOW_ALL = 0x0; + /** Reject traffic on paid networks. */ + public static final int RULE_REJECT_PAID = 0x1; private INetworkPolicyManager mService; @@ -51,9 +52,8 @@ public class NetworkPolicyManager { /** * Set policy flags for specific UID. * - * @param policy {@link #POLICY_NONE} or combination of - * {@link #POLICY_REJECT_BACKGROUND}, {@link #POLICY_REJECT_PAID}, - * or {@link #POLICY_CONSERVE_DATA}. + * @param policy {@link #POLICY_NONE} or combination of flags like + * {@link #POLICY_REJECT_PAID_BACKGROUND}. */ public void setUidPolicy(int uid, int policy) { try { @@ -69,5 +69,4 @@ public class NetworkPolicyManager { return POLICY_NONE; } } - } diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index a564c2d..dd76eb8 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -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); @@ -536,32 +553,92 @@ 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 * than one is active, it is indeterminate which will be returned. * @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(); + 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 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; + } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 5355d44..4cd601f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -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); diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index d083d01..0f31f81 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -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) { |