summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2016-10-24 14:45:42 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-10-24 14:45:46 +0000
commit03db8e4b58342d5ada64f29b049cf30da0ba9369 (patch)
tree4defff7ebaaa8e3faf8c503ef00c16d3892c4066
parent4e5278165bdb4fb73dd3040ee13b4ecc8a490a0d (diff)
parente1c173d2240a8eedf7685c9371087dc047a6931f (diff)
Merge changes I1a544a8d,Ie78d3fb0
* changes: IpConnectivityMetrics: rate limit ApfProgramEvents IpConnectivityMetrics reads buffer size in settings
-rw-r--r--core/java/android/net/IIpConnectivityMetrics.aidl3
-rwxr-xr-xcore/java/android/provider/Settings.java7
-rw-r--r--core/tests/coretests/src/android/util/TokenBucketTest.java1
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java48
-rw-r--r--services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java24
5 files changed, 78 insertions, 5 deletions
diff --git a/core/java/android/net/IIpConnectivityMetrics.aidl b/core/java/android/net/IIpConnectivityMetrics.aidl
index 8f634bbf0cc9..d36b7661aaa3 100644
--- a/core/java/android/net/IIpConnectivityMetrics.aidl
+++ b/core/java/android/net/IIpConnectivityMetrics.aidl
@@ -23,7 +23,8 @@ import android.net.ConnectivityMetricsEvent;
interface IIpConnectivityMetrics {
/**
- * @return number of remaining available slots in buffer.
+ * @return the number of remaining available slots in buffer,
+ * or -1 if the event was dropped due to rate limiting.
*/
int logEvent(in ConnectivityMetricsEvent event);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ceb632bd8b70..d3a978c4c14c 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7232,6 +7232,13 @@ public final class Settings {
*/
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
+ /**
+ * Size of the event buffer for IP connectivity metrics.
+ * @hide
+ */
+ public static final String CONNECTIVITY_METRICS_BUFFER_SIZE =
+ "connectivity_metrics_buffer_size";
+
/** {@hide} */
public static final String NETSTATS_ENABLED = "netstats_enabled";
/** {@hide} */
diff --git a/core/tests/coretests/src/android/util/TokenBucketTest.java b/core/tests/coretests/src/android/util/TokenBucketTest.java
index a053ad33f589..f7ac20c7287f 100644
--- a/core/tests/coretests/src/android/util/TokenBucketTest.java
+++ b/core/tests/coretests/src/android/util/TokenBucketTest.java
@@ -177,4 +177,3 @@ public class TokenBucketTest extends TestCase {
interface Fn { void call(); }
}
-
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index e176fe1575c1..be681739d2ee 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -19,19 +19,25 @@ package com.android.server.connectivity;
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.metrics.ApfProgramEvent;
import android.net.metrics.IpConnectivityLog;
import android.os.IBinder;
import android.os.Parcelable;
+import android.provider.Settings;
import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.Base64;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.TokenBucket;
import com.android.server.SystemService;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.function.ToIntFunction;
import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent;
@@ -51,6 +57,10 @@ final public class IpConnectivityMetrics extends SystemService {
// Default size of the event buffer. Once the buffer is full, incoming events are dropped.
private static final int DEFAULT_BUFFER_SIZE = 2000;
+ // Maximum size of the event buffer.
+ private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
+
+ private static final int ERROR_RATE_LIMITED = -1;
// Lock ensuring that concurrent manipulations of the event buffer are correct.
// There are three concurrent operations to synchronize:
@@ -69,12 +79,21 @@ final public class IpConnectivityMetrics extends SystemService {
private int mDropped;
@GuardedBy("mLock")
private int mCapacity;
+ @GuardedBy("mLock")
+ private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
- public IpConnectivityMetrics(Context ctx) {
+ private final ToIntFunction<Context> mCapacityGetter;
+
+ public IpConnectivityMetrics(Context ctx, ToIntFunction<Context> capacityGetter) {
super(ctx);
+ mCapacityGetter = capacityGetter;
initBuffer();
}
+ public IpConnectivityMetrics(Context ctx) {
+ this(ctx, READ_BUFFER_SIZE);
+ }
+
@Override
public void onStart() {
if (DBG) Log.d(TAG, "onStart");
@@ -93,7 +112,7 @@ final public class IpConnectivityMetrics extends SystemService {
@VisibleForTesting
public int bufferCapacity() {
- return DEFAULT_BUFFER_SIZE; // TODO: read from config
+ return mCapacityGetter.applyAsInt(getContext());
}
private void initBuffer() {
@@ -111,6 +130,10 @@ final public class IpConnectivityMetrics extends SystemService {
if (event == null) {
return left;
}
+ if (isRateLimited(event)) {
+ // Do not count as a dropped event. TODO: consider adding separate counter
+ return ERROR_RATE_LIMITED;
+ }
if (left == 0) {
mDropped++;
return 0;
@@ -120,6 +143,11 @@ final public class IpConnectivityMetrics extends SystemService {
}
}
+ private boolean isRateLimited(ConnectivityMetricsEvent event) {
+ TokenBucket tb = mBuckets.get(event.data.getClass());
+ return (tb != null) && !tb.get();
+ }
+
private String flushEncodedOutput() {
final ArrayList<ConnectivityMetricsEvent> events;
final int dropped;
@@ -236,4 +264,20 @@ final public class IpConnectivityMetrics extends SystemService {
getContext().enforceCallingOrSelfPermission(what, "IpConnectivityMetrics");
}
};
+
+ private static final ToIntFunction<Context> READ_BUFFER_SIZE = (ctx) -> {
+ int size = Settings.Global.getInt(ctx.getContentResolver(),
+ Settings.Global.CONNECTIVITY_METRICS_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
+ if (size <= 0) {
+ return DEFAULT_BUFFER_SIZE;
+ }
+ return Math.min(size, MAXIMUM_BUFFER_SIZE);
+ };
+
+ private static ArrayMap<Class<?>, TokenBucket> makeRateLimitingBuckets() {
+ ArrayMap<Class<?>, TokenBucket> map = new ArrayMap<>();
+ // one token every minute, 50 tokens max: burst of ~50 events every hour.
+ map.put(ApfProgramEvent.class, new TokenBucket((int)DateUtils.MINUTE_IN_MILLIS, 50));
+ return map;
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 8bdc829f7bfa..aa491bbabd79 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
@@ -57,7 +58,7 @@ public class IpConnectivityMetricsTest extends TestCase {
public void setUp() {
MockitoAnnotations.initMocks(this);
- mService = new IpConnectivityMetrics(mCtx);
+ mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
}
public void testLoggingEvents() throws Exception {
@@ -112,6 +113,27 @@ public class IpConnectivityMetricsTest extends TestCase {
assertEquals("", output3);
}
+ public void testRateLimiting() {
+ final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
+ final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
+ final long fakeTimestamp = 1;
+
+ int attempt = 100; // More than burst quota, but less than buffer size.
+ for (int i = 0; i < attempt; i++) {
+ logger.log(ev);
+ }
+
+ String output1 = getdump("flush");
+ assertFalse("".equals(output1));
+
+ for (int i = 0; i < attempt; i++) {
+ assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
+ }
+
+ String output2 = getdump("flush");
+ assertEquals("", output2);
+ }
+
public void testEndToEndLogging() {
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);