IpConnectivityMetrics: rate limit ApfProgramEvents
This patch uses the previously introduced TokenBucket to rate limit
ApfProgramEvents, still allowing for burst of ApfProgramEvents when a
new interface is set up (due to ipv4 provisioning, multicast lock, ipv6 RAs
triggering new APF program events in short amounts of time).
Test: new test in IpConnectivityMetricsTest
Bug: 1550402
Change-Id: I1a544a8d62e6b02f0ed0088ab76c4bad338d2f44
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 346ba20..a6522a3 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -19,15 +19,19 @@
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;
@@ -49,6 +53,8 @@
// 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:
// - appending events to the buffer.
@@ -66,6 +72,8 @@
private int mDropped;
@GuardedBy("mLock")
private int mCapacity;
+ @GuardedBy("mLock")
+ private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
private final ToIntFunction<Context> mCapacityGetter;
@@ -115,6 +123,10 @@
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;
@@ -124,6 +136,11 @@
}
}
+ 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;
@@ -246,4 +263,11 @@
}
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;
+ }
}