Merge "DO NOT MERGE IpConnectivityMetrics: rate limit ApfProgramEvents" into nyc-mr2-dev

This commit is contained in:
Hugo Benichi
2016-12-13 03:55:29 +00:00
committed by Android (Google) Code Review
4 changed files with 48 additions and 2 deletions

View File

@@ -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);
} }

View File

@@ -177,4 +177,3 @@ public class TokenBucketTest extends TestCase {
interface Fn { void call(); } interface Fn { void call(); }
} }

View File

@@ -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;
}
} }

View File

@@ -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);