diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 08d4c6cbf4ab2..b7b87318554cc 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -42,5 +42,7 @@ interface INetworkStatsService { void setUidForeground(int uid, boolean uidForeground); /** Force update of statistics. */ void forceUpdate(); + /** Advise persistance threshold; may be overridden internally. */ + void advisePersistThreshold(long thresholdBytes); } diff --git a/core/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java index b35dd1eac0deb..13a692ea18c95 100644 --- a/core/java/android/util/MathUtils.java +++ b/core/java/android/util/MathUtils.java @@ -39,6 +39,10 @@ public final class MathUtils { return amount < low ? low : (amount > high ? high : amount); } + public static long constrain(long amount, long low, long high) { + return amount < low ? low : (amount > high ? high : amount); + } + public static float constrain(float amount, float low, float high) { return amount < low ? low : (amount > high ? high : amount); } diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 8ebe224f15fd4..961d042c19a45 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -118,6 +118,7 @@ import android.telephony.TelephonyManager; import android.text.format.Formatter; import android.text.format.Time; import android.util.Log; +import android.util.MathUtils; import android.util.NtpTrustedTime; import android.util.Slog; import android.util.SparseArray; @@ -166,7 +167,7 @@ import libcore.io.IoUtils; */ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String TAG = "NetworkPolicy"; - private static final boolean LOGD = true; + private static final boolean LOGD = false; private static final boolean LOGV = false; private static final int VERSION_INIT = 1; @@ -962,6 +963,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } + long lowestRule = Long.MAX_VALUE; final HashSet newMeteredIfaces = Sets.newHashSet(); // apply each policy that we found ifaces for; compute remaining data @@ -985,6 +987,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + Arrays.toString(ifaces)); } + final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED; final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED; if (hasLimit || policy.metered) { final long quotaBytes; @@ -1014,6 +1017,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { newMeteredIfaces.add(iface); } } + + // keep track of lowest warning or limit of active policies + if (hasWarning && policy.warningBytes < lowestRule) { + lowestRule = policy.warningBytes; + } + if (hasLimit && policy.limitBytes < lowestRule) { + lowestRule = policy.limitBytes; + } + } + + try { + // make sure stats are recorded frequently enough; we aim for 2MB + // threshold for 2GB/month rules. + final long persistThreshold = lowestRule / 1000; + mNetworkStats.advisePersistThreshold(persistThreshold); + } catch (RemoteException e) { + // ignored; service lives in system_server } // remove quota on any trailing interfaces diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java index 2892a74845d45..c2e475a5b4ead 100644 --- a/services/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/java/com/android/server/net/NetworkStatsCollection.java @@ -186,12 +186,12 @@ public class NetworkStatsCollection implements FileRotator.Reader { if (history.size() == 0) return; noteRecordedHistory(history.getStart(), history.getEnd(), history.getTotalBytes()); - final NetworkStatsHistory existing = mStats.get(key); - if (existing != null) { - existing.recordEntireHistory(history); - } else { - mStats.put(key, history); + NetworkStatsHistory target = mStats.get(key); + if (target == null) { + target = new NetworkStatsHistory(history.getBucketDuration()); + mStats.put(key, target); } + target.recordEntireHistory(history); } /** diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java index 743a746683fe5..2ce7771135560 100644 --- a/services/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/java/com/android/server/net/NetworkStatsRecorder.java @@ -17,6 +17,8 @@ package com.android.server.net; import static android.net.NetworkStats.TAG_NONE; +import static android.net.TrafficStats.KB_IN_BYTES; +import static android.net.TrafficStats.MB_IN_BYTES; import static com.android.internal.util.Preconditions.checkNotNull; import android.net.NetworkStats; @@ -25,6 +27,7 @@ import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.util.Log; +import android.util.MathUtils; import android.util.Slog; import com.android.internal.util.FileRotator; @@ -58,9 +61,9 @@ public class NetworkStatsRecorder { private final String mCookie; private final long mBucketDuration; - private final long mPersistThresholdBytes; private final boolean mOnlyTags; + private long mPersistThresholdBytes = 2 * MB_IN_BYTES; private NetworkStats mLastSnapshot; private final NetworkStatsCollection mPending; @@ -71,13 +74,12 @@ public class NetworkStatsRecorder { private WeakReference mComplete; public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver observer, - String cookie, long bucketDuration, long persistThresholdBytes, boolean onlyTags) { + String cookie, long bucketDuration, boolean onlyTags) { mRotator = checkNotNull(rotator, "missing FileRotator"); mObserver = checkNotNull(observer, "missing NonMonotonicObserver"); mCookie = cookie; mBucketDuration = bucketDuration; - mPersistThresholdBytes = persistThresholdBytes; mOnlyTags = onlyTags; mPending = new NetworkStatsCollection(bucketDuration); @@ -86,6 +88,12 @@ public class NetworkStatsRecorder { mPendingRewriter = new CombiningRewriter(mPending); } + public void setPersistThreshold(long thresholdBytes) { + if (LOGV) Slog.v(TAG, "setPersistThreshold() with " + thresholdBytes); + mPersistThresholdBytes = MathUtils.constrain( + thresholdBytes, 1 * KB_IN_BYTES, 100 * MB_IN_BYTES); + } + public void resetLocked() { mLastSnapshot = null; mPending.reset(); @@ -153,7 +161,7 @@ public class NetworkStatsRecorder { continue; } - // skip when no delta occured + // skip when no delta occurred if (entry.isEmpty()) continue; // only record tag data when requested diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 6d6f59cf0f675..a9d4b596220ff 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -36,6 +36,7 @@ import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; +import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.provider.Settings.Secure.NETSTATS_DEV_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_DEV_DELETE_AGE; @@ -49,6 +50,10 @@ import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION; import static android.provider.Settings.Secure.NETSTATS_UID_DELETE_AGE; import static android.provider.Settings.Secure.NETSTATS_UID_PERSIST_BYTES; import static android.provider.Settings.Secure.NETSTATS_UID_ROTATE_AGE; +import static android.provider.Settings.Secure.NETSTATS_UID_TAG_BUCKET_DURATION; +import static android.provider.Settings.Secure.NETSTATS_UID_TAG_DELETE_AGE; +import static android.provider.Settings.Secure.NETSTATS_UID_TAG_PERSIST_BYTES; +import static android.provider.Settings.Secure.NETSTATS_UID_TAG_ROTATE_AGE; import static android.telephony.PhoneStateListener.LISTEN_DATA_CONNECTION_STATE; import static android.telephony.PhoneStateListener.LISTEN_NONE; import static android.text.format.DateUtils.DAY_IN_MILLIS; @@ -94,10 +99,12 @@ import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; +import android.provider.Settings.Secure; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; +import android.util.MathUtils; import android.util.NtpTrustedTime; import android.util.Slog; import android.util.SparseIntArray; @@ -169,19 +176,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public interface NetworkStatsSettings { public long getPollInterval(); public long getTimeCacheMaxAge(); - public long getGlobalAlertBytes(); public boolean getSampleEnabled(); public static class Config { public final long bucketDuration; - public final long persistBytes; public final long rotateAgeMillis; public final long deleteAgeMillis; - public Config(long bucketDuration, long persistBytes, long rotateAgeMillis, - long deleteAgeMillis) { + public Config(long bucketDuration, long rotateAgeMillis, long deleteAgeMillis) { this.bucketDuration = bucketDuration; - this.persistBytes = persistBytes; this.rotateAgeMillis = rotateAgeMillis; this.deleteAgeMillis = deleteAgeMillis; } @@ -191,6 +194,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public Config getXtConfig(); public Config getUidConfig(); public Config getUidTagConfig(); + + public long getGlobalAlertBytes(long def); + public long getDevPersistBytes(long def); + public long getXtPersistBytes(long def); + public long getUidPersistBytes(long def); + public long getUidTagPersistBytes(long def); } private final Object mStatsLock = new Object(); @@ -223,6 +232,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final Handler mHandler; private boolean mSystemReady; + private long mPersistThreshold = 2 * MB_IN_BYTES; + private long mGlobalAlertBytes; public NetworkStatsService( Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) { @@ -275,6 +286,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mUidRecorder = buildRecorder(PREFIX_UID, mSettings.getUidConfig(), false); mUidTagRecorder = buildRecorder(PREFIX_UID_TAG, mSettings.getUidTagConfig(), true); + updatePersistThresholds(); + synchronized (mStatsLock) { // upgrade any legacy stats, migrating them to rotated files maybeUpgradeLegacyStatsLocked(); @@ -325,10 +338,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private NetworkStatsRecorder buildRecorder( String prefix, NetworkStatsSettings.Config config, boolean includeTags) { - return new NetworkStatsRecorder( - new FileRotator(mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis), - mNonMonotonicObserver, prefix, config.bucketDuration, config.persistBytes, - includeTags); + return new NetworkStatsRecorder(new FileRotator( + mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis), + mNonMonotonicObserver, prefix, config.bucketDuration, includeTags); } private void shutdownLocked() { @@ -414,8 +426,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ private void registerGlobalAlert() { try { - final long alertBytes = mSettings.getGlobalAlertBytes(); - mNetworkManager.setGlobalAlert(alertBytes); + mNetworkManager.setGlobalAlert(mGlobalAlertBytes); } catch (IllegalStateException e) { Slog.w(TAG, "problem registering for global alert: " + e); } catch (RemoteException e) { @@ -437,14 +448,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private NetworkStatsCollection getUidComplete() { if (mUidComplete == null) { - mUidComplete = mUidRecorder.getOrLoadCompleteLocked(); + synchronized (mStatsLock) { + mUidComplete = mUidRecorder.getOrLoadCompleteLocked(); + } } return mUidComplete; } private NetworkStatsCollection getUidTagComplete() { if (mUidTagComplete == null) { - mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked(); + synchronized (mStatsLock) { + mUidTagComplete = mUidTagRecorder.getOrLoadCompleteLocked(); + } } return mUidTagComplete; } @@ -584,6 +599,45 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + @Override + public void advisePersistThreshold(long thresholdBytes) { + mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG); + assertBandwidthControlEnabled(); + + // clamp threshold into safe range + mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES); + updatePersistThresholds(); + + if (LOGV) { + Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to " + + mPersistThreshold); + } + + // persist if beyond new thresholds + final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() + : System.currentTimeMillis(); + mDevRecorder.maybePersistLocked(currentTime); + mXtRecorder.maybePersistLocked(currentTime); + mUidRecorder.maybePersistLocked(currentTime); + mUidTagRecorder.maybePersistLocked(currentTime); + + // re-arm global alert + registerGlobalAlert(); + } + + /** + * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to + * reflect current {@link #mPersistThreshold} value. Always defers to + * {@link Secure} values when defined. + */ + private void updatePersistThresholds() { + mDevRecorder.setPersistThreshold(mSettings.getDevPersistBytes(mPersistThreshold)); + mXtRecorder.setPersistThreshold(mSettings.getXtPersistBytes(mPersistThreshold)); + mUidRecorder.setPersistThreshold(mSettings.getUidPersistBytes(mPersistThreshold)); + mUidTagRecorder.setPersistThreshold(mSettings.getUidTagPersistBytes(mPersistThreshold)); + mGlobalAlertBytes = mSettings.getGlobalAlertBytes(mPersistThreshold); + } + /** * Receiver that watches for {@link IConnectivityManager} to claim network * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()} @@ -1122,41 +1176,50 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getSecureLong(NETSTATS_TIME_CACHE_MAX_AGE, DAY_IN_MILLIS); } @Override - public long getGlobalAlertBytes() { - return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, 2 * MB_IN_BYTES); + public long getGlobalAlertBytes(long def) { + return getSecureLong(NETSTATS_GLOBAL_ALERT_BYTES, def); } @Override public boolean getSampleEnabled() { return getSecureBoolean(NETSTATS_SAMPLE_ENABLED, true); } - @Override public Config getDevConfig() { return new Config(getSecureLong(NETSTATS_DEV_BUCKET_DURATION, HOUR_IN_MILLIS), - getSecureLong(NETSTATS_DEV_PERSIST_BYTES, 2 * MB_IN_BYTES), getSecureLong(NETSTATS_DEV_ROTATE_AGE, 15 * DAY_IN_MILLIS), getSecureLong(NETSTATS_DEV_DELETE_AGE, 90 * DAY_IN_MILLIS)); } - @Override public Config getXtConfig() { return getDevConfig(); } - @Override public Config getUidConfig() { return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS), - getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES), getSecureLong(NETSTATS_UID_ROTATE_AGE, 15 * DAY_IN_MILLIS), getSecureLong(NETSTATS_UID_DELETE_AGE, 90 * DAY_IN_MILLIS)); } - @Override public Config getUidTagConfig() { - return new Config(getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS), - getSecureLong(NETSTATS_UID_PERSIST_BYTES, 2 * MB_IN_BYTES), - getSecureLong(NETSTATS_UID_ROTATE_AGE, 5 * DAY_IN_MILLIS), - getSecureLong(NETSTATS_UID_DELETE_AGE, 15 * DAY_IN_MILLIS)); + return new Config(getSecureLong(NETSTATS_UID_TAG_BUCKET_DURATION, 2 * HOUR_IN_MILLIS), + getSecureLong(NETSTATS_UID_TAG_ROTATE_AGE, 5 * DAY_IN_MILLIS), + getSecureLong(NETSTATS_UID_TAG_DELETE_AGE, 15 * DAY_IN_MILLIS)); + } + @Override + public long getDevPersistBytes(long def) { + return getSecureLong(NETSTATS_DEV_PERSIST_BYTES, def); + } + @Override + public long getXtPersistBytes(long def) { + return getDevPersistBytes(def); + } + @Override + public long getUidPersistBytes(long def) { + return getSecureLong(NETSTATS_UID_PERSIST_BYTES, def); + } + @Override + public long getUidTagPersistBytes(long def) { + return getSecureLong(NETSTATS_UID_TAG_PERSIST_BYTES, def); } } } diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java index 10f8e61c3d7ce..332d1980167f7 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java @@ -93,6 +93,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { private static final String IMSI_1 = "310004"; private static final String IMSI_2 = "310260"; + private static final String TEST_SSID = "AndroidAP"; private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard(); private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); @@ -162,9 +163,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { @Override public void tearDown() throws Exception { - for (File file : mStatsDir.listFiles()) { - file.delete(); - } + IoUtils.deleteContents(mStatsDir); mServiceContext = null; mStatsDir = null; @@ -847,14 +846,19 @@ public class NetworkStatsServiceTest extends AndroidTestCase { throws Exception { expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes(); expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes(); - expect(mSettings.getGlobalAlertBytes()).andReturn(MB_IN_BYTES).anyTimes(); expect(mSettings.getSampleEnabled()).andReturn(true).anyTimes(); - final Config config = new Config(bucketDuration, persistBytes, deleteAge, deleteAge); + final Config config = new Config(bucketDuration, deleteAge, deleteAge); expect(mSettings.getDevConfig()).andReturn(config).anyTimes(); expect(mSettings.getXtConfig()).andReturn(config).anyTimes(); expect(mSettings.getUidConfig()).andReturn(config).anyTimes(); expect(mSettings.getUidTagConfig()).andReturn(config).anyTimes(); + + expect(mSettings.getGlobalAlertBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes(); + expect(mSettings.getDevPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes(); + expect(mSettings.getXtPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes(); + expect(mSettings.getUidPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes(); + expect(mSettings.getUidTagPersistBytes(anyLong())).andReturn(MB_IN_BYTES).anyTimes(); } private void expectCurrentTime() throws Exception { @@ -905,7 +909,7 @@ public class NetworkStatsServiceTest extends AndroidTestCase { info.setDetailedState(DetailedState.CONNECTED, null, null); final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(TEST_IFACE); - return new NetworkState(info, prop, null); + return new NetworkState(info, prop, null, null, TEST_SSID); } private static NetworkState buildMobile3gState(String subscriberId) {