summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl4
-rw-r--r--core/java/android/net/INetworkStatsService.aidl2
-rw-r--r--core/java/android/net/NetworkPolicy.aidl19
-rw-r--r--core/java/android/net/NetworkPolicy.java81
-rw-r--r--core/java/android/net/NetworkPolicyManager.java17
-rw-r--r--core/res/AndroidManifest.xml13
-rwxr-xr-xcore/res/res/values/strings.xml10
-rw-r--r--services/java/com/android/server/SystemServer.java1
-rw-r--r--services/java/com/android/server/net/NetworkPolicyManagerService.java491
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java56
-rw-r--r--services/tests/servicestests/AndroidManifest.xml2
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java143
12 files changed, 791 insertions, 48 deletions
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index c1f3530..c9238eb 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.INetworkPolicyListener;
+import android.net.NetworkPolicy;
/**
* Interface that creates and modifies network policy rules.
@@ -33,6 +34,7 @@ interface INetworkPolicyManager {
void registerListener(INetworkPolicyListener listener);
void unregisterListener(INetworkPolicyListener listener);
- // TODO: build API to surface stats details for settings UI
+ void setNetworkPolicy(int networkType, String subscriberId, in NetworkPolicy policy);
+ NetworkPolicy getNetworkPolicy(int networkType, String subscriberId);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index d05c9d3..288112a 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -27,6 +27,8 @@ interface INetworkStatsService {
/** Return historical stats for specific UID traffic that matches template. */
NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+ /** Return usage summary for traffic that matches template. */
+ NetworkStats getSummaryForNetwork(long start, long end, int networkTemplate, String subscriberId);
/** Return usage summary per UID for traffic that matches template. */
NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate);
diff --git a/core/java/android/net/NetworkPolicy.aidl b/core/java/android/net/NetworkPolicy.aidl
new file mode 100644
index 0000000..dbabb06
--- /dev/null
+++ b/core/java/android/net/NetworkPolicy.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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;
+
+parcelable NetworkPolicy;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
new file mode 100644
index 0000000..b9909c3
--- /dev/null
+++ b/core/java/android/net/NetworkPolicy.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Policy for a specific network, including usage cycle and limits to be
+ * enforced.
+ *
+ * @hide
+ */
+public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
+ public final int cycleDay;
+ public final long warningBytes;
+ public final long limitBytes;
+
+ public NetworkPolicy(int cycleDay, long warningBytes, long limitBytes) {
+ this.cycleDay = cycleDay;
+ this.warningBytes = warningBytes;
+ this.limitBytes = limitBytes;
+ }
+
+ public NetworkPolicy(Parcel in) {
+ cycleDay = in.readInt();
+ warningBytes = in.readLong();
+ limitBytes = in.readLong();
+ }
+
+ /** {@inheritDoc} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(cycleDay);
+ dest.writeLong(warningBytes);
+ dest.writeLong(limitBytes);
+ }
+
+ /** {@inheritDoc} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(NetworkPolicy another) {
+ if (another == null || limitBytes < another.limitBytes) {
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "NetworkPolicy: cycleDay=" + cycleDay + ", warningBytes=" + warningBytes
+ + ", limitBytes=" + limitBytes;
+ }
+
+ public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
+ public NetworkPolicy createFromParcel(Parcel in) {
+ return new NetworkPolicy(in);
+ }
+
+ public NetworkPolicy[] newArray(int size) {
+ return new NetworkPolicy[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index dd7c1b0..0f4dc9a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -51,6 +51,23 @@ public class NetworkPolicyManager {
return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
}
+ /** {@hide} */
+ public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) {
+ try {
+ mService.setNetworkPolicy(networkType, subscriberId, policy);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /** {@hide} */
+ public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) {
+ try {
+ return mService.getNetworkPolicy(networkType, subscriberId);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
/**
* Set policy flags for specific UID.
*
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b2606c1..a8aff37 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1377,6 +1377,19 @@
<permission android:name="android.permission.CRYPT_KEEPER"
android:protectionLevel="signatureOrSystem" />
+ <!-- Allows an application to read historical network usage for
+ specific networks and applications. @hide -->
+ <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
+ android:label="@string/permlab_readNetworkUsageHistory"
+ android:description="@string/permdesc_readNetworkUsageHistory"
+ android:protectionLevel="signature" />
+
+ <!-- Allows an application to manage network policies (such as warning and disable
+ limits) and to define application-specific rules. @hide -->
+ <permission android:name="android.permission.MANAGE_NETWORK_POLICY"
+ android:label="@string/permlab_manageNetworkPolicy"
+ android:description="@string/permdesc_manageNetworkPolicy"
+ android:protectionLevel="signature" />
<!-- C2DM permission.
@hide Used internally.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 3736157..b264b41 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1431,6 +1431,16 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_use_sip">Allows an application to use the SIP service to make/receive Internet calls.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_readNetworkUsageHistory">read historical network usage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_readNetworkUsageHistory">Allows an application to read historical network usage for specific networks and applications.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_manageNetworkPolicy">manage network policy</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_manageNetworkPolicy">Allows an application to manage network policies and define application-specific rules.</string>
+
<!-- Policy administration -->
<!-- Title of policy access to limiting the user's password choices -->
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fd03e5f..3484baf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -306,6 +306,7 @@ class ServerThread extends Thread {
connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
networkStats.bindConnectivityManager(connectivity);
+ networkPolicy.bindConnectivityManager(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 1a90a92..c8f617e 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -16,15 +16,22 @@
package com.android.server.net;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
-import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkPolicyManager.POLICY_NONE;
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 static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.Time.MONTH_DAY;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.app.IActivityManager;
import android.app.IProcessObserver;
@@ -33,19 +40,51 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.NetworkPolicy;
+import android.net.NetworkState;
+import android.net.NetworkStats;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IPowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.text.format.Time;
+import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import android.util.TrustedTime;
+import android.util.Xml;
+import com.android.internal.os.AtomicFile;
+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 org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+
+import libcore.io.IoUtils;
/**
* Service that maintains low-level network policy rules and collects usage
@@ -59,10 +98,30 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG = "NetworkPolicy";
private static final boolean LOGD = true;
+ private static final int VERSION_CURRENT = 1;
+
+ private static final String TAG_POLICY_LIST = "policy-list";
+ private static final String TAG_NETWORK_POLICY = "network-policy";
+ private static final String TAG_UID_POLICY = "uid-policy";
+
+ private static final String ATTR_VERSION = "version";
+ private static final String ATTR_NETWORK_TEMPLATE = "networkTemplate";
+ private static final String ATTR_SUBSCRIBER_ID = "subscriberId";
+ private static final String ATTR_CYCLE_DAY = "cycleDay";
+ private static final String ATTR_WARNING_BYTES = "warningBytes";
+ private static final String ATTR_LIMIT_BYTES = "limitBytes";
+ private static final String ATTR_UID = "uid";
+ private static final String ATTR_POLICY = "policy";
+
+ private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
+
private final Context mContext;
private final IActivityManager mActivityManager;
private final IPowerManager mPowerManager;
private final INetworkStatsService mNetworkStats;
+ private final TrustedTime mTime;
+
+ private IConnectivityManager mConnManager;
private final Object mRulesLock = new Object();
@@ -73,6 +132,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/** Current derived network rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
+ /** Set of policies for strong network templates. */
+ private HashMap<StrongTemplate, NetworkPolicy> mTemplatePolicy = Maps.newHashMap();
+
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -81,26 +143,52 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private final RemoteCallbackList<INetworkPolicyListener> mListeners = new RemoteCallbackList<
INetworkPolicyListener>();
- // TODO: save/restore policy information from disk
+ private final HandlerThread mHandlerThread;
+ private final Handler mHandler;
+
+ private final AtomicFile mPolicyFile;
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
- // TODO: keep record of billing cycle details, and limit rules
- // TODO: keep map of interfaces-to-billing-relationship
-
// TODO: dispatch callbacks through handler when locked
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats) {
+ // TODO: move to using cached NtpTrustedTime
+ this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(),
+ getSystemDir());
+ }
+
+ private static File getSystemDir() {
+ return new File(Environment.getDataDirectory(), "system");
+ }
+
+ public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
+ IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time,
+ File systemDir) {
mContext = checkNotNull(context, "missing context");
mActivityManager = checkNotNull(activityManager, "missing activityManager");
mPowerManager = checkNotNull(powerManager, "missing powerManager");
mNetworkStats = checkNotNull(networkStats, "missing networkStats");
+ mTime = checkNotNull(time, "missing TrustedTime");
+
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+
+ mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
+ }
+
+ public void bindConnectivityManager(IConnectivityManager connManager) {
+ mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
}
public void systemReady() {
- // TODO: read current policy from disk
+ synchronized (mRulesLock) {
+ // read policy from disk
+ readPolicyLocked();
+ }
updateScreenOn();
@@ -120,6 +208,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mScreenReceiver, screenFilter);
+ // watch for network interfaces to be claimed
+ final IntentFilter ifaceFilter = new IntentFilter();
+ ifaceFilter.addAction(CONNECTIVITY_ACTION);
+ mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
+
}
private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -139,7 +232,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mUidPidForeground.put(uid, pidForeground);
}
pidForeground.put(pid, foregroundActivities);
- computeUidForegroundL(uid);
+ computeUidForegroundLocked(uid);
}
}
@@ -153,7 +246,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
if (pidForeground != null) {
pidForeground.delete(pid);
- computeUidForegroundL(uid);
+ computeUidForegroundLocked(uid);
}
}
}
@@ -170,23 +263,279 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
};
+ /**
+ * Receiver that watches for {@link IConnectivityManager} to claim network
+ * interfaces. Used to apply {@link NetworkPolicy} when networks match
+ * {@link StrongTemplate}.
+ */
+ private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // on background handler thread, and verified CONNECTIVITY_INTERNAL
+ // permission above.
+ synchronized (mRulesLock) {
+ updateIfacesLocked();
+ }
+ }
+ };
+
+ /**
+ * Examine all connected {@link NetworkState}, looking for
+ * {@link NetworkPolicy} that need to be enforced. When matches found, set
+ * remaining quota based on usage cycle and historical stats.
+ */
+ private void updateIfacesLocked() {
+ if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+
+ final NetworkState[] states;
+ try {
+ states = mConnManager.getAllNetworkState();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading network state");
+ return;
+ }
+
+ // first, derive identity for all connected networks, which can be used
+ // to match against templates.
+ final HashMap<NetworkIdentity, String> networks = Maps.newHashMap();
+ for (NetworkState state : states) {
+ // stash identity and iface away for later use
+ if (state.networkInfo.isConnected()) {
+ final String iface = state.linkProperties.getInterfaceName();
+ final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
+ networks.put(ident, iface);
+ }
+ }
+
+ // build list of rules and ifaces to enforce them against
+ final HashMap<StrongTemplate, String[]> rules = Maps.newHashMap();
+ final ArrayList<String> ifaceList = Lists.newArrayList();
+ for (StrongTemplate template : mTemplatePolicy.keySet()) {
+
+ // collect all active ifaces that match this template
+ ifaceList.clear();
+ for (NetworkIdentity ident : networks.keySet()) {
+ if (ident.matchesTemplate(template.networkTemplate, template.subscriberId)) {
+ final String iface = networks.get(ident);
+ ifaceList.add(iface);
+ }
+ }
+
+ if (ifaceList.size() > 0) {
+ final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
+ rules.put(template, ifaces);
+ }
+ }
+
+ // try refreshing time source when stale
+ if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
+ mTime.forceRefresh();
+ }
+
+ final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
+ : System.currentTimeMillis();
+
+ // apply each policy that we found ifaces for; compute remaining data
+ // based on current cycle and historical stats, and push to kernel.
+ for (StrongTemplate template : rules.keySet()) {
+ final NetworkPolicy policy = mTemplatePolicy.get(template);
+ final String[] ifaces = rules.get(policy);
+
+ final long start = computeLastCycleBoundary(currentTime, policy);
+ final long end = currentTime;
+
+ final NetworkStats stats;
+ try {
+ stats = mNetworkStats.getSummaryForNetwork(
+ start, end, template.networkTemplate, template.subscriberId);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "problem reading summary for template " + template.networkTemplate);
+ continue;
+ }
+
+ // remaining "quota" is based on usage in current cycle
+ final long total = stats.rx[0] + stats.tx[0];
+ final long quota = Math.max(0, policy.limitBytes - total);
+
+ if (LOGD) {
+ Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
+ + Arrays.toString(ifaces) + " with quota " + quota);
+ }
+
+ // TODO: push rule down through NetworkManagementService.setInterfaceQuota()
+
+ }
+ }
+
+ /**
+ * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
+ * example, if cycle day is 20th, and today is June 15th, it will return May
+ * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
+ * of following month.
+ */
+ // @VisibleForTesting
+ public static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
+ final Time now = new Time(Time.TIMEZONE_UTC);
+ now.set(currentTime);
+
+ // first, find cycle boundary for current month
+ final Time cycle = new Time(now);
+ cycle.hour = cycle.minute = cycle.second = 0;
+ snapToCycleDay(cycle, policy.cycleDay);
+
+ if (cycle.after(now)) {
+ // cycle boundary is beyond now, use last cycle boundary; start by
+ // pushing ourselves squarely into last month.
+ final Time lastMonth = new Time(now);
+ lastMonth.hour = lastMonth.minute = lastMonth.second = 0;
+ lastMonth.monthDay = 1;
+ lastMonth.month -= 1;
+ lastMonth.normalize(true);
+
+ cycle.set(lastMonth);
+ snapToCycleDay(cycle, policy.cycleDay);
+ }
+
+ return cycle.toMillis(true);
+ }
+
+ /**
+ * Snap to the cycle day for the current month given; when cycle day doesn't
+ * exist, it snaps to 1st of following month.
+ */
+ private static void snapToCycleDay(Time time, int cycleDay) {
+ if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
+ // cycle day isn't valid this month; snap to 1st of next month
+ time.month += 1;
+ time.monthDay = 1;
+ } else {
+ time.monthDay = cycleDay;
+ }
+ time.normalize(true);
+ }
+
+ private void readPolicyLocked() {
+ if (LOGD) Slog.v(TAG, "readPolicyLocked()");
+
+ // clear any existing policy and read from disk
+ mTemplatePolicy.clear();
+ mUidPolicy.clear();
+
+ FileInputStream fis = null;
+ try {
+ fis = mPolicyFile.openRead();
+ final XmlPullParser in = Xml.newPullParser();
+ in.setInput(fis, null);
+
+ int type;
+ int version = VERSION_CURRENT;
+ while ((type = in.next()) != END_DOCUMENT) {
+ final String tag = in.getName();
+ if (type == START_TAG) {
+ if (TAG_POLICY_LIST.equals(tag)) {
+ version = readIntAttribute(in, ATTR_VERSION);
+
+ } else if (TAG_NETWORK_POLICY.equals(tag)) {
+ final int networkTemplate = readIntAttribute(in, ATTR_NETWORK_TEMPLATE);
+ final String subscriberId = in.getAttributeValue(null, ATTR_SUBSCRIBER_ID);
+
+ final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
+ final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
+ final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
+
+ mTemplatePolicy.put(new StrongTemplate(networkTemplate, subscriberId),
+ new NetworkPolicy(cycleDay, warningBytes, limitBytes));
+
+ } else if (TAG_UID_POLICY.equals(tag)) {
+ final int uid = readIntAttribute(in, ATTR_UID);
+ final int policy = readIntAttribute(in, ATTR_POLICY);
+
+ mUidPolicy.put(uid, policy);
+ }
+ }
+ }
+
+ } catch (FileNotFoundException e) {
+ // missing policy is okay, probably first boot
+ } catch (IOException e) {
+ Slog.e(TAG, "problem reading network stats", e);
+ } catch (XmlPullParserException e) {
+ Slog.e(TAG, "problem reading network stats", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private void writePolicyLocked() {
+ if (LOGD) Slog.v(TAG, "writePolicyLocked()");
+
+ FileOutputStream fos = null;
+ try {
+ fos = mPolicyFile.startWrite();
+
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(fos, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, TAG_POLICY_LIST);
+ writeIntAttribute(out, ATTR_VERSION, VERSION_CURRENT);
+
+ // write all known network policies
+ for (StrongTemplate template : mTemplatePolicy.keySet()) {
+ final NetworkPolicy policy = mTemplatePolicy.get(template);
+
+ out.startTag(null, TAG_NETWORK_POLICY);
+ writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.networkTemplate);
+ if (template.subscriberId != null) {
+ out.attribute(null, ATTR_SUBSCRIBER_ID, template.subscriberId);
+ }
+ writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
+ writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
+ writeLongAttribute(out, ATTR_LIMIT_BYTES, policy.limitBytes);
+ out.endTag(null, TAG_NETWORK_POLICY);
+ }
+
+ // write all known uid policies
+ for (int i = 0; i < mUidPolicy.size(); i++) {
+ final int uid = mUidPolicy.keyAt(i);
+ final int policy = mUidPolicy.valueAt(i);
+
+ out.startTag(null, TAG_UID_POLICY);
+ writeIntAttribute(out, ATTR_UID, uid);
+ writeIntAttribute(out, ATTR_POLICY, policy);
+ out.endTag(null, TAG_UID_POLICY);
+ }
+
+ out.endTag(null, TAG_POLICY_LIST);
+ out.endDocument();
+
+ mPolicyFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mPolicyFile.failWrite(fos);
+ }
+ }
+ }
+
@Override
public void setUidPolicy(int uid, int policy) {
- // TODO: create permission for modifying data policy
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
final int oldPolicy;
synchronized (mRulesLock) {
oldPolicy = getUidPolicy(uid);
mUidPolicy.put(uid, policy);
- updateRulesForUidL(uid);
- }
- // TODO: consider dispatching BACKGROUND_DATA_SETTING broadcast
+ // uid policy changed, recompute rules and persist policy.
+ updateRulesForUidLocked(uid);
+ writePolicyLocked();
+ }
}
@Override
public int getUidPolicy(int uid) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
synchronized (mRulesLock) {
return mUidPolicy.get(uid, POLICY_NONE);
}
@@ -218,10 +567,40 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
@Override
+ public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mRulesLock) {
+ mTemplatePolicy.put(new StrongTemplate(networkType, subscriberId), policy);
+
+ // network policy changed, recompute template rules based on active
+ // interfaces and persist policy.
+ updateIfacesLocked();
+ writePolicyLocked();
+ }
+ }
+
+ @Override
+ public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ synchronized (mRulesLock) {
+ return mTemplatePolicy.get(new StrongTemplate(networkType, subscriberId));
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
synchronized (mRulesLock) {
+ fout.println("Network policies:");
+ for (StrongTemplate template : mTemplatePolicy.keySet()) {
+ final NetworkPolicy policy = mTemplatePolicy.get(template);
+ fout.print(" "); fout.println(template.toString());
+ fout.print(" "); fout.println(policy.toString());
+ }
+
fout.println("Policy status for known UIDs:");
final SparseBooleanArray knownUids = new SparseBooleanArray();
@@ -274,9 +653,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
/**
* Foreground for PID changed; recompute foreground at UID level. If
- * changed, will trigger {@link #updateRulesForUidL(int)}.
+ * changed, will trigger {@link #updateRulesForUidLocked(int)}.
*/
- private void computeUidForegroundL(int uid) {
+ private void computeUidForegroundLocked(int uid) {
final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
// current pid is dropping foreground; examine other pids
@@ -293,7 +672,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
if (oldUidForeground != uidForeground) {
// foreground changed, push updated rules
mUidForeground.put(uid, uidForeground);
- updateRulesForUidL(uid);
+ updateRulesForUidLocked(uid);
}
}
@@ -303,25 +682,25 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mScreenOn = mPowerManager.isScreenOn();
} catch (RemoteException e) {
}
- updateRulesForScreenL();
+ updateRulesForScreenLocked();
}
}
/**
* Update rules that might be changed by {@link #mScreenOn} value.
*/
- private void updateRulesForScreenL() {
+ private void updateRulesForScreenLocked() {
// only update rules for anyone with foreground activities
final int size = mUidForeground.size();
for (int i = 0; i < size; i++) {
if (mUidForeground.valueAt(i)) {
final int uid = mUidForeground.keyAt(i);
- updateRulesForUidL(uid);
+ updateRulesForUidLocked(uid);
}
}
}
- private void updateRulesForUidL(int uid) {
+ private void updateRulesForUidLocked(int uid) {
final int uidPolicy = getUidPolicy(uid);
final boolean uidForeground = isUidForeground(uid);
@@ -351,13 +730,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mListeners.finishBroadcast();
}
- private static <T> T checkNotNull(T value, String message) {
- if (value == null) {
- throw new NullPointerException(message);
- }
- return value;
- }
-
private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
@@ -381,4 +753,69 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
fout.print("]");
}
+
+ private static int readIntAttribute(XmlPullParser in, String name) throws IOException {
+ final String value = in.getAttributeValue(null, name);
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
+ }
+ }
+
+ private static long readLongAttribute(XmlPullParser in, String name) throws IOException {
+ final String value = in.getAttributeValue(null, name);
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
+ }
+ }
+
+ private static void writeIntAttribute(XmlSerializer out, String name, int value)
+ throws IOException {
+ out.attribute(null, name, Integer.toString(value));
+ }
+
+ private static void writeLongAttribute(XmlSerializer out, String name, long value)
+ throws IOException {
+ out.attribute(null, name, Long.toString(value));
+ }
+
+ /**
+ * Network template with strong subscriber ID, used as key when defining
+ * {@link NetworkPolicy}.
+ */
+ private static class StrongTemplate {
+ public final int networkTemplate;
+ public final String subscriberId;
+
+ public StrongTemplate(int networkTemplate, String subscriberId) {
+ this.networkTemplate = networkTemplate;
+ this.subscriberId = subscriberId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(networkTemplate, subscriberId);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof StrongTemplate) {
+ final StrongTemplate template = (StrongTemplate) obj;
+ return template.networkTemplate == networkTemplate
+ && Objects.equal(template.subscriberId, subscriberId);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "TemplateIdentity: networkTemplate=" + networkTemplate + ", subscriberId="
+ + subscriberId;
+ }
+
+ }
+
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 8db2839..161c393 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -18,8 +18,8 @@ package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.SHUTDOWN;
-import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.UID_ALL;
@@ -86,7 +86,7 @@ import libcore.io.IoUtils;
* other system services.
*/
public class NetworkStatsService extends INetworkStatsService.Stub {
- private static final String TAG = "NetworkStatsService";
+ private static final String TAG = "NetworkStats";
private static final boolean LOGD = true;
/** File header magic number: "ANET" */
@@ -178,20 +178,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mSummaryFile = new AtomicFile(new File(systemDir, "netstats.bin"));
}
+ public void bindConnectivityManager(IConnectivityManager connManager) {
+ mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+ }
+
public void systemReady() {
synchronized (mStatsLock) {
// read historical stats from disk
readStatsLocked();
}
- // watch other system services that claim interfaces
+ // watch for network interfaces to be claimed
final IntentFilter ifaceFilter = new IntentFilter();
ifaceFilter.addAction(CONNECTIVITY_ACTION);
mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
// listen for periodic polling events
+ // TODO: switch to stronger internal permission
final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
- mContext.registerReceiver(mPollReceiver, pollFilter, UPDATE_DEVICE_STATS, mHandler);
+ mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
// persist stats during clean shutdown
final IntentFilter shutdownFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
@@ -204,10 +209,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
}
}
- public void bindConnectivityManager(IConnectivityManager connManager) {
- mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
- }
-
private void shutdownLocked() {
mContext.unregisterReceiver(mIfaceReceiver);
mContext.unregisterReceiver(mPollReceiver);
@@ -237,8 +238,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
- // TODO: create relaxed permission for reading stats
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
// combine all interfaces that match template
@@ -257,17 +257,41 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override
public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
- // TODO: create relaxed permission for reading stats
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
// TODO: return history for requested uid
return null;
}
@Override
+ public NetworkStats getSummaryForNetwork(
+ long start, long end, int networkTemplate, String subscriberId) {
+ mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+
+ synchronized (mStatsLock) {
+ long rx = 0;
+ long tx = 0;
+ long[] networkTotal = new long[2];
+
+ // combine total from all interfaces that match template
+ for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+ final NetworkStatsHistory history = mSummaryStats.get(ident);
+ if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+ networkTotal = history.getTotalData(start, end, networkTotal);
+ rx += networkTotal[0];
+ tx += networkTotal[1];
+ }
+ }
+
+ final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, 1);
+ stats.addEntry(IFACE_ALL, UID_ALL, tx, tx);
+ return stats.build();
+ }
+ }
+
+ @Override
public NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate) {
- // TODO: create relaxed permission for reading stats
- mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+ mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
// TODO: apply networktemplate once granular uid stats are stored.
@@ -275,11 +299,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final int size = mDetailStats.size();
final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
- final long[] total = new long[2];
+ long[] total = new long[2];
for (int i = 0; i < size; i++) {
final int uid = mDetailStats.keyAt(i);
final NetworkStatsHistory history = mDetailStats.valueAt(i);
- history.getTotalData(start, end, total);
+ total = history.getTotalData(start, end, total);
stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
}
return stats.build();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 295d569..151fde7 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 6552cdf..c4ddb00 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -16,10 +16,15 @@
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.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_PAID;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static com.android.server.net.NetworkPolicyManagerService.computeLastCycleBoundary;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.eq;
@@ -31,20 +36,30 @@ import android.app.IProcessObserver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicy;
+import android.net.NetworkState;
+import android.net.NetworkStats;
import android.os.Binder;
import android.os.IPowerManager;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.Suppress;
+import android.text.format.Time;
+import android.util.TrustedTime;
import com.android.server.net.NetworkPolicyManagerService;
import org.easymock.Capture;
import org.easymock.EasyMock;
+import java.io.File;
import java.util.concurrent.Future;
/**
@@ -54,12 +69,18 @@ import java.util.concurrent.Future;
public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
private static final String TAG = "NetworkPolicyManagerServiceTest";
+ private static final long TEST_START = 1194220800000L;
+ private static final String TEST_IFACE = "test0";
+
private BroadcastInterceptingContext mServiceContext;
+ private File mPolicyDir;
private IActivityManager mActivityManager;
private IPowerManager mPowerManager;
private INetworkStatsService mStatsService;
private INetworkPolicyListener mPolicyListener;
+ private TrustedTime mTime;
+ private IConnectivityManager mConnManager;
private NetworkPolicyManagerService mService;
private IProcessObserver mProcessObserver;
@@ -90,13 +111,18 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
}
};
+ mPolicyDir = getContext().getFilesDir();
+
mActivityManager = createMock(IActivityManager.class);
mPowerManager = createMock(IPowerManager.class);
mStatsService = createMock(INetworkStatsService.class);
mPolicyListener = createMock(INetworkPolicyListener.class);
+ mTime = createMock(TrustedTime.class);
+ mConnManager = createMock(IConnectivityManager.class);
mService = new NetworkPolicyManagerService(
- mServiceContext, mActivityManager, mPowerManager, mStatsService);
+ mServiceContext, mActivityManager, mPowerManager, mStatsService, mTime, mPolicyDir);
+ mService.bindConnectivityManager(mConnManager);
// RemoteCallbackList needs a binder to use as key
expect(mPolicyListener.asBinder()).andReturn(mStubBinder).atLeastOnce();
@@ -122,12 +148,18 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
@Override
public void tearDown() throws Exception {
+ for (File file : mPolicyDir.listFiles()) {
+ file.delete();
+ }
+
mServiceContext = null;
+ mPolicyDir = null;
mActivityManager = null;
mPowerManager = null;
mStatsService = null;
mPolicyListener = null;
+ mTime = null;
mService = null;
mProcessObserver = null;
@@ -260,17 +292,120 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase {
verifyAndReset();
}
+ public void testLastCycleBoundaryThisMonth() throws Exception {
+ // assume cycle day of "5th", which should be in same month
+ final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
+ final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(5, 1024L, 1024L);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertEquals(expectedCycle, actualCycle);
+ }
+
+ public void testLastCycleBoundaryLastMonth() throws Exception {
+ // assume cycle day of "20th", which should be in last month
+ final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
+ final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(20, 1024L, 1024L);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertEquals(expectedCycle, actualCycle);
+ }
+
+ public void testLastCycleBoundaryThisMonthFebruary() throws Exception {
+ // assume cycle day of "30th" in february; should go to january
+ final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
+ final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(30, 1024L, 1024L);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertEquals(expectedCycle, actualCycle);
+ }
+
+ public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
+ // assume cycle day of "30th" in february, which should clamp
+ final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
+ final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z");
+
+ final NetworkPolicy policy = new NetworkPolicy(30, 1024L, 1024L);
+ final long actualCycle = computeLastCycleBoundary(currentTime, policy);
+ assertEquals(expectedCycle, actualCycle);
+ }
+
+ public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
+ long elapsedRealtime = 0;
+ NetworkState[] state = null;
+ NetworkStats stats = null;
+
+ final long TIME_FEB_15 = 1171497600000L;
+ final long TIME_MAR_10 = 1173484800000L;
+ final int CYCLE_DAY = 15;
+
+ // first, pretend that wifi network comes online. no policy active,
+ // which means we shouldn't push limit to interface.
+ state = new NetworkState[] { buildWifi() };
+ expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+ expectTime(TIME_MAR_10 + elapsedRealtime);
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // now change cycle to be on 15th, and test in early march, to verify we
+ // pick cycle day in previous month.
+ expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+ expectTime(TIME_MAR_10 + elapsedRealtime);
+
+ // pretend that 512 bytes total have happened
+ stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
+ TEST_IFACE, UID_ALL, 256L, 256L).build();
+ expect(mStatsService.getSummaryForNetwork(TIME_FEB_15, TIME_MAR_10, TEMPLATE_WIFI, null))
+ .andReturn(stats).atLeastOnce();
+
+ // expect that quota remaining should be 1536 bytes
+ // TODO: write up NetworkManagementService mock
+
+ replay();
+ mService.setNetworkPolicy(TEMPLATE_WIFI, null, new NetworkPolicy(CYCLE_DAY, 1024L, 2048L));
+ verifyAndReset();
+ }
+
+ private static long parseTime(String time) {
+ final Time result = new Time();
+ result.parse3339(time);
+ return result.toMillis(true);
+ }
+
+ private static NetworkState buildWifi() {
+ final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
+ info.setDetailedState(DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_IFACE);
+ return new NetworkState(info, prop, null);
+ }
+
+ public void expectTime(long currentTime) throws Exception {
+ expect(mTime.forceRefresh()).andReturn(false).anyTimes();
+ expect(mTime.hasCache()).andReturn(true).anyTimes();
+ expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+ expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
+ expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
+ }
+
private void expectRulesChanged(int uid, int policy) throws Exception {
mPolicyListener.onRulesChanged(eq(uid), eq(policy));
expectLastCall().atLeastOnce();
}
private void replay() {
- EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener);
+ EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
+ mConnManager);
}
private void verifyAndReset() {
- EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener);
- EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener);
+ EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
+ mConnManager);
+ EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
+ mConnManager);
}
}