Enforce background data flag, rules through netd.
Connect up netd penalty box through NMS, and enforce the existing
background data flag by putting all UIDs in penalty box. Allow
platform applications to have policy applied. Only dispatch unique
rules to netd, avoiding any repeats.
Bug: 4598463, 4965677
Change-Id: Ibf9beff998ba7a1ea92f5e2f7eeba7b483d4b918
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 21fad2c..593b2b7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -49,6 +49,8 @@
/** Reject traffic on metered networks. */
public static final int RULE_REJECT_METERED = 0x1;
+ private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
+
/**
* {@link Intent} action launched when user selects {@link NetworkPolicy}
* warning notification.
@@ -223,25 +225,27 @@
return false;
}
- final PackageManager pm = context.getPackageManager();
- final HashSet<Signature> systemSignature;
- try {
- systemSignature = Sets.newHashSet(
- pm.getPackageInfo("android", GET_SIGNATURES).signatures);
- } catch (NameNotFoundException e) {
- throw new RuntimeException("problem finding system signature", e);
- }
-
- try {
- // reject apps signed with system cert
- for (String packageName : pm.getPackagesForUid(uid)) {
- final HashSet<Signature> packageSignature = Sets.newHashSet(
- pm.getPackageInfo(packageName, GET_SIGNATURES).signatures);
- if (packageSignature.containsAll(systemSignature)) {
- return false;
- }
+ if (!ALLOW_PLATFORM_APP_POLICY) {
+ final PackageManager pm = context.getPackageManager();
+ final HashSet<Signature> systemSignature;
+ try {
+ systemSignature = Sets.newHashSet(
+ pm.getPackageInfo("android", GET_SIGNATURES).signatures);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException("problem finding system signature", e);
}
- } catch (NameNotFoundException e) {
+
+ try {
+ // reject apps signed with platform cert
+ for (String packageName : pm.getPackagesForUid(uid)) {
+ final HashSet<Signature> packageSignature = Sets.newHashSet(
+ pm.getPackageInfo(packageName, GET_SIGNATURES).signatures);
+ if (packageSignature.containsAll(systemSignature)) {
+ return false;
+ }
+ }
+ } catch (NameNotFoundException e) {
+ }
}
// nothing found above; we can apply policy to UID
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 047a265..c9b6121 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -210,9 +210,14 @@
NetworkStats getNetworkStatsUidDetail(int uid);
/**
- * Set an overall quota for a group of interfaces.
+ * Set quota for an interface.
*/
- void setInterfaceQuota(in String[] iface, long quota);
+ void setInterfaceQuota(String iface, long quota);
+
+ /**
+ * Remove quota for an interface.
+ */
+ void removeInterfaceQuota(String iface);
/**
* Control network activity of a UID over interfaces with a quota limit.
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 8d92a39..adc6570 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -37,9 +37,11 @@
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import java.io.BufferedReader;
import java.io.DataInputStream;
@@ -52,6 +54,7 @@
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
@@ -115,6 +118,11 @@
private ArrayList<INetworkManagementEventObserver> mObservers;
+ /** Set of interfaces with active quotas. */
+ private HashSet<String> mInterfaceQuota = Sets.newHashSet();
+ /** Set of UIDs with active reject rules. */
+ private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+
/**
* Constructs a new NetworkManagementService instance
*
@@ -919,35 +927,81 @@
}
@Override
- public void setInterfaceQuota(String[] iface, long quota)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_NETWORK_POLICY, "NetworkManagementService");
- try {
- // TODO: Add support for clubbing together multiple interfaces under
- // one quota. Will need support from the kernel and
- // BandwidthController to do this.
- mConnector.doCommand(
- String.format("bandwidth setquota %s %d", iface[0], quota));
- } catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating to native daemon to set Interface quota",
- e);
+ public void setInterfaceQuota(String iface, long quota) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mInterfaceQuota) {
+ if (mInterfaceQuota.contains(iface)) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
+
+ try {
+ // TODO: add support for quota shared across interfaces
+ mConnector.doCommand(command.toString());
+ mInterfaceQuota.add(iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
}
}
@Override
- public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
- throws IllegalStateException {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_NETWORK_POLICY, "NetworkManagementService");
- try {
- // TODO: Connect with BandwidthController
- // mConnector.doCommand("");
- } catch (NativeDaemonConnectorException e) {
- throw new IllegalStateException(
- "Error communicating to native daemon to set Interface quota",
- e);
+ public void removeInterfaceQuota(String iface) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mInterfaceQuota) {
+ if (!mInterfaceQuota.contains(iface)) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth removeiquota ").append(iface);
+
+ try {
+ // TODO: add support for quota shared across interfaces
+ mConnector.doCommand(command.toString());
+ mInterfaceQuota.remove(iface);
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
+ }
+ }
+
+ @Override
+ public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mUidRejectOnQuota) {
+ final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
+ if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+ // TODO: eventually consider throwing
+ return;
+ }
+
+ final StringBuilder command = new StringBuilder();
+ command.append("bandwidth");
+ if (rejectOnQuotaInterfaces) {
+ command.append(" addnaughtyapps");
+ } else {
+ command.append(" removenaughtyapps");
+ }
+ command.append(" ").append(uid);
+
+ try {
+ mConnector.doCommand(command.toString());
+ if (rejectOnQuotaInterfaces) {
+ mUidRejectOnQuota.put(uid, true);
+ } else {
+ mUidRejectOnQuota.delete(uid);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw new IllegalStateException("Error communicating to native daemon", e);
+ }
}
}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 8a93f43..1f2ec2c 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -25,6 +25,7 @@
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.*;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -57,6 +58,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.IConnectivityManager;
@@ -110,6 +113,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import libcore.io.IoUtils;
@@ -166,6 +170,7 @@
private final Object mRulesLock = new Object();
private boolean mScreenOn;
+ private boolean mBackgroundData;
/** Current policy for network templates. */
private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList();
@@ -194,13 +199,14 @@
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
+ // TODO: watch for package added broadcast to catch new UIDs.
+
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats,
INetworkManagementService networkManagement) {
// TODO: move to using cached NtpTrustedTime
- this(context, activityManager, powerManager, networkStats,
- networkManagement, new NtpTrustedTime(),
- getSystemDir());
+ this(context, activityManager, powerManager, networkStats, networkManagement,
+ new NtpTrustedTime(), getSystemDir());
}
private static File getSystemDir() {
@@ -215,7 +221,7 @@
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
mNetworkStats = checkNotNull(networkStats, "missing networkStats");
- mNetworkManagement = checkNotNull(networkManagement, "missing networkManagementService");
+ mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement");
mTime = checkNotNull(time, "missing TrustedTime");
mHandlerThread = new HandlerThread(TAG);
@@ -241,6 +247,7 @@
}
updateScreenOn();
+ updateBackgroundData(true);
try {
mActivityManager.registerProcessObserver(mProcessObserver);
@@ -266,11 +273,15 @@
final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
- // listen for warning polling events; currently dispatched by
+ // listen for stats update events
final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
mContext.registerReceiver(
mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
+ // listen for changes to background data flag
+ final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+ mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler);
+
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -353,6 +364,22 @@
};
/**
+ * Receiver that watches for
+ * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+ */
+ private BroadcastReceiver mBgReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and verified CONNECTIVITY_INTERNAL
+ // permission above.
+
+ synchronized (mRulesLock) {
+ updateBackgroundData(false);
+ }
+ }
+ };
+
+ /**
* Check {@link NetworkPolicy} against current {@link INetworkStatsService}
* to show visible notifications as needed.
*/
@@ -565,7 +592,7 @@
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- mMeteredIfaces.clear();
+ final HashSet<String> newMeteredIfaces = Sets.newHashSet();
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
@@ -595,50 +622,32 @@
if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
// remaining "quota" is based on usage in current cycle
final long quota = Math.max(0, policy.limitBytes - total);
- if (LOGD) {
- Slog.d(TAG, "Applying quota rules for ifaces=" + Arrays.toString(ifaces)
- + " LIMIT=" + policy.limitBytes + " TOTAL="
- + total + " QUOTA=" + quota);
+
+ if (ifaces.length > 1) {
+ // TODO: switch to shared quota once NMS supports
+ Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
}
- setQuotaOnIfaceList(ifaces, quota);
-
for (String iface : ifaces) {
- mMeteredIfaces.add(iface);
+ removeInterfaceQuota(iface);
+ setInterfaceQuota(iface, quota);
+ newMeteredIfaces.add(iface);
}
}
}
+ // remove quota on any trailing interfaces
+ for (String iface : mMeteredIfaces) {
+ if (!newMeteredIfaces.contains(iface)) {
+ removeInterfaceQuota(iface);
+ }
+ }
+ mMeteredIfaces = newMeteredIfaces;
+
final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
}
- private void setQuotaOnIfaceList(String[] ifaces, long quota) {
- try {
- mNetworkManagement.setInterfaceQuota(ifaces, quota);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "IllegalStateException in setQuotaOnIfaceList " + e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote Exception in setQuotaOnIfaceList " + e);
- }
- }
-
- private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
- // TODO: connect over to NMS
- // ndc bandwidth app <uid> naughty
- try {
- if (LOGD) {
- Slog.d(TAG, "setUidNetworkRules() with uid=" + uid
- + ", rejectOnQuotaInterfaces=" + rejectOnQuotaInterfaces);
- }
- mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
- } catch (IllegalStateException e) {
- Slog.e(TAG, "IllegalStateException in setUidNetworkRules " + e);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote Exception in setUidNetworkRules " + e);
- }
- }
-
/**
* Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
* have at least a default mobile policy defined.
@@ -962,6 +971,21 @@
}
}
+ private void updateBackgroundData(boolean systemReady) {
+ synchronized (mRulesLock) {
+ try {
+ mBackgroundData = mConnManager.getBackgroundDataSetting();
+ } catch (RemoteException e) {
+ }
+ if (systemReady && mBackgroundData) {
+ // typical behavior of background enabled during systemReady;
+ // no need to clear rules for all UIDs.
+ } else {
+ updateRulesForBackgroundDataLocked();
+ }
+ }
+ }
+
/**
* Update rules that might be changed by {@link #mScreenOn} value.
*/
@@ -976,6 +1000,33 @@
}
}
+ /**
+ * Update rules that might be changed by {@link #mBackgroundData} value.
+ */
+ private void updateRulesForBackgroundDataLocked() {
+ // update rules for all installed applications
+ final PackageManager pm = mContext.getPackageManager();
+ final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
+ for (ApplicationInfo app : apps) {
+ updateRulesForUidLocked(app.uid);
+ }
+
+ // and catch system UIDs
+ // TODO: keep in sync with android_filesystem_config.h
+ for (int uid = 1000; uid <= 1025; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 2000; uid <= 2002; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 3000; uid <= 3007; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ for (int uid = 9998; uid <= 9999; uid++) {
+ updateRulesForUidLocked(uid);
+ }
+ }
+
private void updateRulesForUidLocked(int uid) {
final int uidPolicy = getUidPolicy(uid);
final boolean uidForeground = isUidForeground(uid);
@@ -986,6 +1037,10 @@
// uid in background, and policy says to block metered data
uidRules = RULE_REJECT_METERED;
}
+ if (!uidForeground && !mBackgroundData) {
+ // uid in background, and global background disabled
+ uidRules = RULE_REJECT_METERED;
+ }
// TODO: only dispatch when rules actually change
@@ -1041,6 +1096,36 @@
}
};
+ private void setInterfaceQuota(String iface, long quota) {
+ try {
+ mNetworkManagement.setInterfaceQuota(iface, quota);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem setting interface quota", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting interface quota", e);
+ }
+ }
+
+ private void removeInterfaceQuota(String iface) {
+ try {
+ mNetworkManagement.removeInterfaceQuota(iface);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem removing interface quota", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem removing interface quota", e);
+ }
+ }
+
+ private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+ try {
+ mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ } catch (IllegalStateException e) {
+ Slog.e(TAG, "problem setting uid rules", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "problem setting uid rules", e);
+ }
+ }
+
private String getActiveSubscriberId() {
final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index af2e01c..b4ac987 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -179,6 +179,9 @@
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
expectTime(System.currentTimeMillis());
+ // default behavior is background data enabled
+ expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
+
replay();
mService.systemReady();
verifyAndReset();
@@ -257,6 +260,7 @@
public void testScreenChangesRules() throws Exception {
Future<Void> future;
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
@@ -264,6 +268,7 @@
verifyAndReset();
// push strict policy for foreground uid, verify ALLOW rule
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -272,6 +277,7 @@
// now turn screen off and verify REJECT rule
expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
+ expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
@@ -280,6 +286,7 @@
// and turn screen back on, verify ALLOW rule restored
expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
@@ -290,6 +297,7 @@
public void testPolicyNone() throws Exception {
Future<Void> future;
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
@@ -297,6 +305,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in foreground
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
@@ -304,6 +313,7 @@
verifyAndReset();
// POLICY_NONE should RULE_ALLOW in background
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
@@ -315,6 +325,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_ALLOW in background
+ expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -322,6 +333,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_ALLOW in foreground
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
@@ -329,6 +341,7 @@
verifyAndReset();
// POLICY_REJECT should RULE_REJECT in background
+ expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
@@ -340,6 +353,7 @@
Future<Void> future;
// POLICY_NONE should have RULE_ALLOW in background
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
@@ -348,6 +362,7 @@
verifyAndReset();
// adding POLICY_REJECT should cause RULE_REJECT
+ expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -355,6 +370,7 @@
verifyAndReset();
// removing POLICY_REJECT should return us to RULE_ALLOW
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
mService.setUidPolicy(UID_A, POLICY_NONE);
@@ -435,8 +451,9 @@
expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
.andReturn(stats).atLeastOnce();
- // expect that quota remaining should be 1536 bytes
- // TODO: write up NetworkManagementService mock
+ // TODO: consider making strongly ordered mock
+ expectRemoveInterfaceQuota(TEST_IFACE);
+ expectSetInterfaceQuota(TEST_IFACE, 1536L);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -451,6 +468,7 @@
Future<Void> future;
// POLICY_REJECT should RULE_REJECT in background
+ expectSetUidNetworkRules(UID_A, true);
future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
replay();
mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
@@ -458,6 +476,7 @@
verifyAndReset();
// uninstall should clear RULE_REJECT
+ expectSetUidNetworkRules(UID_A, false);
future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
replay();
final Intent intent = new Intent(ACTION_UID_REMOVED);
@@ -498,6 +517,22 @@
expectLastCall().anyTimes();
}
+ private void expectSetInterfaceQuota(String iface, long quota) throws Exception {
+ mNetworkManagement.setInterfaceQuota(iface, quota);
+ expectLastCall().atLeastOnce();
+ }
+
+ private void expectRemoveInterfaceQuota(String iface) throws Exception {
+ mNetworkManagement.removeInterfaceQuota(iface);
+ expectLastCall().atLeastOnce();
+ }
+
+ private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+ throws Exception {
+ mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+ expectLastCall().atLeastOnce();
+ }
+
private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
final FutureAnswer future = new FutureAnswer();
mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));