summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/ConnectivityService.java81
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java55
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java30
3 files changed, 121 insertions, 45 deletions
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 07855d9..aa3dfa6 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -19,7 +19,7 @@ package com.android.server;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import android.bluetooth.BluetoothTetheringDataTracker;
import android.content.ContentResolver;
@@ -71,6 +71,7 @@ import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.google.android.collect.Lists;
+import com.google.android.collect.Sets;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -78,8 +79,10 @@ import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
+import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -108,8 +111,12 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private Vpn mVpn;
+ /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
+ private Object mRulesLock = new Object();
/** Currently active network rules by UID. */
private SparseIntArray mUidRules = new SparseIntArray();
+ /** Set of ifaces that are costly. */
+ private HashSet<String> mMeteredIfaces = Sets.newHashSet();
/**
* Sometimes we want to refer to the individual network state
@@ -570,31 +577,35 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
/**
- * Check if UID is blocked from using the given {@link NetworkInfo}.
+ * Check if UID should be blocked from using the network represented by the
+ * given {@link NetworkStateTracker}.
*/
- private boolean isNetworkBlocked(NetworkInfo info, int uid) {
- synchronized (mUidRules) {
- // 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);
+ private boolean isNetworkBlocked(NetworkStateTracker tracker, int uid) {
+ final String iface = tracker.getLinkProperties().getInterfaceName();
- if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
- return true;
- }
+ final boolean networkCostly;
+ final int uidRules;
+ synchronized (mRulesLock) {
+ networkCostly = mMeteredIfaces.contains(iface);
+ uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ }
- // no restrictive rules; network is visible
- return false;
+ if (networkCostly && (uidRules & RULE_REJECT_METERED) != 0) {
+ return true;
}
+
+ // no restrictive rules; network is visible
+ return false;
}
/**
- * Return a filtered version of the given {@link NetworkInfo}, potentially
- * marked {@link DetailedState#BLOCKED} based on
- * {@link #isNetworkBlocked(NetworkInfo, int)}.
+ * Return a filtered {@link NetworkInfo}, potentially marked
+ * {@link DetailedState#BLOCKED} based on
+ * {@link #isNetworkBlocked(NetworkStateTracker, int)}.
*/
- private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
- if (isNetworkBlocked(info, uid)) {
+ private NetworkInfo getFilteredNetworkInfo(NetworkStateTracker tracker, int uid) {
+ NetworkInfo info = tracker.getNetworkInfo();
+ if (isNetworkBlocked(tracker, uid)) {
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
@@ -634,7 +645,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
if (isNetworkTypeValid(networkType)) {
final NetworkStateTracker tracker = mNetTrackers[networkType];
if (tracker != null) {
- info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ info = getFilteredNetworkInfo(tracker, uid);
}
}
return info;
@@ -645,10 +656,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
final ArrayList<NetworkInfo> result = Lists.newArrayList();
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
+ result.add(getFilteredNetworkInfo(tracker, uid));
}
}
}
@@ -685,10 +696,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
enforceAccessPermission();
final int uid = Binder.getCallingUid();
final ArrayList<NetworkState> result = Lists.newArrayList();
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
for (NetworkStateTracker tracker : mNetTrackers) {
if (tracker != null) {
- final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+ final NetworkInfo info = getFilteredNetworkInfo(tracker, uid);
result.add(new NetworkState(
info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
}
@@ -1139,15 +1150,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
@Override
- public void onRulesChanged(int uid, int uidRules) {
+ public void onUidRulesChanged(int uid, int uidRules) {
// only someone like NPMS should only be calling us
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
if (LOGD_RULES) {
- Slog.d(TAG, "onRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
+ Slog.d(TAG, "onUidRulesChanged(uid=" + uid + ", uidRules=" + uidRules + ")");
}
- synchronized (mUidRules) {
+ synchronized (mRulesLock) {
// skip update when we've already applied rules
final int oldRules = mUidRules.get(uid, RULE_ALLOW_ALL);
if (oldRules == uidRules) return;
@@ -1158,6 +1169,24 @@ public class ConnectivityService extends IConnectivityManager.Stub {
// TODO: dispatch into NMS to push rules towards kernel module
// TODO: notify UID when it has requested targeted updates
}
+
+ @Override
+ public void onMeteredIfacesChanged(String[] meteredIfaces) {
+ // only someone like NPMS should only be calling us
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ if (LOGD_RULES) {
+ Slog.d(TAG,
+ "onMeteredIfacesChanged(ifaces=" + Arrays.toString(meteredIfaces) + ")");
+ }
+
+ synchronized (mRulesLock) {
+ mMeteredIfaces.clear();
+ for (String iface : meteredIfaces) {
+ mMeteredIfaces.add(iface);
+ }
+ }
+ }
};
/**
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 9cbe82d..43f3c63 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -29,9 +29,9 @@ import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_LIMIT;
import static android.net.NetworkPolicyManager.ACTION_DATA_USAGE_WARNING;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
@@ -87,6 +87,7 @@ import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Objects;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -103,6 +104,7 @@ import java.net.ProtocolException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import libcore.io.IoUtils;
@@ -164,6 +166,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Current derived network rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
+ /** Set of ifaces that are metered. */
+ private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -536,6 +541,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
+ mMeteredIfaces.clear();
+
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
for (NetworkPolicy policy : rules.keySet()) {
@@ -566,8 +573,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// remaining "quota" is based on usage in current cycle
final long quota = Math.max(0, policy.limitBytes - total);
//kernelSetIfacesQuota(ifaces, quota);
+
+ for (String iface : ifaces) {
+ mMeteredIfaces.add(iface);
+ }
}
}
+
+ // dispatch changed rule to existing listeners
+ // TODO: dispatch outside of holding lock
+ final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+ final int length = mListeners.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ if (listener != null) {
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mListeners.finishBroadcast();
}
/**
@@ -754,17 +780,29 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
synchronized (mRulesLock) {
// dispatch any existing rules to new listeners
+ // TODO: dispatch outside of holding lock
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);
+ listener.onUidRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
}
+
+ // dispatch any metered ifaces to new listeners
+ // TODO: dispatch outside of holding lock
+ if (mMeteredIfaces.size() > 0) {
+ final String[] meteredIfaces = mMeteredIfaces.toArray(
+ new String[mMeteredIfaces.size()]);
+ try {
+ listener.onMeteredIfacesChanged(meteredIfaces);
+ } catch (RemoteException e) {
+ }
+ }
}
}
@@ -921,9 +959,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// 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_METERED_BACKGROUND) != 0) {
+ // uid in background, and policy says to block metered data
+ uidRules = RULE_REJECT_METERED;
}
// TODO: only dispatch when rules actually change
@@ -931,16 +969,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// record rule locally to dispatch to new listeners
mUidRules.put(uid, uidRules);
- final boolean rejectPaid = (uidRules & RULE_REJECT_PAID) != 0;
+ final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
//kernelSetUidRejectPaid(uid, rejectPaid);
// dispatch changed rule to existing listeners
+ // TODO: dispatch outside of holding lock
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);
+ listener.onUidRulesChanged(uid, uidRules);
} catch (RemoteException e) {
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index edccf6c..5cb1763 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -19,13 +19,14 @@ package com.android.server;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.TEMPLATE_WIFI;
import static org.easymock.EasyMock.anyInt;
+import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -182,7 +183,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
final Future<Intent> backgroundChanged = mServiceContext.nextBroadcastIntent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
backgroundChanged.get();
}
@@ -225,12 +226,12 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// now turn screen off and verify REJECT rule
expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
verifyAndReset();
@@ -260,9 +261,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
public void testPolicyReject() throws Exception {
// POLICY_REJECT should RULE_ALLOW in background
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
@@ -272,7 +273,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
verifyAndReset();
@@ -287,9 +288,9 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
- expectRulesChanged(UID_A, RULE_REJECT_PAID);
+ expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
- mService.setUidPolicy(UID_A, POLICY_REJECT_PAID_BACKGROUND);
+ mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
@@ -353,6 +354,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
state = new NetworkState[] { buildWifi() };
expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
expectTime(TIME_MAR_10 + elapsedRealtime);
+ expectMeteredIfacesChanged();
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
@@ -373,6 +375,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
// TODO: write up NetworkManagementService mock
expectClearNotifications();
+ expectMeteredIfacesChanged(TEST_IFACE);
replay();
setNetworkPolicies(new NetworkPolicy(TEMPLATE_WIFI, null, CYCLE_DAY, 1024L, 2048L));
@@ -411,7 +414,12 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
private void expectRulesChanged(int uid, int policy) throws Exception {
- mPolicyListener.onRulesChanged(eq(uid), eq(policy));
+ mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+ expectLastCall().atLeastOnce();
+ }
+
+ private void expectMeteredIfacesChanged(String... ifaces) throws Exception {
+ mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
expectLastCall().atLeastOnce();
}