Merge "Augment network stats based on SubscriptionPlan." into oc-mr1-dev

am: f31cf45e82

Change-Id: Ia6c6d5893d841f21d181363dc01f77efa6579a8f
This commit is contained in:
Jeff Sharkey
2017-08-30 19:14:21 +00:00
committed by android-build-merger
9 changed files with 305 additions and 119 deletions

View File

@@ -97,12 +97,12 @@ public final class NetworkStats implements AutoCloseable {
private NetworkStatsHistory.Entry mRecycledHistoryEntry = null; private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
/** @hide */ /** @hide */
NetworkStats(Context context, NetworkTemplate template, long startTimestamp, NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
long endTimestamp) throws RemoteException, SecurityException { long endTimestamp) throws RemoteException, SecurityException {
final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
// Open network stats session // Open network stats session
mSession = statsService.openSessionForUsageStats(context.getOpPackageName()); mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
mCloseGuard.open("close"); mCloseGuard.open("close");
mTemplate = template; mTemplate = template;
mStartTimeStamp = startTimestamp; mStartTimeStamp = startTimestamp;

View File

@@ -24,15 +24,14 @@ import android.app.usage.NetworkStats.Bucket;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.DataUsageRequest; import android.net.DataUsageRequest;
import android.net.INetworkStatsService;
import android.net.NetworkIdentity; import android.net.NetworkIdentity;
import android.net.NetworkTemplate; import android.net.NetworkTemplate;
import android.net.INetworkStatsService;
import android.os.Binder; import android.os.Binder;
import android.os.Build;
import android.os.Message;
import android.os.Messenger;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException; import android.os.ServiceManager.ServiceNotFoundException;
@@ -79,7 +78,7 @@ import android.util.Log;
* In addition to tethering usage, usage by removed users and apps, and usage by the system * In addition to tethering usage, usage by removed users and apps, and usage by the system
* is also included in the results for callers with one of these higher levels of access. * is also included in the results for callers with one of these higher levels of access.
* <p /> * <p />
* <b>NOTE:</b> Prior to API level {@value Build.VERSION_CODES#N}, all calls to these APIs required * <b>NOTE:</b> Prior to API level {@value android.os.Build.VERSION_CODES#N}, all calls to these APIs required
* the above permission, even to access an app's own data usage, and carrier-privileged apps were * the above permission, even to access an app's own data usage, and carrier-privileged apps were
* not included. * not included.
*/ */
@@ -96,6 +95,13 @@ public class NetworkStatsManager {
private final Context mContext; private final Context mContext;
private final INetworkStatsService mService; private final INetworkStatsService mService;
/** @hide */
public static final int FLAG_POLL_ON_OPEN = 1 << 0;
/** @hide */
public static final int FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN = 1 << 1;
private int mFlags;
/** /**
* {@hide} * {@hide}
*/ */
@@ -103,6 +109,25 @@ public class NetworkStatsManager {
mContext = context; mContext = context;
mService = INetworkStatsService.Stub.asInterface( mService = INetworkStatsService.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)); ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
setPollOnOpen(true);
}
/** @hide */
public void setPollOnOpen(boolean pollOnOpen) {
if (pollOnOpen) {
mFlags |= FLAG_POLL_ON_OPEN;
} else {
mFlags &= ~FLAG_POLL_ON_OPEN;
}
}
/** @hide */
public void setAugmentWithSubscriptionPlan(boolean augmentWithSubscriptionPlan) {
if (augmentWithSubscriptionPlan) {
mFlags |= FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
} else {
mFlags &= ~FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN;
}
} }
/** /**
@@ -136,7 +161,7 @@ public class NetworkStatsManager {
} }
Bucket bucket = null; Bucket bucket = null;
NetworkStats stats = new NetworkStats(mContext, template, startTime, endTime); NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
bucket = stats.getDeviceSummaryForNetwork(); bucket = stats.getDeviceSummaryForNetwork();
stats.close(); stats.close();
@@ -174,7 +199,7 @@ public class NetworkStatsManager {
} }
NetworkStats stats; NetworkStats stats;
stats = new NetworkStats(mContext, template, startTime, endTime); stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
stats.startSummaryEnumeration(); stats.startSummaryEnumeration();
stats.close(); stats.close();
@@ -211,7 +236,7 @@ public class NetworkStatsManager {
} }
NetworkStats result; NetworkStats result;
result = new NetworkStats(mContext, template, startTime, endTime); result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
result.startSummaryEnumeration(); result.startSummaryEnumeration();
return result; return result;
@@ -260,7 +285,7 @@ public class NetworkStatsManager {
NetworkStats result; NetworkStats result;
try { try {
result = new NetworkStats(mContext, template, startTime, endTime); result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
result.startHistoryEnumeration(uid, tag); result.startHistoryEnumeration(uid, tag);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e); Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
@@ -305,7 +330,7 @@ public class NetworkStatsManager {
} }
NetworkStats result; NetworkStats result;
result = new NetworkStats(mContext, template, startTime, endTime); result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
result.startUserUidEnumeration(); result.startUserUidEnumeration();
return result; return result;
} }

View File

@@ -36,7 +36,7 @@ interface INetworkStatsService {
* PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted * PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted
* READ_NETWORK_USAGE_STATS is checked for. * READ_NETWORK_USAGE_STATS is checked for.
*/ */
INetworkStatsSession openSessionForUsageStats(String callingPackage); INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage);
/** Return network layer usage total for traffic that matches template. */ /** Return network layer usage total for traffic that matches template. */
long getNetworkTotalBytes(in NetworkTemplate template, long start, long end); long getNetworkTotalBytes(in NetworkTemplate template, long start, long end);

View File

@@ -27,6 +27,7 @@ import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray; import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray; import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
import static android.text.format.DateUtils.SECOND_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.internal.util.ArrayUtils.total; import static com.android.internal.util.ArrayUtils.total;
import android.os.Parcel; import android.os.Parcel;
@@ -282,6 +283,24 @@ public class NetworkStatsHistory implements Parcelable {
return entry; return entry;
} }
public void setValues(int i, Entry entry) {
// Unwind old values
if (rxBytes != null) totalBytes -= rxBytes[i];
if (txBytes != null) totalBytes -= txBytes[i];
bucketStart[i] = entry.bucketStart;
setLong(activeTime, i, entry.activeTime);
setLong(rxBytes, i, entry.rxBytes);
setLong(rxPackets, i, entry.rxPackets);
setLong(txBytes, i, entry.txBytes);
setLong(txPackets, i, entry.txPackets);
setLong(operations, i, entry.operations);
// Apply new values
if (rxBytes != null) totalBytes += rxBytes[i];
if (txBytes != null) totalBytes += txBytes[i];
}
/** /**
* Record that data traffic occurred in the given time range. Will * Record that data traffic occurred in the given time range. Will
* distribute across internal buckets, creating new buckets as needed. * distribute across internal buckets, creating new buckets as needed.

View File

@@ -326,6 +326,10 @@ public class NetworkTemplate implements Parcelable {
} }
} }
public boolean matchesSubscriberId(String subscriberId) {
return ArrayUtils.contains(mMatchSubscriberIds, subscriberId);
}
/** /**
* Check if mobile network with matching IMSI. * Check if mobile network with matching IMSI.
*/ */

View File

@@ -28,6 +28,8 @@ import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.UID_REMOVED; import static android.net.TrafficStats.UID_REMOVED;
import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.TAG;
import android.net.NetworkIdentity; import android.net.NetworkIdentity;
import android.net.NetworkStats; import android.net.NetworkStats;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
@@ -37,20 +39,24 @@ import android.os.Binder;
import android.service.NetworkStatsCollectionKeyProto; import android.service.NetworkStatsCollectionKeyProto;
import android.service.NetworkStatsCollectionProto; import android.service.NetworkStatsCollectionProto;
import android.service.NetworkStatsCollectionStatsProto; import android.service.NetworkStatsCollectionStatsProto;
import android.telephony.SubscriptionPlan;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.AtomicFile; import android.util.AtomicFile;
import android.util.IntArray; import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FileRotator; import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import libcore.io.IoUtils;
import com.google.android.collect.Lists; import com.google.android.collect.Lists;
import com.google.android.collect.Maps; import com.google.android.collect.Maps;
import libcore.io.IoUtils;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -60,9 +66,11 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -140,6 +148,35 @@ public class NetworkStatsCollection implements FileRotator.Reader {
return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE; return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
} }
@VisibleForTesting
public long roundUp(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
|| time == SubscriptionPlan.TIME_UNKNOWN) {
return time;
} else {
final long mod = time % mBucketDuration;
if (mod > 0) {
time -= mod;
time += mBucketDuration;
}
return time;
}
}
@VisibleForTesting
public long roundDown(long time) {
if (time == Long.MIN_VALUE || time == Long.MAX_VALUE
|| time == SubscriptionPlan.TIME_UNKNOWN) {
return time;
} else {
final long mod = time % mBucketDuration;
if (mod > 0) {
time -= mod;
}
return time;
}
}
public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) { public int[] getRelevantUids(@NetworkStatsAccess.Level int accessLevel) {
return getRelevantUids(accessLevel, Binder.getCallingUid()); return getRelevantUids(accessLevel, Binder.getCallingUid());
} }
@@ -165,60 +202,110 @@ public class NetworkStatsCollection implements FileRotator.Reader {
* Combine all {@link NetworkStatsHistory} in this collection which match * Combine all {@link NetworkStatsHistory} in this collection which match
* the requested parameters. * the requested parameters.
*/ */
public NetworkStatsHistory getHistory( public NetworkStatsHistory getHistory(NetworkTemplate template, SubscriptionPlan augmentPlan,
NetworkTemplate template, int uid, int set, int tag, int fields, int uid, int set, int tag, int fields, long start, long end,
@NetworkStatsAccess.Level int accessLevel) {
return getHistory(template, uid, set, tag, fields, Long.MIN_VALUE, Long.MAX_VALUE,
accessLevel);
}
/**
* Combine all {@link NetworkStatsHistory} in this collection which match
* the requested parameters.
*/
public NetworkStatsHistory getHistory(
NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
@NetworkStatsAccess.Level int accessLevel) {
return getHistory(template, uid, set, tag, fields, start, end, accessLevel,
Binder.getCallingUid());
}
/**
* Combine all {@link NetworkStatsHistory} in this collection which match
* the requested parameters.
*/
public NetworkStatsHistory getHistory(
NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end,
@NetworkStatsAccess.Level int accessLevel, int callerUid) { @NetworkStatsAccess.Level int accessLevel, int callerUid) {
if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) { if (!NetworkStatsAccess.isAccessibleToUser(uid, callerUid, accessLevel)) {
throw new SecurityException("Network stats history of uid " + uid throw new SecurityException("Network stats history of uid " + uid
+ " is forbidden for caller " + callerUid); + " is forbidden for caller " + callerUid);
} }
final int bucketEstimate = (int) ((end - start) / mBucketDuration);
final NetworkStatsHistory combined = new NetworkStatsHistory( final NetworkStatsHistory combined = new NetworkStatsHistory(
mBucketDuration, start == end ? 1 : estimateBuckets(), fields); mBucketDuration, bucketEstimate, fields);
// shortcut when we know stats will be empty // shortcut when we know stats will be empty
if (start == end) return combined; if (start == end) return combined;
// Figure out the window of time that we should be augmenting (if any)
long augmentStart = SubscriptionPlan.TIME_UNKNOWN;
long augmentEnd = (augmentPlan != null) ? augmentPlan.getDataUsageTime()
: SubscriptionPlan.TIME_UNKNOWN;
// And if augmenting, we might need to collect more data to adjust with
long collectStart = start;
long collectEnd = end;
if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = augmentPlan.cycleIterator();
while (it.hasNext()) {
final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
final long cycleStart = cycle.first.toInstant().toEpochMilli();
final long cycleEnd = cycle.second.toInstant().toEpochMilli();
if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
augmentStart = cycleStart;
collectStart = Long.min(collectStart, augmentStart);
collectEnd = Long.max(collectEnd, augmentEnd);
break;
}
}
}
if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
// Shrink augmentation window so we don't risk undercounting.
augmentStart = roundUp(augmentStart);
augmentEnd = roundDown(augmentEnd);
// Grow collection window so we get all the stats needed.
collectStart = roundDown(collectStart);
collectEnd = roundUp(collectEnd);
}
for (int i = 0; i < mStats.size(); i++) { for (int i = 0; i < mStats.size(); i++) {
final Key key = mStats.keyAt(i); final Key key = mStats.keyAt(i);
if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
&& templateMatches(template, key.ident)) { && templateMatches(template, key.ident)) {
final NetworkStatsHistory value = mStats.valueAt(i); final NetworkStatsHistory value = mStats.valueAt(i);
combined.recordHistory(value, start, end); combined.recordHistory(value, collectStart, collectEnd);
} }
} }
return combined;
}
/** if (augmentStart != SubscriptionPlan.TIME_UNKNOWN) {
* Summarize all {@link NetworkStatsHistory} in this collection which match final NetworkStatsHistory.Entry entry = combined.getValues(
* the requested parameters. augmentStart, augmentEnd, null);
*/
public NetworkStats getSummary(NetworkTemplate template, long start, long end, // If we don't have any recorded data for this time period, give
@NetworkStatsAccess.Level int accessLevel) { // ourselves something to scale with.
return getSummary(template, start, end, accessLevel, Binder.getCallingUid()); if (entry.rxBytes == 0 || entry.txBytes == 0) {
combined.recordData(augmentStart, augmentEnd,
new NetworkStats.Entry(1, 0, 1, 0, 0));
combined.getValues(augmentStart, augmentEnd, entry);
}
final long rawBytes = entry.rxBytes + entry.txBytes;
final long rawRxBytes = entry.rxBytes;
final long rawTxBytes = entry.txBytes;
final long targetBytes = augmentPlan.getDataUsageBytes();
final long targetRxBytes = (rawRxBytes * targetBytes) / rawBytes;
final long targetTxBytes = (rawTxBytes * targetBytes) / rawBytes;
// Scale all matching buckets to reach anchor target
final long beforeTotal = combined.getTotalBytes();
for (int i = 0; i < combined.size(); i++) {
combined.getValues(i, entry);
if (entry.bucketStart >= augmentStart
&& entry.bucketStart + entry.bucketDuration <= augmentEnd) {
entry.rxBytes = (entry.rxBytes * targetRxBytes) / rawRxBytes;
entry.txBytes = (entry.txBytes * targetTxBytes) / rawTxBytes;
// We purposefully clear out packet counters to indicate
// that this data has been augmented.
entry.rxPackets = 0;
entry.txPackets = 0;
combined.setValues(i, entry);
}
}
final long deltaTotal = combined.getTotalBytes() - beforeTotal;
if (deltaTotal != 0) {
Slog.d(TAG, "Augmented network usage by " + deltaTotal + " bytes");
}
// Finally we can slice data as originally requested
final NetworkStatsHistory sliced = new NetworkStatsHistory(
mBucketDuration, bucketEstimate, fields);
sliced.recordHistory(combined, start, end);
return sliced;
} else {
return combined;
}
} }
/** /**
@@ -230,6 +317,7 @@ public class NetworkStatsCollection implements FileRotator.Reader {
final long now = System.currentTimeMillis(); final long now = System.currentTimeMillis();
final NetworkStats stats = new NetworkStats(end - start, 24); final NetworkStats stats = new NetworkStats(end - start, 24);
// shortcut when we know stats will be empty // shortcut when we know stats will be empty
if (start == end) return stats; if (start == end) return stats;

View File

@@ -17,28 +17,26 @@
package com.android.server.net; package com.android.server.net;
import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES;
import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkArgument;
import android.app.usage.NetworkStatsManager; import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest; import android.net.DataUsageRequest;
import android.net.NetworkStats; import android.net.NetworkStats;
import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate; import android.net.NetworkTemplate;
import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.IntArray;
import android.util.SparseArray;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnInfo;
@@ -410,7 +408,7 @@ class NetworkStatsObservers {
*/ */
private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) { private long getTotalBytesForNetworkUid(NetworkTemplate template, int uid) {
try { try {
NetworkStatsHistory history = mCollection.getHistory(template, uid, NetworkStatsHistory history = mCollection.getHistory(template, null, uid,
NetworkStats.SET_ALL, NetworkStats.TAG_NONE, NetworkStats.SET_ALL, NetworkStats.TAG_NONE,
NetworkStatsHistory.FIELD_ALL, NetworkStatsHistory.FIELD_ALL,
Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */, Long.MIN_VALUE /* start */, Long.MAX_VALUE /* end */,

View File

@@ -20,6 +20,7 @@ import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.YEAR_IN_MILLIS; import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull; import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable; import android.annotation.Nullable;
@@ -28,6 +29,7 @@ import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory; import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate; import android.net.NetworkTemplate;
import android.net.TrafficStats; import android.net.TrafficStats;
import android.os.Binder;
import android.os.DropBoxManager; import android.os.DropBoxManager;
import android.service.NetworkStatsRecorderProto; import android.service.NetworkStatsRecorderProto;
import android.util.Log; import android.util.Log;
@@ -38,6 +40,9 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.net.VpnInfo; import com.android.internal.net.VpnInfo;
import com.android.internal.util.FileRotator; import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import libcore.io.IoUtils;
import com.google.android.collect.Sets; import com.google.android.collect.Sets;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -52,8 +57,6 @@ import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import libcore.io.IoUtils;
/** /**
* Logic to record deltas between periodic {@link NetworkStats} snapshots into * Logic to record deltas between periodic {@link NetworkStats} snapshots into
* {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}. * {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
@@ -150,7 +153,7 @@ public class NetworkStatsRecorder {
public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) { public NetworkStats.Entry getTotalSinceBootLocked(NetworkTemplate template) {
return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE, return mSinceBoot.getSummary(template, Long.MIN_VALUE, Long.MAX_VALUE,
NetworkStatsAccess.Level.DEVICE).getTotal(null); NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotal(null);
} }
public NetworkStatsCollection getSinceBoot() { public NetworkStatsCollection getSinceBoot() {

View File

@@ -26,6 +26,8 @@ import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.ConnectivityManager.isNetworkTypeMobile; import static android.net.ConnectivityManager.isNetworkTypeMobile;
import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND; import static android.net.NetworkStats.SET_FOREGROUND;
@@ -33,10 +35,12 @@ import static android.net.NetworkStats.STATS_PER_IFACE;
import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateMobileWildcard;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.KB_IN_BYTES;
import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES;
import static android.provider.Settings.Global.NETSTATS_AUGMENT_ENABLED;
import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Global.NETSTATS_DEV_BUCKET_DURATION;
import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE; import static android.provider.Settings.Global.NETSTATS_DEV_DELETE_AGE;
import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES; import static android.provider.Settings.Global.NETSTATS_DEV_PERSIST_BYTES;
@@ -65,6 +69,7 @@ import static com.android.server.NetworkManagementSocketTagger.setKernelCounterS
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -104,6 +109,8 @@ import android.provider.Settings;
import android.provider.Settings.Global; import android.provider.Settings.Global;
import android.service.NetworkInterfaceProto; import android.service.NetworkInterfaceProto;
import android.service.NetworkStatsServiceDumpProto; import android.service.NetworkStatsServiceDumpProto;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
@@ -139,8 +146,8 @@ import java.util.List;
* other system services. * other system services.
*/ */
public class NetworkStatsService extends INetworkStatsService.Stub { public class NetworkStatsService extends INetworkStatsService.Stub {
private static final String TAG = "NetworkStats"; static final String TAG = "NetworkStats";
private static final boolean LOGV = false; static final boolean LOGV = false;
private static final int MSG_PERFORM_POLL = 1; private static final int MSG_PERFORM_POLL = 1;
private static final int MSG_UPDATE_IFACES = 2; private static final int MSG_UPDATE_IFACES = 2;
@@ -194,6 +201,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public long getPollInterval(); public long getPollInterval();
public long getTimeCacheMaxAge(); public long getTimeCacheMaxAge();
public boolean getSampleEnabled(); public boolean getSampleEnabled();
public boolean getAugmentEnabled();
public static class Config { public static class Config {
public final long bucketDuration; public final long bucketDuration;
@@ -466,18 +474,20 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public INetworkStatsSession openSession() { public INetworkStatsSession openSession() {
return createSession(null, /* poll on create */ false); // NOTE: if callers want to get non-augmented data, they should go
// through the public API
return openSessionInternal(NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, null);
} }
@Override @Override
public INetworkStatsSession openSessionForUsageStats(final String callingPackage) { public INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage) {
return createSession(callingPackage, /* poll on create */ true); return openSessionInternal(flags, callingPackage);
} }
private INetworkStatsSession createSession(final String callingPackage, boolean pollOnCreate) { private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
assertBandwidthControlEnabled(); assertBandwidthControlEnabled();
if (pollOnCreate) { if ((flags & NetworkStatsManager.FLAG_POLL_ON_OPEN) != 0) {
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
performPoll(FLAG_PERSIST_ALL); performPoll(FLAG_PERSIST_ALL);
@@ -490,9 +500,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
// for its lifetime; when caller closes only weak references remain. // for its lifetime; when caller closes only weak references remain.
return new INetworkStatsSession.Stub() { return new INetworkStatsSession.Stub() {
private final int mCallingUid = Binder.getCallingUid();
private final String mCallingPackage = callingPackage;
private final @NetworkStatsAccess.Level int mAccessLevel = checkAccessLevel(
callingPackage);
private NetworkStatsCollection mUidComplete; private NetworkStatsCollection mUidComplete;
private NetworkStatsCollection mUidTagComplete; private NetworkStatsCollection mUidTagComplete;
private String mCallingPackage = callingPackage;
private NetworkStatsCollection getUidComplete() { private NetworkStatsCollection getUidComplete() {
synchronized (mStatsLock) { synchronized (mStatsLock) {
@@ -514,55 +528,38 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public int[] getRelevantUids() { public int[] getRelevantUids() {
return getUidComplete().getRelevantUids(checkAccessLevel(mCallingPackage)); return getUidComplete().getRelevantUids(mAccessLevel);
} }
@Override @Override
public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start, public NetworkStats getDeviceSummaryForNetwork(
long end) { NetworkTemplate template, long start, long end) {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetSummaryForNetwork(template, flags, start, end, mAccessLevel,
if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) { mCallingUid);
throw new SecurityException("Calling package " + mCallingPackage
+ " cannot access device summary network stats");
}
NetworkStats result = new NetworkStats(end - start, 1);
final long ident = Binder.clearCallingIdentity();
try {
// Using access level higher than the one we checked for above.
// Reason is that we are combining usage data in a way that is not PII
// anymore.
result.combineAllValues(
internalGetSummaryForNetwork(template, start, end,
NetworkStatsAccess.Level.DEVICE));
} finally {
Binder.restoreCallingIdentity(ident);
}
return result;
} }
@Override @Override
public NetworkStats getSummaryForNetwork( public NetworkStats getSummaryForNetwork(
NetworkTemplate template, long start, long end) { NetworkTemplate template, long start, long end) {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetSummaryForNetwork(template, flags, start, end, mAccessLevel,
return internalGetSummaryForNetwork(template, start, end, accessLevel); mCallingUid);
} }
@Override @Override
public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) { public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); return internalGetHistoryForNetwork(template, flags, fields, mAccessLevel,
return internalGetHistoryForNetwork(template, fields, accessLevel); mCallingUid);
} }
@Override @Override
public NetworkStats getSummaryForAllUid( public NetworkStats getSummaryForAllUid(
NetworkTemplate template, long start, long end, boolean includeTags) { NetworkTemplate template, long start, long end, boolean includeTags) {
try { try {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); final NetworkStats stats = getUidComplete()
final NetworkStats stats = .getSummary(template, start, end, mAccessLevel, mCallingUid);
getUidComplete().getSummary(template, start, end, accessLevel);
if (includeTags) { if (includeTags) {
final NetworkStats tagStats = getUidTagComplete() final NetworkStats tagStats = getUidTagComplete()
.getSummary(template, start, end, accessLevel); .getSummary(template, start, end, mAccessLevel, mCallingUid);
stats.combineAllValues(tagStats); stats.combineAllValues(tagStats);
} }
return stats; return stats;
@@ -576,13 +573,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
@Override @Override
public NetworkStatsHistory getHistoryForUid( public NetworkStatsHistory getHistoryForUid(
NetworkTemplate template, int uid, int set, int tag, int fields) { NetworkTemplate template, int uid, int set, int tag, int fields) {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); // NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) { if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, uid, set, tag, fields, return getUidComplete().getHistory(template, null, uid, set, tag, fields,
accessLevel); Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid);
} else { } else {
return getUidTagComplete().getHistory(template, uid, set, tag, fields, return getUidTagComplete().getHistory(template, null, uid, set, tag, fields,
accessLevel); Long.MIN_VALUE, Long.MAX_VALUE, mAccessLevel, mCallingUid);
} }
} }
@@ -590,13 +587,13 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
public NetworkStatsHistory getHistoryIntervalForUid( public NetworkStatsHistory getHistoryIntervalForUid(
NetworkTemplate template, int uid, int set, int tag, int fields, NetworkTemplate template, int uid, int set, int tag, int fields,
long start, long end) { long start, long end) {
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(mCallingPackage); // NOTE: We don't augment UID-level statistics
if (tag == TAG_NONE) { if (tag == TAG_NONE) {
return getUidComplete().getHistory(template, uid, set, tag, fields, start, end, return getUidComplete().getHistory(template, null, uid, set, tag, fields,
accessLevel); start, end, mAccessLevel, mCallingUid);
} else if (uid == Binder.getCallingUid()) { } else if (uid == Binder.getCallingUid()) {
return getUidTagComplete().getHistory(template, uid, set, tag, fields, return getUidTagComplete().getHistory(template, null, uid, set, tag, fields,
start, end, accessLevel); start, end, mAccessLevel, mCallingUid);
} else { } else {
throw new SecurityException("Calling package " + mCallingPackage throw new SecurityException("Calling package " + mCallingPackage
+ " cannot access tag information from a different uid"); + " cannot access tag information from a different uid");
@@ -616,37 +613,85 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
mContext, Binder.getCallingUid(), callingPackage); mContext, Binder.getCallingUid(), callingPackage);
} }
/**
* Find the most relevant {@link SubscriptionPlan} for the given
* {@link NetworkTemplate} and flags. This is typically used to augment
* local measurement results to match a known anchor from the carrier.
*/
private SubscriptionPlan resolveSubscriptionPlan(NetworkTemplate template, int flags) {
SubscriptionPlan plan = null;
if ((flags & NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN) != 0
&& (template.getMatchRule() == NetworkTemplate.MATCH_MOBILE_ALL)
&& mSettings.getAugmentEnabled()) {
Slog.d(TAG, "Resolving plan for " + template);
final long token = Binder.clearCallingIdentity();
try {
final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
for (int subId : sm.getActiveSubscriptionIdList()) {
if (template.matchesSubscriberId(tm.getSubscriberId(subId))) {
Slog.d(TAG, "Found active matching subId " + subId);
final List<SubscriptionPlan> plans = sm.getSubscriptionPlans(subId);
if (!plans.isEmpty()) {
plan = plans.get(0);
}
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
Slog.d(TAG, "Resolved to plan " + plan);
}
return plan;
}
/** /**
* Return network summary, splicing between DEV and XT stats when * Return network summary, splicing between DEV and XT stats when
* appropriate. * appropriate.
*/ */
private NetworkStats internalGetSummaryForNetwork( private NetworkStats internalGetSummaryForNetwork(NetworkTemplate template, int flags,
NetworkTemplate template, long start, long end, long start, long end, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
@NetworkStatsAccess.Level int accessLevel) {
// We've been using pure XT stats long enough that we no longer need to // We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together. // splice DEV and XT together.
return mXtStatsCached.getSummary(template, start, end, accessLevel); final NetworkStatsHistory history = internalGetHistoryForNetwork(template, flags, FIELD_ALL,
accessLevel, callingUid);
final long now = System.currentTimeMillis();
final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
final NetworkStats stats = new NetworkStats(end - start, 1);
stats.addValues(new NetworkStats.Entry(IFACE_ALL, UID_ALL, SET_ALL, TAG_NONE, METERED_ALL,
ROAMING_ALL, entry.rxBytes, entry.rxPackets, entry.txBytes, entry.txPackets,
entry.operations));
return stats;
} }
/** /**
* Return network history, splicing between DEV and XT stats when * Return network history, splicing between DEV and XT stats when
* appropriate. * appropriate.
*/ */
private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template, int fields, private NetworkStatsHistory internalGetHistoryForNetwork(NetworkTemplate template,
@NetworkStatsAccess.Level int accessLevel) { int flags, int fields, @NetworkStatsAccess.Level int accessLevel, int callingUid) {
// We've been using pure XT stats long enough that we no longer need to // We've been using pure XT stats long enough that we no longer need to
// splice DEV and XT together. // splice DEV and XT together.
return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields, accessLevel); return mXtStatsCached.getHistory(template, resolveSubscriptionPlan(template, flags),
UID_ALL, SET_ALL, TAG_NONE, fields, Long.MIN_VALUE, Long.MAX_VALUE,
accessLevel, callingUid);
} }
@Override @Override
public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) { public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
// Special case - since this is for internal use only, don't worry about a full access level // Special case - since this is for internal use only, don't worry about
// check and just require the signature/privileged permission. // a full access level check and just require the signature/privileged
// permission.
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
assertBandwidthControlEnabled(); assertBandwidthControlEnabled();
return internalGetSummaryForNetwork(template, start, end, NetworkStatsAccess.Level.DEVICE)
.getTotalBytes(); // NOTE: if callers want to get non-augmented data, they should go
// through the public API
return internalGetSummaryForNetwork(template,
NetworkStatsManager.FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN, start, end,
NetworkStatsAccess.Level.DEVICE, Binder.getCallingUid()).getTotalBytes();
} }
@Override @Override
@@ -1530,6 +1575,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true); return getGlobalBoolean(NETSTATS_SAMPLE_ENABLED, true);
} }
@Override @Override
public boolean getAugmentEnabled() {
return getGlobalBoolean(NETSTATS_AUGMENT_ENABLED, true);
}
@Override
public Config getDevConfig() { public Config getDevConfig() {
return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), return new Config(getGlobalLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS),
getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), getGlobalLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS),