diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 161c3937e6..f5d01d6c22 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 mActiveIface = Maps.newHashMap(); /** Set of historical stats for known ifaces. */ - private HashMap mSummaryStats = Maps.newHashMap(); + private HashMap mNetworkStats = Maps.newHashMap(); /** Set of historical stats for known UIDs. */ - private SparseArray mDetailStats = new SparseArray(); + private SparseArray mUidStats = new SparseArray(); - private NetworkStats mLastSummaryPoll; - private NetworkStats mLastSummaryPersist; + /** Flag if {@link #mUidStats} have been loaded from disk. */ + private boolean mUidStatsLoaded = false; - private NetworkStats mLastDetailPoll; + private NetworkStats mLastNetworkPoll; + private NetworkStats mLastNetworkPersist; + + 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()); + 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]; @@ -296,13 +310,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // TODO: apply networktemplate once granular uid stats are stored. synchronized (mStatsLock) { - final int size = mDetailStats.size(); + ensureUidStatsLoadedLocked(); + + final int size = mUidStats.size(); final NetworkStats.Builder stats = new NetworkStats.Builder(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]); } @@ -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 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,69 @@ 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); + } else if (existing.bucketDuration != bucketDuration) { + updated = new NetworkStatsHistory(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); + } else if (existing.bucketDuration != bucketDuration) { + updated = new NetworkStatsHistory(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 +554,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 +575,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 +599,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 +728,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } if (argSet.contains("poll")) { - performPollLocked(); + performPollLocked(true); pw.println("Forced poll"); return; } @@ -603,17 +741,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 +768,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 +809,42 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return telephony.getSubscriberId(); } + /** + * 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; + } + } + }