diff options
18 files changed, 764 insertions, 399 deletions
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index c9238eb..82495e3 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -34,7 +34,7 @@ interface INetworkPolicyManager { void registerListener(INetworkPolicyListener listener); void unregisterListener(INetworkPolicyListener listener); - void setNetworkPolicy(int networkType, String subscriberId, in NetworkPolicy policy); - NetworkPolicy getNetworkPolicy(int networkType, String subscriberId); + void setNetworkPolicies(in NetworkPolicy[] policies); + NetworkPolicy[] getNetworkPolicies(); } diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index b9909c3..1899281 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -26,17 +26,27 @@ import android.os.Parcelable; * @hide */ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { - public final int cycleDay; - public final long warningBytes; - public final long limitBytes; + public final int networkTemplate; + public final String subscriberId; + public int cycleDay; + public long warningBytes; + public long limitBytes; - public NetworkPolicy(int cycleDay, long warningBytes, long limitBytes) { + public static final long WARNING_DISABLED = -1; + public static final long LIMIT_DISABLED = -1; + + public NetworkPolicy(int networkTemplate, String subscriberId, int cycleDay, long warningBytes, + long limitBytes) { + this.networkTemplate = networkTemplate; + this.subscriberId = subscriberId; this.cycleDay = cycleDay; this.warningBytes = warningBytes; this.limitBytes = limitBytes; } public NetworkPolicy(Parcel in) { + networkTemplate = in.readInt(); + subscriberId = in.readString(); cycleDay = in.readInt(); warningBytes = in.readLong(); limitBytes = in.readLong(); @@ -44,6 +54,8 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { /** {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(networkTemplate); + dest.writeString(subscriberId); dest.writeInt(cycleDay); dest.writeLong(warningBytes); dest.writeLong(limitBytes); @@ -56,17 +68,21 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { /** {@inheritDoc} */ public int compareTo(NetworkPolicy another) { - if (another == null || limitBytes < another.limitBytes) { + if (another == null || another.limitBytes == LIMIT_DISABLED) { + // other value is missing or disabled; we win return -1; - } else { + } + if (limitBytes == LIMIT_DISABLED || another.limitBytes < limitBytes) { + // we're disabled or other limit is smaller; they win return 1; } + return 0; } @Override public String toString() { - return "NetworkPolicy: cycleDay=" + cycleDay + ", warningBytes=" + warningBytes - + ", limitBytes=" + limitBytes; + return "NetworkPolicy: networkTemplate=" + networkTemplate + ", cycleDay=" + cycleDay + + ", warningBytes=" + warningBytes + ", limitBytes=" + limitBytes; } public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() { diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 08b1a81..13ece40 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -55,17 +55,17 @@ public class NetworkPolicyManager { } /** {@hide} */ - public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) { + public void setNetworkPolicies(NetworkPolicy[] policies) { try { - mService.setNetworkPolicy(networkType, subscriberId, policy); + mService.setNetworkPolicies(policies); } catch (RemoteException e) { } } /** {@hide} */ - public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) { + public NetworkPolicy[] getNetworkPolicies() { try { - return mService.getNetworkPolicy(networkType, subscriberId); + return mService.getNetworkPolicies(); } catch (RemoteException e) { return null; } diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 6354e9a..60f740e 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -23,6 +23,7 @@ import android.util.SparseBooleanArray; import java.io.CharArrayWriter; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashSet; /** @@ -48,74 +49,60 @@ public class NetworkStats implements Parcelable { * generated. */ public final long elapsedRealtime; - public final String[] iface; - public final int[] uid; - public final long[] rx; - public final long[] tx; + public int size; + public String[] iface; + public int[] uid; + public long[] rx; + public long[] tx; // TODO: add fg/bg stats once reported by kernel - private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) { + public NetworkStats(long elapsedRealtime, int initialSize) { this.elapsedRealtime = elapsedRealtime; - this.iface = iface; - this.uid = uid; - this.rx = rx; - this.tx = tx; + this.size = 0; + this.iface = new String[initialSize]; + this.uid = new int[initialSize]; + this.rx = new long[initialSize]; + this.tx = new long[initialSize]; } public NetworkStats(Parcel parcel) { elapsedRealtime = parcel.readLong(); + size = parcel.readInt(); iface = parcel.createStringArray(); uid = parcel.createIntArray(); rx = parcel.createLongArray(); tx = parcel.createLongArray(); } - public static class Builder { - private long mElapsedRealtime; - private final String[] mIface; - private final int[] mUid; - private final long[] mRx; - private final long[] mTx; - - private int mIndex = 0; - - public Builder(long elapsedRealtime, int size) { - mElapsedRealtime = elapsedRealtime; - mIface = new String[size]; - mUid = new int[size]; - mRx = new long[size]; - mTx = new long[size]; + public NetworkStats addEntry(String iface, int uid, long rx, long tx) { + if (size >= this.iface.length) { + final int newLength = Math.max(this.iface.length, 10) * 3 / 2; + this.iface = Arrays.copyOf(this.iface, newLength); + this.uid = Arrays.copyOf(this.uid, newLength); + this.rx = Arrays.copyOf(this.rx, newLength); + this.tx = Arrays.copyOf(this.tx, newLength); } - public Builder addEntry(String iface, int uid, long rx, long tx) { - mIface[mIndex] = iface; - mUid[mIndex] = uid; - mRx[mIndex] = rx; - mTx[mIndex] = tx; - mIndex++; - return this; - } + this.iface[size] = iface; + this.uid[size] = uid; + this.rx[size] = rx; + this.tx[size] = tx; + size++; - public NetworkStats build() { - if (mIndex != mIface.length) { - throw new IllegalArgumentException("unexpected number of entries"); - } - return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx); - } + return this; } + @Deprecated public int length() { - // length is identical for all fields - return iface.length; + return size; } /** * Find first stats index that matches the requested parameters. */ public int findIndex(String iface, int uid) { - final int length = length(); - for (int i = 0; i < length; i++) { + for (int i = 0; i < size; i++) { if (equal(iface, this.iface[i]) && uid == this.uid[i]) { return i; } @@ -195,9 +182,8 @@ public class NetworkStats implements Parcelable { } // result will have our rows, and elapsed time between snapshots - final int length = length(); - final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length); - for (int i = 0; i < length; i++) { + final NetworkStats result = new NetworkStats(deltaRealtime, size); + for (int i = 0; i < size; i++) { final String iface = this.iface[i]; final int uid = this.uid[i]; @@ -221,7 +207,7 @@ public class NetworkStats implements Parcelable { } } - return result.build(); + return result; } private static boolean equal(Object a, Object b) { @@ -255,6 +241,7 @@ public class NetworkStats implements Parcelable { /** {@inheritDoc} */ public void writeToParcel(Parcel dest, int flags) { dest.writeLong(elapsedRealtime); + dest.writeInt(size); dest.writeStringArray(iface); dest.writeIntArray(uid); dest.writeLongArray(rx); diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java index a697e96..5fa8e21 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -53,11 +53,15 @@ public class NetworkStatsHistory implements Parcelable { public long[] tx; public NetworkStatsHistory(long bucketDuration) { + this(bucketDuration, 10); + } + + public NetworkStatsHistory(long bucketDuration, int initialSize) { this.bucketDuration = bucketDuration; - bucketStart = new long[0]; - rx = new long[0]; - tx = new long[0]; - bucketCount = bucketStart.length; + bucketStart = new long[initialSize]; + rx = new long[initialSize]; + tx = new long[initialSize]; + bucketCount = 0; } public NetworkStatsHistory(Parcel in) { @@ -168,8 +172,8 @@ public class NetworkStatsHistory implements Parcelable { */ private void insertBucket(int index, long start) { // create more buckets when needed - if (bucketCount + 1 > bucketStart.length) { - final int newLength = bucketStart.length + 10; + if (bucketCount >= bucketStart.length) { + final int newLength = Math.max(bucketStart.length, 10) * 3 / 2; bucketStart = Arrays.copyOf(bucketStart, newLength); rx = Arrays.copyOf(rx, newLength); tx = Arrays.copyOf(tx, newLength); diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 8a688d5..3725fa6 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -41,11 +41,9 @@ public class TrafficStats { */ public final static int UNSUPPORTED = -1; - // TODO: find better home for these template constants - /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style - * networks together. Only uses statistics for currently active IMSI. + * networks together. Only uses statistics for requested IMSI. * * @hide */ @@ -54,7 +52,7 @@ public class TrafficStats { /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style * networks together that roughly meet a "3G" definition, or lower. Only - * uses statistics for currently active IMSI. + * uses statistics for requested IMSI. * * @hide */ @@ -63,7 +61,7 @@ public class TrafficStats { /** * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style * networks together that meet a "4G" definition. Only uses statistics for - * currently active IMSI. + * requested IMSI. * * @hide */ @@ -184,6 +182,17 @@ public class TrafficStats { } } + /** {@hide} */ + public static boolean isNetworkTemplateMobile(int networkTemplate) { + switch (networkTemplate) { + case TEMPLATE_MOBILE_3G_LOWER: + case TEMPLATE_MOBILE_4G: + case TEMPLATE_MOBILE_ALL: + return true; + } + return false; + } + /** * Get the total number of packets transmitted through the mobile interface. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 6ab7738..1025b20 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3800,13 +3800,13 @@ public final class Settings { /** {@hide} */ public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold"; /** {@hide} */ - public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration"; + public static final String NETSTATS_NETWORK_BUCKET_DURATION = "netstats_network_bucket_duration"; /** {@hide} */ - public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history"; + public static final String NETSTATS_NETWORK_MAX_HISTORY = "netstats_network_max_history"; /** {@hide} */ - public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration"; + public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration"; /** {@hide} */ - public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history"; + public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history"; /** * @hide diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index c397af9..376e0bb 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -19,6 +19,7 @@ import android.content.Intent; import android.database.ContentObserver; import android.net.Uri; import android.provider.CallLog.Calls; + /** * The contract between the voicemail provider and applications. Contains * definitions for the supported URIs and columns. @@ -45,13 +46,17 @@ import android.provider.CallLog.Calls; */ // TODO: unhide when the API is approved by android-api-council public class VoicemailContract { + /** Not instantiable. */ + private VoicemailContract() { + } + /** The authority used by the voicemail provider. */ public static final String AUTHORITY = "com.android.voicemail"; /** URI to insert/retrieve all voicemails. */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/voicemail"); - /** URI to insert/retrieve voicemails by a given voicemai source. */ + /** URI to insert/retrieve voicemails by a given voicemail source. */ public static final Uri CONTENT_URI_SOURCE = Uri.parse("content://" + AUTHORITY + "/voicemail/source/"); @@ -72,6 +77,10 @@ public class VoicemailContract { "vnd.android.cursor.dir/voicemails"; public static final class Voicemails implements BaseColumns { + /** Not instantiable. */ + private Voicemails() { + } + /** * Phone number of the voicemail sender. * <P>Type: TEXT</P> diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 40b0a9c..4c47d37 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -711,6 +711,17 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback requestBindService(); } + @Override + protected void finalize() throws Throwable { + try { + if (mWorkerThread != null) { + mWorkerThread.quit(); + } + } finally { + super.finalize(); + } + } + private void loadNextIndexInBackground() { mWorkerQueue.post(new Runnable() { @Override diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java index 8a3e871..5250a7c 100644 --- a/core/tests/coretests/src/android/net/NetworkStatsTest.java +++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java @@ -16,7 +16,6 @@ package android.net; -import android.os.SystemClock; import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; @@ -25,12 +24,14 @@ import junit.framework.TestCase; public class NetworkStatsTest extends TestCase { private static final String TEST_IFACE = "test0"; + private static final int TEST_UID = 1001; + private static final long TEST_START = 1194220800000L; public void testFindIndex() throws Exception { - final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + final NetworkStats stats = new NetworkStats(TEST_START, 3) .addEntry(TEST_IFACE, 100, 1024, 0) .addEntry(TEST_IFACE, 101, 0, 1024) - .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + .addEntry(TEST_IFACE, 102, 1024, 1024); assertEquals(2, stats.findIndex(TEST_IFACE, 102)); assertEquals(2, stats.findIndex(TEST_IFACE, 102)); @@ -38,14 +39,40 @@ public class NetworkStatsTest extends TestCase { assertEquals(-1, stats.findIndex(TEST_IFACE, 6)); } + public void testAddEntryGrow() throws Exception { + final NetworkStats stats = new NetworkStats(TEST_START, 2); + + assertEquals(0, stats.size); + assertEquals(2, stats.iface.length); + + stats.addEntry(TEST_IFACE, TEST_UID, 1L, 2L); + stats.addEntry(TEST_IFACE, TEST_UID, 2L, 2L); + + assertEquals(2, stats.size); + assertEquals(2, stats.iface.length); + + stats.addEntry(TEST_IFACE, TEST_UID, 3L, 4L); + stats.addEntry(TEST_IFACE, TEST_UID, 4L, 4L); + stats.addEntry(TEST_IFACE, TEST_UID, 5L, 5L); + + assertEquals(5, stats.size); + assertTrue(stats.iface.length >= 5); + + assertEquals(1L, stats.rx[0]); + assertEquals(2L, stats.rx[1]); + assertEquals(3L, stats.rx[2]); + assertEquals(4L, stats.rx[3]); + assertEquals(5L, stats.rx[4]); + } + public void testSubtractIdenticalData() throws Exception { - final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + final NetworkStats before = new NetworkStats(TEST_START, 2) .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024).build(); + .addEntry(TEST_IFACE, 101, 0, 1024); - final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + final NetworkStats after = new NetworkStats(TEST_START, 2) .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024).build(); + .addEntry(TEST_IFACE, 101, 0, 1024); final NetworkStats result = after.subtract(before); @@ -57,13 +84,13 @@ public class NetworkStatsTest extends TestCase { } public void testSubtractIdenticalRows() throws Exception { - final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + final NetworkStats before = new NetworkStats(TEST_START, 2) .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024).build(); + .addEntry(TEST_IFACE, 101, 0, 1024); - final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + final NetworkStats after = new NetworkStats(TEST_START, 2) .addEntry(TEST_IFACE, 100, 1025, 2) - .addEntry(TEST_IFACE, 101, 3, 1028).build(); + .addEntry(TEST_IFACE, 101, 3, 1028); final NetworkStats result = after.subtract(before); @@ -75,14 +102,14 @@ public class NetworkStatsTest extends TestCase { } public void testSubtractNewRows() throws Exception { - final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2) + final NetworkStats before = new NetworkStats(TEST_START, 2) .addEntry(TEST_IFACE, 100, 1024, 0) - .addEntry(TEST_IFACE, 101, 0, 1024).build(); + .addEntry(TEST_IFACE, 101, 0, 1024); - final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3) + final NetworkStats after = new NetworkStats(TEST_START, 3) .addEntry(TEST_IFACE, 100, 1024, 0) .addEntry(TEST_IFACE, 101, 0, 1024) - .addEntry(TEST_IFACE, 102, 1024, 1024).build(); + .addEntry(TEST_IFACE, 102, 1024, 1024); final NetworkStats result = after.subtract(before); diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index 8f179f5..2190b30 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -882,8 +882,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); final String[] ifaces = listInterfaces(); - final NetworkStats.Builder stats = new NetworkStats.Builder( - SystemClock.elapsedRealtime(), ifaces.length); + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length); for (String iface : ifaces) { final long rx = getInterfaceCounter(iface, true); @@ -891,7 +890,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx); } - return stats.build(); + return stats; } @Override @@ -900,7 +899,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); final String[] knownUids = PATH_PROC_UID_STAT.list(); - final NetworkStats.Builder stats = new NetworkStats.Builder( + final NetworkStats stats = new NetworkStats( SystemClock.elapsedRealtime(), knownUids.length); for (String uid : knownUids) { @@ -908,7 +907,7 @@ class NetworkManagementService extends INetworkManagementService.Stub { collectNetworkStatsDetail(stats, uidInt); } - return stats.build(); + return stats; } @Override @@ -918,13 +917,12 @@ class NetworkManagementService extends INetworkManagementService.Stub { android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); } - final NetworkStats.Builder stats = new NetworkStats.Builder( - SystemClock.elapsedRealtime(), 1); + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); collectNetworkStatsDetail(stats, uid); - return stats.build(); + return stats; } - private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) { + private void collectNetworkStatsDetail(NetworkStats stats, int uid) { // TODO: kernel module will provide interface-level stats in future // TODO: migrate these stats to come across netd in bulk, instead of all // these individual file reads. diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java index f7a7c49..4a207f7 100644 --- a/services/java/com/android/server/net/NetworkIdentity.java +++ b/services/java/com/android/server/net/NetworkIdentity.java @@ -26,6 +26,7 @@ import static android.net.TrafficStats.TEMPLATE_WIFI; import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G; import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G; +import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN; import static android.telephony.TelephonyManager.getNetworkClass; import android.content.Context; @@ -148,6 +149,7 @@ public class NetworkIdentity { if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) { switch (getNetworkClass(subType)) { + case NETWORK_CLASS_UNKNOWN: case NETWORK_CLASS_2_G: case NETWORK_CLASS_3_G: return true; diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 2766093..e7d6063 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -20,7 +20,9 @@ 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.MANAGE_NETWORK_POLICY; +import static android.Manifest.permission.READ_PHONE_STATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.NetworkPolicy.LIMIT_DISABLED; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; @@ -28,6 +30,8 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_PAID; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkPolicyManager.dumpPolicy; import static android.net.NetworkPolicyManager.dumpRules; +import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL; +import static android.net.TrafficStats.isNetworkTemplateMobile; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -53,6 +57,8 @@ import android.os.HandlerThread; import android.os.IPowerManager; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.telephony.TelephonyManager; +import android.text.format.Time; import android.util.NtpTrustedTime; import android.util.Slog; import android.util.SparseArray; @@ -96,9 +102,14 @@ import libcore.io.IoUtils; public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG = "NetworkPolicy"; private static final boolean LOGD = true; + private static final boolean LOGV = false; private static final int VERSION_CURRENT = 1; + private static final long KB_IN_BYTES = 1024; + private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; + private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; + 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"; @@ -126,14 +137,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private boolean mScreenOn; - /** Current network policy for each UID. */ + /** Current policy for network templates. */ + private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList(); + + /** Current policy for each UID. */ private SparseIntArray mUidPolicy = new SparseIntArray(); /** 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< @@ -150,8 +161,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: keep whitelist of system-critical services that should never have // rules enforced, such as system, phone, and radio UIDs. - // TODO: dispatch callbacks through handler when locked - public NetworkPolicyManagerService(Context context, IActivityManager activityManager, IPowerManager powerManager, INetworkStatsService networkStats) { // TODO: move to using cached NtpTrustedTime @@ -264,8 +273,7 @@ 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}. + * interfaces. Used to apply {@link NetworkPolicy} to matching networks. */ private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() { @Override @@ -273,6 +281,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // on background handler thread, and verified CONNECTIVITY_INTERNAL // permission above. synchronized (mRulesLock) { + ensureActiveMobilePolicyLocked(); updateIfacesLocked(); } } @@ -284,7 +293,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * remaining quota based on usage cycle and historical stats. */ private void updateIfacesLocked() { - if (LOGD) Slog.v(TAG, "updateIfacesLocked()"); + if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); final NetworkState[] states; try { @@ -307,14 +316,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } // build list of rules and ifaces to enforce them against - final HashMap<StrongTemplate, String[]> rules = Maps.newHashMap(); + final HashMap<NetworkPolicy, String[]> rules = Maps.newHashMap(); final ArrayList<String> ifaceList = Lists.newArrayList(); - for (StrongTemplate template : mTemplatePolicy.keySet()) { + for (NetworkPolicy policy : mNetworkPolicy) { // collect all active ifaces that match this template ifaceList.clear(); for (NetworkIdentity ident : networks.keySet()) { - if (ident.matchesTemplate(template.networkTemplate, template.subscriberId)) { + if (ident.matchesTemplate(policy.networkTemplate, policy.subscriberId)) { final String iface = networks.get(ident); ifaceList.add(iface); } @@ -322,7 +331,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (ifaceList.size() > 0) { final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]); - rules.put(template, ifaces); + rules.put(policy, ifaces); } } @@ -336,41 +345,81 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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); + for (NetworkPolicy policy : rules.keySet()) { final String[] ifaces = rules.get(policy); final long start = computeLastCycleBoundary(currentTime, policy); final long end = currentTime; final NetworkStats stats; + final long total; try { stats = mNetworkStats.getSummaryForNetwork( - start, end, template.networkTemplate, template.subscriberId); + start, end, policy.networkTemplate, policy.subscriberId); + total = stats.rx[0] + stats.tx[0]; } catch (RemoteException e) { - Slog.w(TAG, "problem reading summary for template " + template.networkTemplate); + Slog.w(TAG, "problem reading summary for template " + policy.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); + + Arrays.toString(ifaces)); + } + + // TODO: register for warning notification trigger through NMS + + if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { + // remaining "quota" is based on usage in current cycle + final long quota = Math.max(0, policy.limitBytes - total); + + // TODO: push quota rule down through NMS + } + } + } + + /** + * Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we + * have at least a default mobile policy defined. + */ + private void ensureActiveMobilePolicyLocked() { + if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()"); + final String subscriberId = getActiveSubscriberId(); + if (subscriberId == null) { + if (LOGV) Slog.v(TAG, "no active mobile network, ignoring policy check"); + return; + } + + // examine to see if any policy is defined for active mobile + boolean mobileDefined = false; + for (NetworkPolicy policy : mNetworkPolicy) { + if (isNetworkTemplateMobile(policy.networkTemplate) + && Objects.equal(subscriberId, policy.subscriberId)) { + mobileDefined = true; } + } + + if (!mobileDefined) { + Slog.i(TAG, "no policy for active mobile network; generating default policy"); - // TODO: push rule down through NetworkManagementService.setInterfaceQuota() + // default mobile policy has combined 4GB warning, and assume usage + // cycle starts today today. + // TODO: move this policy definition to overlay or secure setting + final Time time = new Time(Time.TIMEZONE_UTC); + time.setToNow(); + final int cycleDay = time.monthDay; + + mNetworkPolicy.add(new NetworkPolicy( + TEMPLATE_MOBILE_ALL, subscriberId, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED)); } } private void readPolicyLocked() { - if (LOGD) Slog.v(TAG, "readPolicyLocked()"); + if (LOGV) Slog.v(TAG, "readPolicyLocked()"); // clear any existing policy and read from disk - mTemplatePolicy.clear(); + mNetworkPolicy.clear(); mUidPolicy.clear(); FileInputStream fis = null; @@ -390,13 +439,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } 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)); + mNetworkPolicy.add(new NetworkPolicy( + networkTemplate, subscriberId, cycleDay, warningBytes, limitBytes)); } else if (TAG_UID_POLICY.equals(tag)) { final int uid = readIntAttribute(in, ATTR_UID); @@ -419,7 +467,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void writePolicyLocked() { - if (LOGD) Slog.v(TAG, "writePolicyLocked()"); + if (LOGV) Slog.v(TAG, "writePolicyLocked()"); FileOutputStream fos = null; try { @@ -433,13 +481,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { writeIntAttribute(out, ATTR_VERSION, VERSION_CURRENT); // write all known network policies - for (StrongTemplate template : mTemplatePolicy.keySet()) { - final NetworkPolicy policy = mTemplatePolicy.get(template); - + for (NetworkPolicy policy : mNetworkPolicy) { 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_NETWORK_TEMPLATE, policy.networkTemplate); + if (policy.subscriberId != null) { + out.attribute(null, ATTR_SUBSCRIBER_ID, policy.subscriberId); } writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay); writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes); @@ -519,25 +565,27 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } @Override - public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) { + public void setNetworkPolicies(NetworkPolicy[] policies) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); synchronized (mRulesLock) { - mTemplatePolicy.put(new StrongTemplate(networkType, subscriberId), policy); + mNetworkPolicy.clear(); + for (NetworkPolicy policy : policies) { + mNetworkPolicy.add(policy); + } - // network policy changed, recompute template rules based on active - // interfaces and persist policy. updateIfacesLocked(); writePolicyLocked(); } } @Override - public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) { + public NetworkPolicy[] getNetworkPolicies() { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, TAG); synchronized (mRulesLock) { - return mTemplatePolicy.get(new StrongTemplate(networkType, subscriberId)); + return mNetworkPolicy.toArray(new NetworkPolicy[mNetworkPolicy.size()]); } } @@ -547,10 +595,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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()); + for (NetworkPolicy policy : mNetworkPolicy) { + fout.print(" "); fout.println(policy.toString()); } fout.println("Policy status for known UIDs:"); @@ -682,6 +728,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mListeners.finishBroadcast(); } + private String getActiveSubscriberId() { + final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService( + Context.TELEPHONY_SERVICE); + return telephony.getSubscriberId(); + } + private static void collectKeys(SparseIntArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { @@ -734,40 +786,4 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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 161c393..de69849 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -23,12 +23,12 @@ import static android.Manifest.permission.SHUTDOWN; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.UID_ALL; -import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION; -import static android.provider.Settings.Secure.NETSTATS_DETAIL_MAX_HISTORY; +import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION; +import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY; import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD; import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL; -import static android.provider.Settings.Secure.NETSTATS_SUMMARY_BUCKET_DURATION; -import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY; +import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION; +import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; @@ -38,6 +38,7 @@ import android.app.AlarmManager; import android.app.IAlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -88,6 +89,7 @@ import libcore.io.IoUtils; public class NetworkStatsService extends INetworkStatsService.Stub { private static final String TAG = "NetworkStats"; private static final boolean LOGD = true; + private static final boolean LOGV = false; /** File header magic number: "ANET" */ private static final int FILE_MAGIC = 0x414E4554; @@ -97,6 +99,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final INetworkManagementService mNetworkManager; private final IAlarmManager mAlarmManager; private final TrustedTime mTime; + private final NetworkStatsSettings mSettings; private IConnectivityManager mConnManager; @@ -112,22 +115,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES; private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES; - private LongSecureSetting mPollInterval = new LongSecureSetting( - NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS); - private LongSecureSetting mPersistThreshold = new LongSecureSetting( - NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES); - - // TODO: adjust these timings for production builds - private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting( - NETSTATS_SUMMARY_BUCKET_DURATION, 1 * HOUR_IN_MILLIS); - private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting( - NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS); - private LongSecureSetting mDetailBucketDuration = new LongSecureSetting( - NETSTATS_DETAIL_BUCKET_DURATION, 2 * HOUR_IN_MILLIS); - private LongSecureSetting mDetailMaxHistory = new LongSecureSetting( - NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS); - - private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; + /** + * Settings that can be changed externally. + */ + public interface NetworkStatsSettings { + public long getPollInterval(); + public long getPersistThreshold(); + public long getNetworkBucketDuration(); + public long getNetworkMaxHistory(); + public long getUidBucketDuration(); + public long getUidMaxHistory(); + public long getTimeCacheMaxAge(); + } private final Object mStatsLock = new Object(); @@ -135,19 +134,23 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap(); /** Set of historical stats for known ifaces. */ - private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap(); + private HashMap<InterfaceIdentity, NetworkStatsHistory> mNetworkStats = Maps.newHashMap(); /** Set of historical stats for known UIDs. */ - private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>(); + private SparseArray<NetworkStatsHistory> mUidStats = new SparseArray<NetworkStatsHistory>(); + + /** Flag if {@link #mUidStats} have been loaded from disk. */ + private boolean mUidStatsLoaded = false; - private NetworkStats mLastSummaryPoll; - private NetworkStats mLastSummaryPersist; + private NetworkStats mLastNetworkPoll; + private NetworkStats mLastNetworkPersist; - private NetworkStats mLastDetailPoll; + private NetworkStats mLastUidPoll; private final HandlerThread mHandlerThread; private final Handler mHandler; - private final AtomicFile mSummaryFile; + private final AtomicFile mNetworkFile; + private final AtomicFile mUidFile; // TODO: collect detailed uid stats, storing tag-granularity data until next // dropbox, and uid summary for a specific bucket count. @@ -157,7 +160,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsService( Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) { // TODO: move to using cached NtpTrustedTime - this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir()); + this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(), + new DefaultNetworkStatsSettings(context)); } private static File getSystemDir() { @@ -165,17 +169,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } public NetworkStatsService(Context context, INetworkManagementService networkManager, - IAlarmManager alarmManager, TrustedTime time, File systemDir) { + IAlarmManager alarmManager, TrustedTime time, File systemDir, + NetworkStatsSettings settings) { mContext = checkNotNull(context, "missing Context"); mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService"); mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager"); mTime = checkNotNull(time, "missing TrustedTime"); + mSettings = checkNotNull(settings, "missing NetworkStatsSettings"); mHandlerThread = new HandlerThread(TAG); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); - mSummaryFile = new AtomicFile(new File(systemDir, "netstats.bin")); + mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin")); + mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin")); } public void bindConnectivityManager(IConnectivityManager connManager) { @@ -184,8 +191,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public void systemReady() { synchronized (mStatsLock) { - // read historical stats from disk - readStatsLocked(); + // read historical network stats from disk, since policy service + // might need them right away. we delay loading detailed UID stats + // until actually needed. + readNetworkStatsLocked(); } // watch for network interfaces to be claimed @@ -214,14 +223,16 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.unregisterReceiver(mPollReceiver); mContext.unregisterReceiver(mShutdownReceiver); - writeStatsLocked(); - mSummaryStats.clear(); - mDetailStats.clear(); + writeNetworkStatsLocked(); + writeUidStatsLocked(); + mNetworkStats.clear(); + mUidStats.clear(); + mUidStatsLoaded = false; } /** * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and - * reschedule based on current {@link #mPollInterval} value. + * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}. */ private void registerPollAlarmLocked() throws RemoteException { if (mPollIntent != null) { @@ -232,8 +243,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0); final long currentRealtime = SystemClock.elapsedRealtime(); - mAlarmManager.setInexactRepeating( - AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent); + mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime, + mSettings.getPollInterval(), mPollIntent); } @Override @@ -244,9 +255,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // combine all interfaces that match template final String subscriberId = getActiveSubscriberId(); final NetworkStatsHistory combined = new NetworkStatsHistory( - mSummaryBucketDuration.get()); - for (InterfaceIdentity ident : mSummaryStats.keySet()) { - final NetworkStatsHistory history = mSummaryStats.get(ident); + mSettings.getNetworkBucketDuration(), estimateNetworkBuckets()); + for (InterfaceIdentity ident : mNetworkStats.keySet()) { + final NetworkStatsHistory history = mNetworkStats.get(ident); if (ident.matchesTemplate(networkTemplate, subscriberId)) { combined.recordEntireHistory(history); } @@ -259,8 +270,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) { mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); - // TODO: return history for requested uid - return null; + synchronized (mStatsLock) { + // TODO: combine based on template, if we store that granularity + ensureUidStatsLoadedLocked(); + return mUidStats.get(uid); + } } @Override @@ -274,8 +288,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { long[] networkTotal = new long[2]; // combine total from all interfaces that match template - for (InterfaceIdentity ident : mSummaryStats.keySet()) { - final NetworkStatsHistory history = mSummaryStats.get(ident); + for (InterfaceIdentity ident : mNetworkStats.keySet()) { + final NetworkStatsHistory history = mNetworkStats.get(ident); if (ident.matchesTemplate(networkTemplate, subscriberId)) { networkTotal = history.getTotalData(start, end, networkTotal); rx += networkTotal[0]; @@ -283,9 +297,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, 1); + final NetworkStats stats = new NetworkStats(end - start, 1); stats.addEntry(IFACE_ALL, UID_ALL, tx, tx); - return stats.build(); + return stats; } } @@ -296,17 +310,19 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // TODO: apply networktemplate once granular uid stats are stored. synchronized (mStatsLock) { - final int size = mDetailStats.size(); - final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size); + ensureUidStatsLoadedLocked(); + + final int size = mUidStats.size(); + final NetworkStats stats = new NetworkStats(end - start, size); long[] total = new long[2]; for (int i = 0; i < size; i++) { - final int uid = mDetailStats.keyAt(i); - final NetworkStatsHistory history = mDetailStats.valueAt(i); + final int uid = mUidStats.keyAt(i); + final NetworkStatsHistory history = mUidStats.valueAt(i); total = history.getTotalData(start, end, total); stats.addEntry(IFACE_ALL, uid, total[0], total[1]); } - return stats.build(); + return stats; } } @@ -333,7 +349,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // permission above. synchronized (mStatsLock) { // TODO: acquire wakelock while performing poll - performPollLocked(); + performPollLocked(true); } } }; @@ -355,13 +371,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * {@link InterfaceIdentity}. */ private void updateIfacesLocked() { - if (LOGD) Slog.v(TAG, "updateIfacesLocked()"); + if (LOGV) Slog.v(TAG, "updateIfacesLocked()"); // take one last stats snapshot before updating iface mapping. this // isn't perfect, since the kernel may already be counting traffic from // the updated network. - // TODO: verify that we only poll summary stats, not uid details - performPollLocked(); + performPollLocked(false); final NetworkState[] states; try { @@ -384,11 +399,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private void performPollLocked() { - if (LOGD) Slog.v(TAG, "performPollLocked()"); + /** + * Periodic poll operation, reading current statistics and recording into + * {@link NetworkStatsHistory}. + * + * @param detailedPoll Indicate if detailed UID stats should be collected + * during this poll operation. + */ + private void performPollLocked(boolean detailedPoll) { + if (LOGV) Slog.v(TAG, "performPollLocked()"); // try refreshing time source when stale - if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { + if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { mTime.forceRefresh(); } @@ -396,42 +418,45 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); - final NetworkStats summary; - final NetworkStats detail; + final NetworkStats networkStats; + final NetworkStats uidStats; try { - summary = mNetworkManager.getNetworkStatsSummary(); - detail = mNetworkManager.getNetworkStatsDetail(); + networkStats = mNetworkManager.getNetworkStatsSummary(); + uidStats = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null; } catch (RemoteException e) { Slog.w(TAG, "problem reading network stats"); return; } - performSummaryPollLocked(summary, currentTime); - performDetailPollLocked(detail, currentTime); + performNetworkPollLocked(networkStats, currentTime); + if (detailedPoll) { + performUidPollLocked(uidStats, currentTime); + } // decide if enough has changed to trigger persist - final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary); - final long persistThreshold = mPersistThreshold.get(); + final NetworkStats persistDelta = computeStatsDelta(mLastNetworkPersist, networkStats); + final long persistThreshold = mSettings.getPersistThreshold(); for (String iface : persistDelta.getUniqueIfaces()) { final int index = persistDelta.findIndex(iface, UID_ALL); if (persistDelta.rx[index] > persistThreshold || persistDelta.tx[index] > persistThreshold) { - writeStatsLocked(); - mLastSummaryPersist = summary; + writeNetworkStatsLocked(); + writeUidStatsLocked(); + mLastNetworkPersist = networkStats; break; } } } /** - * Update {@link #mSummaryStats} historical usage. + * Update {@link #mNetworkStats} historical usage. */ - private void performSummaryPollLocked(NetworkStats summary, long currentTime) { + private void performNetworkPollLocked(NetworkStats networkStats, long currentTime) { final ArrayList<String> unknownIface = Lists.newArrayList(); - final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary); + final NetworkStats delta = computeStatsDelta(mLastNetworkPoll, networkStats); final long timeStart = currentTime - delta.elapsedRealtime; - final long maxHistory = mSummaryMaxHistory.get(); + final long maxHistory = mSettings.getNetworkMaxHistory(); for (String iface : delta.getUniqueIfaces()) { final InterfaceIdentity ident = mActiveIface.get(iface); if (ident == null) { @@ -443,11 +468,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long rx = delta.rx[index]; final long tx = delta.tx[index]; - final NetworkStatsHistory history = findOrCreateSummaryLocked(ident); + final NetworkStatsHistory history = findOrCreateNetworkLocked(ident); history.recordData(timeStart, currentTime, rx, tx); history.removeBucketsBefore(currentTime - maxHistory); } - mLastSummaryPoll = summary; + mLastNetworkPoll = networkStats; if (LOGD && unknownIface.size() > 0) { Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats"); @@ -455,40 +480,71 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Update {@link #mDetailStats} historical usage. + * Update {@link #mUidStats} historical usage. */ - private void performDetailPollLocked(NetworkStats detail, long currentTime) { - final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail); + private void performUidPollLocked(NetworkStats uidStats, long currentTime) { + ensureUidStatsLoadedLocked(); + + final NetworkStats delta = computeStatsDelta(mLastUidPoll, uidStats); final long timeStart = currentTime - delta.elapsedRealtime; - final long maxHistory = mDetailMaxHistory.get(); + final long maxHistory = mSettings.getUidMaxHistory(); for (int uid : delta.getUniqueUids()) { + // TODO: traverse all ifaces once surfaced in stats final int index = delta.findIndex(IFACE_ALL, uid); - final long rx = delta.rx[index]; - final long tx = delta.tx[index]; + if (index != -1) { + final long rx = delta.rx[index]; + final long tx = delta.tx[index]; - final NetworkStatsHistory history = findOrCreateDetailLocked(uid); - history.recordData(timeStart, currentTime, rx, tx); - history.removeBucketsBefore(currentTime - maxHistory); + final NetworkStatsHistory history = findOrCreateUidLocked(uid); + history.recordData(timeStart, currentTime, rx, tx); + history.removeBucketsBefore(currentTime - maxHistory); + } } - mLastDetailPoll = detail; + mLastUidPoll = uidStats; } - private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) { - NetworkStatsHistory stats = mSummaryStats.get(ident); - if (stats == null) { - stats = new NetworkStatsHistory(mSummaryBucketDuration.get()); - mSummaryStats.put(ident, stats); + private NetworkStatsHistory findOrCreateNetworkLocked(InterfaceIdentity ident) { + final long bucketDuration = mSettings.getNetworkBucketDuration(); + final NetworkStatsHistory existing = mNetworkStats.get(ident); + + // update when no existing, or when bucket duration changed + NetworkStatsHistory updated = null; + if (existing == null) { + updated = new NetworkStatsHistory(bucketDuration, 10); + } else if (existing.bucketDuration != bucketDuration) { + updated = new NetworkStatsHistory( + bucketDuration, estimateResizeBuckets(existing, bucketDuration)); + updated.recordEntireHistory(existing); + } + + if (updated != null) { + mNetworkStats.put(ident, updated); + return updated; + } else { + return existing; } - return stats; } - private NetworkStatsHistory findOrCreateDetailLocked(int uid) { - NetworkStatsHistory stats = mDetailStats.get(uid); - if (stats == null) { - stats = new NetworkStatsHistory(mDetailBucketDuration.get()); - mDetailStats.put(uid, stats); + private NetworkStatsHistory findOrCreateUidLocked(int uid) { + final long bucketDuration = mSettings.getUidBucketDuration(); + final NetworkStatsHistory existing = mUidStats.get(uid); + + // update when no existing, or when bucket duration changed + NetworkStatsHistory updated = null; + if (existing == null) { + updated = new NetworkStatsHistory(bucketDuration, 10); + } else if (existing.bucketDuration != bucketDuration) { + updated = new NetworkStatsHistory( + bucketDuration, estimateResizeBuckets(existing, bucketDuration)); + updated.recordEntireHistory(existing); + } + + if (updated != null) { + mUidStats.put(uid, updated); + return updated; + } else { + return existing; } - return stats; } private InterfaceIdentity findOrCreateInterfaceLocked(String iface) { @@ -500,15 +556,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return ident; } - private void readStatsLocked() { - if (LOGD) Slog.v(TAG, "readStatsLocked()"); + private void readNetworkStatsLocked() { + if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()"); // clear any existing stats and read from disk - mSummaryStats.clear(); + mNetworkStats.clear(); FileInputStream fis = null; try { - fis = mSummaryFile.openRead(); + fis = mNetworkFile.openRead(); final DataInputStream in = new DataInputStream(fis); // verify file magic header intact @@ -521,13 +577,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { switch (version) { case VERSION_CURRENT: { // file format is pairs of interfaces and stats: - // summary := size *(InterfaceIdentity NetworkStatsHistory) + // network := size *(InterfaceIdentity NetworkStatsHistory) final int size = in.readInt(); for (int i = 0; i < size; i++) { final InterfaceIdentity ident = new InterfaceIdentity(in); final NetworkStatsHistory history = new NetworkStatsHistory(in); - mSummaryStats.put(ident, history); + + mNetworkStats.put(ident, history); } break; } @@ -544,30 +601,113 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } - private void writeStatsLocked() { - if (LOGD) Slog.v(TAG, "writeStatsLocked()"); + private void ensureUidStatsLoadedLocked() { + if (!mUidStatsLoaded) { + readUidStatsLocked(); + mUidStatsLoaded = true; + } + } + + private void readUidStatsLocked() { + if (LOGV) Slog.v(TAG, "readUidStatsLocked()"); + + // clear any existing stats and read from disk + mUidStats.clear(); + + FileInputStream fis = null; + try { + fis = mUidFile.openRead(); + final DataInputStream in = new DataInputStream(fis); + + // verify file magic header intact + final int magic = in.readInt(); + if (magic != FILE_MAGIC) { + throw new ProtocolException("unexpected magic: " + magic); + } + + final int version = in.readInt(); + switch (version) { + case VERSION_CURRENT: { + // file format is pairs of UIDs and stats: + // uid := size *(UID NetworkStatsHistory) + + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final int uid = in.readInt(); + final NetworkStatsHistory history = new NetworkStatsHistory(in); + + mUidStats.put(uid, history); + } + break; + } + default: { + throw new ProtocolException("unexpected version: " + version); + } + } + } catch (FileNotFoundException e) { + // missing stats is okay, probably first boot + } catch (IOException e) { + Slog.e(TAG, "problem reading uid stats", e); + } finally { + IoUtils.closeQuietly(fis); + } + } + + private void writeNetworkStatsLocked() { + if (LOGV) Slog.v(TAG, "writeNetworkStatsLocked()"); // TODO: consider duplicating stats and releasing lock while writing FileOutputStream fos = null; try { - fos = mSummaryFile.startWrite(); + fos = mNetworkFile.startWrite(); final DataOutputStream out = new DataOutputStream(fos); out.writeInt(FILE_MAGIC); out.writeInt(VERSION_CURRENT); - out.writeInt(mSummaryStats.size()); - for (InterfaceIdentity ident : mSummaryStats.keySet()) { - final NetworkStatsHistory history = mSummaryStats.get(ident); + out.writeInt(mNetworkStats.size()); + for (InterfaceIdentity ident : mNetworkStats.keySet()) { + final NetworkStatsHistory history = mNetworkStats.get(ident); ident.writeToStream(out); history.writeToStream(out); } - mSummaryFile.finishWrite(fos); + mNetworkFile.finishWrite(fos); } catch (IOException e) { if (fos != null) { - mSummaryFile.failWrite(fos); + mNetworkFile.failWrite(fos); + } + } + } + + private void writeUidStatsLocked() { + if (LOGV) Slog.v(TAG, "writeUidStatsLocked()"); + + // TODO: consider duplicating stats and releasing lock while writing + + FileOutputStream fos = null; + try { + fos = mUidFile.startWrite(); + final DataOutputStream out = new DataOutputStream(fos); + + out.writeInt(FILE_MAGIC); + out.writeInt(VERSION_CURRENT); + + final int size = mUidStats.size(); + + out.writeInt(size); + for (int i = 0; i < size; i++) { + final int uid = mUidStats.keyAt(i); + final NetworkStatsHistory history = mUidStats.valueAt(i); + out.writeInt(uid); + history.writeToStream(out); + } + + mUidFile.finishWrite(fos); + } catch (IOException e) { + if (fos != null) { + mUidFile.failWrite(fos); } } } @@ -590,7 +730,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } if (argSet.contains("poll")) { - performPollLocked(); + performPollLocked(true); pw.println("Forced poll"); return; } @@ -603,17 +743,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } pw.println("Known historical stats:"); - for (InterfaceIdentity ident : mSummaryStats.keySet()) { - final NetworkStatsHistory stats = mSummaryStats.get(ident); + for (InterfaceIdentity ident : mNetworkStats.keySet()) { + final NetworkStatsHistory stats = mNetworkStats.get(ident); pw.print(" ident="); pw.println(ident.toString()); stats.dump(" ", pw); } if (argSet.contains("detail")) { - pw.println("Known detail stats:"); - for (int i = 0; i < mDetailStats.size(); i++) { - final int uid = mDetailStats.keyAt(i); - final NetworkStatsHistory stats = mDetailStats.valueAt(i); + // since explicitly requested with argument, we're okay to load + // from disk if not already in memory. + ensureUidStatsLoadedLocked(); + pw.println("Known UID stats:"); + for (int i = 0; i < mUidStats.size(); i++) { + final int uid = mUidStats.keyAt(i); + final NetworkStatsHistory stats = mUidStats.valueAt(i); pw.print(" UID="); pw.println(uid); stats.dump(" ", pw); } @@ -627,47 +770,29 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Deprecated private void generateRandomLocked() { long end = System.currentTimeMillis(); - long start = end - mSummaryMaxHistory.get(); + long start = end - mSettings.getNetworkMaxHistory(); long rx = 3 * GB_IN_BYTES; long tx = 2 * GB_IN_BYTES; - mSummaryStats.clear(); + mNetworkStats.clear(); for (InterfaceIdentity ident : mActiveIface.values()) { - final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident); + final NetworkStatsHistory stats = findOrCreateNetworkLocked(ident); stats.generateRandom(start, end, rx, tx); } end = System.currentTimeMillis(); - start = end - mDetailMaxHistory.get(); + start = end - mSettings.getUidMaxHistory(); rx = 500 * MB_IN_BYTES; tx = 100 * MB_IN_BYTES; - mDetailStats.clear(); + mUidStats.clear(); for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) { final int uid = info.uid; - final NetworkStatsHistory stats = findOrCreateDetailLocked(uid); + final NetworkStatsHistory stats = findOrCreateUidLocked(uid); stats.generateRandom(start, end, rx, tx); } } - private class LongSecureSetting { - private String mKey; - private long mDefaultValue; - - public LongSecureSetting(String key, long defaultValue) { - mKey = key; - mDefaultValue = defaultValue; - } - - public long get() { - if (mContext != null) { - return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue); - } else { - return mDefaultValue; - } - } - } - /** * Return the delta between two {@link NetworkStats} snapshots, where {@code * before} can be {@code null}. @@ -686,4 +811,54 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return telephony.getSubscriberId(); } + private int estimateNetworkBuckets() { + return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration()); + } + + private int estimateUidBuckets() { + return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration()); + } + + private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) { + return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration); + } + + /** + * Default external settings that read from {@link Settings.Secure}. + */ + private static class DefaultNetworkStatsSettings implements NetworkStatsSettings { + private final ContentResolver mResolver; + + public DefaultNetworkStatsSettings(Context context) { + mResolver = checkNotNull(context.getContentResolver()); + // TODO: adjust these timings for production builds + } + + private long getSecureLong(String name, long def) { + return Settings.Secure.getLong(mResolver, name, def); + } + + public long getPollInterval() { + return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS); + } + public long getPersistThreshold() { + return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES); + } + public long getNetworkBucketDuration() { + return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS); + } + public long getNetworkMaxHistory() { + return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS); + } + public long getUidBucketDuration() { + return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS); + } + public long getUidMaxHistory() { + return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS); + } + public long getTimeCacheMaxAge() { + return DAY_IN_MILLIS; + } + } + } diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 1eeb56b..476aded 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -297,7 +297,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 5, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -307,7 +307,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 20, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -317,7 +317,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -327,7 +327,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L); final long actualCycle = computeLastCycleBoundary(currentTime, policy); assertEquals(expectedCycle, actualCycle); } @@ -357,8 +357,8 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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(); + stats = new NetworkStats(elapsedRealtime, 1) + .addEntry(TEST_IFACE, UID_ALL, 256L, 256L); expect(mStatsService.getSummaryForNetwork(TIME_FEB_15, TIME_MAR_10, TEMPLATE_WIFI, null)) .andReturn(stats).atLeastOnce(); @@ -366,7 +366,7 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { // TODO: write up NetworkManagementService mock replay(); - mService.setNetworkPolicy(TEMPLATE_WIFI, null, new NetworkPolicy(CYCLE_DAY, 1024L, 2048L)); + setNetworkPolicies(new NetworkPolicy(TEMPLATE_WIFI, null, CYCLE_DAY, 1024L, 2048L)); verifyAndReset(); } @@ -376,6 +376,10 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { return result.toMillis(true); } + private void setNetworkPolicies(NetworkPolicy... policies) { + mService.setNetworkPolicies(policies); + } + private static NetworkState buildWifi() { final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null); info.setDetailedState(DetailedState.CONNECTED, null, null); diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 9846372..2457ff3 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -18,10 +18,13 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.ConnectivityManager.TYPE_WIFI; +import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.TEMPLATE_WIFI; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; +import static android.text.format.DateUtils.MINUTE_IN_MILLIS; +import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; import static org.easymock.EasyMock.anyLong; import static org.easymock.EasyMock.createMock; @@ -47,6 +50,7 @@ import android.test.suitebuilder.annotation.LargeTest; import android.util.TrustedTime; import com.android.server.net.NetworkStatsService; +import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import org.easymock.EasyMock; @@ -62,12 +66,16 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private static final String TEST_IFACE = "test0"; private static final long TEST_START = 1194220800000L; + private static final int TEST_UID_1 = 1001; + private static final int TEST_UID_2 = 1002; + private BroadcastInterceptingContext mServiceContext; private File mStatsDir; private INetworkManagementService mNetManager; private IAlarmManager mAlarmManager; private TrustedTime mTime; + private NetworkStatsSettings mSettings; private IConnectivityManager mConnManager; private NetworkStatsService mService; @@ -82,12 +90,14 @@ public class NetworkStatsServiceTest extends AndroidTestCase { mNetManager = createMock(INetworkManagementService.class); mAlarmManager = createMock(IAlarmManager.class); mTime = createMock(TrustedTime.class); + mSettings = createMock(NetworkStatsSettings.class); mConnManager = createMock(IConnectivityManager.class); mService = new NetworkStatsService( - mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir); + mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir, mSettings); mService.bindConnectivityManager(mConnManager); + expectDefaultSettings(); expectSystemReady(); replay(); @@ -114,115 +124,93 @@ public class NetworkStatsServiceTest extends AndroidTestCase { super.tearDown(); } - 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 testHistoryForWifi() throws Exception { + public void testSummaryStatsWifi() throws Exception { long elapsedRealtime = 0; - NetworkState[] state = null; - NetworkStats stats = null; - NetworkStats detail = null; // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. - state = new NetworkState[] { buildWifi() }; - stats = new NetworkStats.Builder(elapsedRealtime, 0).build(); - detail = new NetworkStats.Builder(elapsedRealtime, 0).build(); - - expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); - expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); - expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); expectTime(TEST_START + elapsedRealtime); + expectDefaultSettings(); + expectNetworkState(buildWifiState()); + expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); - verifyAndReset(); // verify service has empty history for wifi assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); + verifyAndReset(); // modify some number on wifi, and trigger poll event elapsedRealtime += HOUR_IN_MILLIS; - stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry( - TEST_IFACE, UID_ALL, 1024L, 2048L).build(); - - expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); - expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); expectTime(TEST_START + elapsedRealtime); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) + .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L)); + expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - verifyAndReset(); // verify service recorded history assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); + verifyAndReset(); // and bump forward again, with counters going higher. this is // important, since polling should correctly subtract last snapshot. elapsedRealtime += DAY_IN_MILLIS; - stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry( - TEST_IFACE, UID_ALL, 4096L, 8192L).build(); - - expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); - expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); expectTime(TEST_START + elapsedRealtime); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) + .addEntry(TEST_IFACE, UID_ALL, 4096L, 8192L)); + expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - verifyAndReset(); // verify service recorded history assertNetworkTotal(TEMPLATE_WIFI, 4096L, 8192L); + verifyAndReset(); + } - public void testHistoryForRebootPersist() throws Exception { + public void testStatsRebootPersist() throws Exception { long elapsedRealtime = 0; - NetworkState[] state = null; - NetworkStats stats = null; - NetworkStats detail = null; - - // assert that no stats file exists - final File statsFile = new File(mStatsDir, "netstats.bin"); - assertFalse(statsFile.exists()); + assertStatsFilesExist(false); // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. - state = new NetworkState[] { buildWifi() }; - stats = new NetworkStats.Builder(elapsedRealtime, 0).build(); - detail = new NetworkStats.Builder(elapsedRealtime, 0).build(); - - expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); - expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); - expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); expectTime(TEST_START + elapsedRealtime); + expectDefaultSettings(); + expectNetworkState(buildWifiState()); + expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); replay(); mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); - verifyAndReset(); // verify service has empty history for wifi assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); + verifyAndReset(); // modify some number on wifi, and trigger poll event elapsedRealtime += HOUR_IN_MILLIS; - stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry( - TEST_IFACE, UID_ALL, 1024L, 2048L).build(); - - expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); - expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); expectTime(TEST_START + elapsedRealtime); + expectDefaultSettings(); + expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) + .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L)); + // TODO: switch these stats to specific iface + expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2) + .addEntry(IFACE_ALL, TEST_UID_1, 512L, 256L) + .addEntry(IFACE_ALL, TEST_UID_2, 128L, 128L)); replay(); mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); - verifyAndReset(); // verify service recorded history assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); + assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L); + assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L); + verifyAndReset(); // graceful shutdown system, which should trigger persist of stats, and // clear any values in memory. @@ -230,18 +218,84 @@ public class NetworkStatsServiceTest extends AndroidTestCase { // talk with zombie service to assert stats have gone; and assert that // we persisted them to file. + expectDefaultSettings(); + replay(); assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L); - assertTrue(statsFile.exists()); + verifyAndReset(); + + assertStatsFilesExist(true); // boot through serviceReady() again + expectDefaultSettings(); expectSystemReady(); replay(); mService.systemReady(); - verifyAndReset(); // after systemReady(), we should have historical stats loaded again assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L); + assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L); + assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L); + verifyAndReset(); + + } + + public void testStatsBucketResize() throws Exception { + long elapsedRealtime = 0; + NetworkStatsHistory history = null; + long[] total = null; + + assertStatsFilesExist(false); + + // pretend that wifi network comes online; service should ask about full + // network state, and poll any existing interfaces before updating. + expectTime(TEST_START + elapsedRealtime); + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + expectNetworkState(buildWifiState()); + expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); + + replay(); + mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION)); + verifyAndReset(); + + // modify some number on wifi, and trigger poll event + elapsedRealtime += 2 * HOUR_IN_MILLIS; + expectTime(TEST_START + elapsedRealtime); + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1) + .addEntry(TEST_IFACE, UID_ALL, 512L, 512L)); + expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); + + replay(); + mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); + + // verify service recorded history + history = mService.getHistoryForNetwork(TEMPLATE_WIFI); + total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); + assertEquals(512L, total[0]); + assertEquals(512L, total[1]); + assertEquals(HOUR_IN_MILLIS, history.bucketDuration); + assertEquals(2, history.bucketCount); + verifyAndReset(); + + // now change bucket duration setting and trigger another poll with + // exact same values, which should resize existing buckets. + expectTime(TEST_START + elapsedRealtime); + expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS); + expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime)); + expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime)); + + replay(); + mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); + + // verify identical stats, but spread across 4 buckets now + history = mService.getHistoryForNetwork(TEMPLATE_WIFI); + total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); + assertEquals(512L, total[0]); + assertEquals(512L, total[1]); + assertEquals(30 * MINUTE_IN_MILLIS, history.bucketDuration); + assertEquals(4, history.bucketCount); + verifyAndReset(); } @@ -252,6 +306,13 @@ public class NetworkStatsServiceTest extends AndroidTestCase { assertEquals(tx, total[1]); } + private void assertUidTotal(int uid, int template, long rx, long tx) { + final NetworkStatsHistory history = mService.getHistoryForUid(uid, template); + final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null); + assertEquals(rx, total[0]); + assertEquals(tx, total[1]); + } + private void expectSystemReady() throws Exception { mAlarmManager.remove(isA(PendingIntent.class)); expectLastCall().anyTimes(); @@ -261,7 +322,34 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expectLastCall().atLeastOnce(); } - public void expectTime(long currentTime) throws Exception { + private void expectNetworkState(NetworkState... state) throws Exception { + expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce(); + } + + private void expectNetworkStatsSummary(NetworkStats summary) throws Exception { + expect(mNetManager.getNetworkStatsSummary()).andReturn(summary).atLeastOnce(); + } + + private void expectNetworkStatsDetail(NetworkStats detail) throws Exception { + expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce(); + } + + private void expectDefaultSettings() throws Exception { + expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS); + } + + private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory) + throws Exception { + expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); + expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes(); + expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes(); + expect(mSettings.getNetworkMaxHistory()).andReturn(maxHistory).anyTimes(); + expect(mSettings.getUidBucketDuration()).andReturn(bucketDuration).anyTimes(); + expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes(); + expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes(); + } + + private void expectTime(long currentTime) throws Exception { expect(mTime.forceRefresh()).andReturn(false).anyTimes(); expect(mTime.hasCache()).andReturn(true).anyTimes(); expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes(); @@ -269,12 +357,36 @@ public class NetworkStatsServiceTest extends AndroidTestCase { expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes(); } + private void assertStatsFilesExist(boolean exist) { + final File summaryFile = new File(mStatsDir, "netstats.bin"); + final File detailFile = new File(mStatsDir, "netstats_uid.bin"); + if (exist) { + assertTrue(summaryFile.exists()); + assertTrue(detailFile.exists()); + } else { + assertFalse(summaryFile.exists()); + assertFalse(detailFile.exists()); + } + } + + private static NetworkState buildWifiState() { + 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); + } + + private static NetworkStats buildEmptyStats(long elapsedRealtime) { + return new NetworkStats(elapsedRealtime, 0); + } + private void replay() { - EasyMock.replay(mNetManager, mAlarmManager, mTime, mConnManager); + EasyMock.replay(mNetManager, mAlarmManager, mTime, mSettings, mConnManager); } private void verifyAndReset() { - EasyMock.verify(mNetManager, mAlarmManager, mTime, mConnManager); - EasyMock.reset(mNetManager, mAlarmManager, mTime, mConnManager); + EasyMock.verify(mNetManager, mAlarmManager, mTime, mSettings, mConnManager); + EasyMock.reset(mNetManager, mAlarmManager, mTime, mSettings, mConnManager); } } diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java index d1ee4f6..30afdd8 100644 --- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java @@ -288,11 +288,10 @@ public class ThrottleServiceTest extends AndroidTestCase { */ public void expectGetInterfaceCounter(long rx, long tx) throws Exception { // TODO: provide elapsedRealtime mock to match TimeAuthority - final NetworkStats.Builder stats = new NetworkStats.Builder( - SystemClock.elapsedRealtime(), 1); + final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, rx, tx); - expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats.build()).atLeastOnce(); + expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce(); } /** diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java index b5f1a17..4837eb9 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -528,12 +528,8 @@ class SipSessionGroup implements SipListener { public void answerCall(String sessionDescription, int timeout) { synchronized (SipSessionGroup.this) { if (mPeerProfile == null) return; - try { - processCommand(new MakeCallCommand(mPeerProfile, - sessionDescription, timeout)); - } catch (SipException e) { - onError(e); - } + doCommandAsync(new MakeCallCommand(mPeerProfile, + sessionDescription, timeout)); } } |