Persist UID stats, lazy loading, resize buckets.
Persisting detailed UID stats in separate "netstats_detail.bin" file to enable different schedules for summary and detail polling. Only load detailed UID history on demand, since it's not needed during boot. Add test to verify UID stats are persisted across simulated reboot. Move external settings into well-named interface, which is still backed by Settings.Secure. During periodic poll events, resize any history to match current bucket duration setting. Test to verify. Change-Id: I6366f3583a591f8ba859b0e5987daf8cafa4e95a
This commit is contained in:
@@ -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>();
|
||||
|
||||
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<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,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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user