Merge "DO NOT MERGE IpConnectivityMetrics: rate limit ApfProgramEvents" into nyc-mr2-dev
This commit is contained in:
@@ -23,7 +23,8 @@ import android.net.ConnectivityMetricsEvent;
|
|||||||
interface IIpConnectivityMetrics {
|
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);
|
int logEvent(in ConnectivityMetricsEvent event);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -177,4 +177,3 @@ public class TokenBucketTest extends TestCase {
|
|||||||
|
|
||||||
interface Fn { void call(); }
|
interface Fn { void call(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,19 @@ package com.android.server.connectivity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityMetricsEvent;
|
import android.net.ConnectivityMetricsEvent;
|
||||||
import android.net.IIpConnectivityMetrics;
|
import android.net.IIpConnectivityMetrics;
|
||||||
|
import android.net.metrics.ApfProgramEvent;
|
||||||
import android.net.metrics.IpConnectivityLog;
|
import android.net.metrics.IpConnectivityLog;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.format.DateUtils;
|
||||||
|
import android.util.ArrayMap;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.util.TokenBucket;
|
||||||
import com.android.server.SystemService;
|
import com.android.server.SystemService;
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -56,6 +60,8 @@ final public class IpConnectivityMetrics extends SystemService {
|
|||||||
// Maximum size of the event buffer.
|
// Maximum size of the event buffer.
|
||||||
private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10;
|
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.
|
// Lock ensuring that concurrent manipulations of the event buffer are correct.
|
||||||
// There are three concurrent operations to synchronize:
|
// There are three concurrent operations to synchronize:
|
||||||
// - appending events to the buffer.
|
// - appending events to the buffer.
|
||||||
@@ -73,6 +79,8 @@ final public class IpConnectivityMetrics extends SystemService {
|
|||||||
private int mDropped;
|
private int mDropped;
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mCapacity;
|
private int mCapacity;
|
||||||
|
@GuardedBy("mLock")
|
||||||
|
private final ArrayMap<Class<?>, TokenBucket> mBuckets = makeRateLimitingBuckets();
|
||||||
|
|
||||||
private final ToIntFunction<Context> mCapacityGetter;
|
private final ToIntFunction<Context> mCapacityGetter;
|
||||||
|
|
||||||
@@ -122,6 +130,10 @@ final public class IpConnectivityMetrics extends SystemService {
|
|||||||
if (event == null) {
|
if (event == null) {
|
||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
if (isRateLimited(event)) {
|
||||||
|
// Do not count as a dropped event. TODO: consider adding separate counter
|
||||||
|
return ERROR_RATE_LIMITED;
|
||||||
|
}
|
||||||
if (left == 0) {
|
if (left == 0) {
|
||||||
mDropped++;
|
mDropped++;
|
||||||
return 0;
|
return 0;
|
||||||
@@ -131,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() {
|
private String flushEncodedOutput() {
|
||||||
final ArrayList<ConnectivityMetricsEvent> events;
|
final ArrayList<ConnectivityMetricsEvent> events;
|
||||||
final int dropped;
|
final int dropped;
|
||||||
@@ -253,4 +270,11 @@ final public class IpConnectivityMetrics extends SystemService {
|
|||||||
}
|
}
|
||||||
return Math.min(size, MAXIMUM_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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityMetricsEvent;
|
import android.net.ConnectivityMetricsEvent;
|
||||||
import android.net.IIpConnectivityMetrics;
|
import android.net.IIpConnectivityMetrics;
|
||||||
|
import android.net.metrics.ApfProgramEvent;
|
||||||
import android.net.metrics.ApfStats;
|
import android.net.metrics.ApfStats;
|
||||||
import android.net.metrics.DefaultNetworkEvent;
|
import android.net.metrics.DefaultNetworkEvent;
|
||||||
import android.net.metrics.DhcpClientEvent;
|
import android.net.metrics.DhcpClientEvent;
|
||||||
@@ -112,6 +113,27 @@ public class IpConnectivityMetricsTest extends TestCase {
|
|||||||
assertEquals("", output3);
|
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() {
|
public void testEndToEndLogging() {
|
||||||
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
|
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user