Loading core/java/com/android/internal/util/TokenBucket.java +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ import static com.android.internal.util.Preconditions.checkArgumentPositive; * The available amount of tokens is computed lazily when the bucket state is inspected. * Therefore it is purely synchronous and does not involve any asynchronous activity. * It is not synchronized in any way and not a thread-safe object. * * {@hide} */ public class TokenBucket { Loading services/core/java/com/android/server/connectivity/ConnectStats.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.connectivity; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics; import com.android.server.connectivity.metrics.IpConnectivityLogClass.Pair; /** * A class that aggregates connect() statistics and helps build * IpConnectivityLogClass.ConnectStatistics instances. * * {@hide} */ public class ConnectStats { private final static int EALREADY = OsConstants.EALREADY; private final static int EINPROGRESS = OsConstants.EINPROGRESS; /** How many events resulted in a given errno. */ private final SparseIntArray mErrnos = new SparseIntArray(); /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */ private final IntArray mLatencies = new IntArray(); /** TokenBucket for rate limiting latency recording. */ private final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ private final int mMaxLatencyRecords; /** Total count of successful connects. */ private int mConnectCount = 0; /** Total count of successful connects with IPv6 socket address. */ private int mIpv6ConnectCount = 0; public ConnectStats(TokenBucket tb, int maxLatencyRecords) { mLatencyTb = tb; mMaxLatencyRecords = maxLatencyRecords; } public ConnectStatistics toProto() { ConnectStatistics stats = new ConnectStatistics(); stats.connectCount = mConnectCount; stats.ipv6AddrCount = mIpv6ConnectCount; stats.latenciesMs = mLatencies.toArray(); stats.errnosCounters = toPairArrays(mErrnos); return stats; } public void addEvent(int errno, int latencyMs, String ipAddr) { if (isSuccess(errno)) { countConnect(ipAddr); countLatency(errno, latencyMs); } else { countError(errno); } } private void countConnect(String ipAddr) { mConnectCount++; if (isIPv6(ipAddr)) mIpv6ConnectCount++; } private void countLatency(int errno, int ms) { if (isNonBlocking(errno)) { // Ignore connect() on non-blocking sockets return; } if (!mLatencyTb.get()) { // Rate limited return; } if (mLatencies.size() >= mMaxLatencyRecords) { // Hard limit the total number of latency measurements. return; } mLatencies.add(ms); } private void countError(int errno) { final int newcount = mErrnos.get(errno, 0) + 1; mErrnos.put(errno, newcount); } private static boolean isSuccess(int errno) { return (errno == 0) || isNonBlocking(errno); } private static boolean isNonBlocking(int errno) { // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. return (errno == EINPROGRESS) || (errno == EALREADY); } private static boolean isIPv6(String ipAddr) { return ipAddr.contains(":"); } private static Pair[] toPairArrays(SparseIntArray counts) { final int s = counts.size(); Pair[] pairs = new Pair[s]; for (int i = 0; i < s; i++) { Pair p = new Pair(); p.key = counts.keyAt(i); p.value = counts.valueAt(i); pairs[i] = p; } return pairs; } } services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java +4 −4 Original line number Diff line number Diff line Loading @@ -43,10 +43,10 @@ final public class IpConnectivityEventBuilder { private IpConnectivityEventBuilder() { } public static byte[] serialize(int dropped, List<ConnectivityMetricsEvent> events) public static byte[] serialize(int dropped, List<IpConnectivityEvent> events) throws IOException { final IpConnectivityLog log = new IpConnectivityLog(); log.events = toProto(events); log.events = events.toArray(new IpConnectivityEvent[events.size()]); log.droppedEvents = dropped; if ((log.events.length > 0) || (dropped > 0)) { // Only write version number if log has some information at all. Loading @@ -55,7 +55,7 @@ final public class IpConnectivityEventBuilder { return IpConnectivityLog.toByteArray(log); } public static IpConnectivityEvent[] toProto(List<ConnectivityMetricsEvent> eventsIn) { public static List<IpConnectivityEvent> toProto(List<ConnectivityMetricsEvent> eventsIn) { final ArrayList<IpConnectivityEvent> eventsOut = new ArrayList<>(eventsIn.size()); for (ConnectivityMetricsEvent in : eventsIn) { final IpConnectivityEvent out = toProto(in); Loading @@ -64,7 +64,7 @@ final public class IpConnectivityEventBuilder { } eventsOut.add(out); } return eventsOut.toArray(new IpConnectivityEvent[eventsOut.size()]); return eventsOut; } public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) { Loading services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +11 −3 Original line number Diff line number Diff line Loading @@ -36,14 +36,14 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.TokenBucket; import com.android.server.SystemService; import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.function.ToIntFunction; import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; /** {@hide} */ final public class IpConnectivityMetrics extends SystemService { private static final String TAG = IpConnectivityMetrics.class.getSimpleName(); Loading @@ -63,6 +63,8 @@ final public class IpConnectivityMetrics extends SystemService { // Maximum size of the event buffer. private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10; private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000; private static final int ERROR_RATE_LIMITED = -1; // Lock ensuring that concurrent manipulations of the event buffer are correct. Loading Loading @@ -160,9 +162,15 @@ final public class IpConnectivityMetrics extends SystemService { initBuffer(); } final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events); if (mNetdListener != null) { mNetdListener.flushStatistics(protoEvents); } final byte[] data; try { data = IpConnectivityEventBuilder.serialize(dropped, events); data = IpConnectivityEventBuilder.serialize(dropped, protoEvents); } catch (IOException e) { Log.e(TAG, "could not serialize events", e); return ""; Loading services/core/java/com/android/server/connectivity/NetdEventListenerService.java +39 −5 Original line number Diff line number Diff line Loading @@ -19,25 +19,27 @@ package com.android.server.connectivity; import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.INetdEventCallback; import android.net.Network; import android.net.NetworkRequest; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; import android.os.RemoteException; import android.text.format.DateUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics; import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * Implementation of the INetdEventListener interface. */ Loading @@ -52,6 +54,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // TODO: read this constant from system property private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100; // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum // bursts of 5000 measurements. private static final int CONNECT_LATENCY_BURST_LIMIT = 5000; private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS; private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000; // Stores the results of a number of consecutive DNS lookups on the same network. // This class is not thread-safe and it is the responsibility of the service to call its methods // on one thread at a time. Loading Loading @@ -121,6 +129,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } }; @GuardedBy("this") private final TokenBucket mConnectTb = new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT); @GuardedBy("this") private ConnectStats mConnectStats = makeConnectStats(); // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM // by the device owner. It's DevicePolicyManager's responsibility to ensure that. @GuardedBy("this") Loading Loading @@ -175,13 +189,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // This method must not block or perform long-running operations. public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port, int uid) throws RemoteException { maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs); maybeVerboseLog("onConnectEvent(%d, %d)", netId, latencyMs); mConnectStats.addEvent(error, latencyMs, ipAddr); if (mNetdEventCallback != null) { mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid); } } public synchronized void flushStatistics(List<IpConnectivityEvent> events) { events.add(flushConnectStats()); // TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies } private IpConnectivityEvent flushConnectStats() { IpConnectivityEvent ev = new IpConnectivityEvent(); ev.connectStatistics = mConnectStats.toProto(); // TODO: add transport information mConnectStats = makeConnectStats(); return ev; } public synchronized void dump(PrintWriter writer) { IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println(TAG + ":"); Loading @@ -189,9 +218,14 @@ public class NetdEventListenerService extends INetdEventListener.Stub { for (DnsEventBatch batch : mEventBatches.values()) { pw.println(batch.toString()); } // TODO: also dump ConnectStats pw.decreaseIndent(); } private ConnectStats makeConnectStats() { return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS); } private static void maybeLog(String s, Object... args) { if (DBG) Log.d(TAG, String.format(s, args)); } Loading Loading
core/java/com/android/internal/util/TokenBucket.java +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ import static com.android.internal.util.Preconditions.checkArgumentPositive; * The available amount of tokens is computed lazily when the bucket state is inspected. * Therefore it is purely synchronous and does not involve any asynchronous activity. * It is not synchronized in any way and not a thread-safe object. * * {@hide} */ public class TokenBucket { Loading
services/core/java/com/android/server/connectivity/ConnectStats.java 0 → 100644 +123 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.connectivity; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics; import com.android.server.connectivity.metrics.IpConnectivityLogClass.Pair; /** * A class that aggregates connect() statistics and helps build * IpConnectivityLogClass.ConnectStatistics instances. * * {@hide} */ public class ConnectStats { private final static int EALREADY = OsConstants.EALREADY; private final static int EINPROGRESS = OsConstants.EINPROGRESS; /** How many events resulted in a given errno. */ private final SparseIntArray mErrnos = new SparseIntArray(); /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */ private final IntArray mLatencies = new IntArray(); /** TokenBucket for rate limiting latency recording. */ private final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ private final int mMaxLatencyRecords; /** Total count of successful connects. */ private int mConnectCount = 0; /** Total count of successful connects with IPv6 socket address. */ private int mIpv6ConnectCount = 0; public ConnectStats(TokenBucket tb, int maxLatencyRecords) { mLatencyTb = tb; mMaxLatencyRecords = maxLatencyRecords; } public ConnectStatistics toProto() { ConnectStatistics stats = new ConnectStatistics(); stats.connectCount = mConnectCount; stats.ipv6AddrCount = mIpv6ConnectCount; stats.latenciesMs = mLatencies.toArray(); stats.errnosCounters = toPairArrays(mErrnos); return stats; } public void addEvent(int errno, int latencyMs, String ipAddr) { if (isSuccess(errno)) { countConnect(ipAddr); countLatency(errno, latencyMs); } else { countError(errno); } } private void countConnect(String ipAddr) { mConnectCount++; if (isIPv6(ipAddr)) mIpv6ConnectCount++; } private void countLatency(int errno, int ms) { if (isNonBlocking(errno)) { // Ignore connect() on non-blocking sockets return; } if (!mLatencyTb.get()) { // Rate limited return; } if (mLatencies.size() >= mMaxLatencyRecords) { // Hard limit the total number of latency measurements. return; } mLatencies.add(ms); } private void countError(int errno) { final int newcount = mErrnos.get(errno, 0) + 1; mErrnos.put(errno, newcount); } private static boolean isSuccess(int errno) { return (errno == 0) || isNonBlocking(errno); } private static boolean isNonBlocking(int errno) { // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. return (errno == EINPROGRESS) || (errno == EALREADY); } private static boolean isIPv6(String ipAddr) { return ipAddr.contains(":"); } private static Pair[] toPairArrays(SparseIntArray counts) { final int s = counts.size(); Pair[] pairs = new Pair[s]; for (int i = 0; i < s; i++) { Pair p = new Pair(); p.key = counts.keyAt(i); p.value = counts.valueAt(i); pairs[i] = p; } return pairs; } }
services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java +4 −4 Original line number Diff line number Diff line Loading @@ -43,10 +43,10 @@ final public class IpConnectivityEventBuilder { private IpConnectivityEventBuilder() { } public static byte[] serialize(int dropped, List<ConnectivityMetricsEvent> events) public static byte[] serialize(int dropped, List<IpConnectivityEvent> events) throws IOException { final IpConnectivityLog log = new IpConnectivityLog(); log.events = toProto(events); log.events = events.toArray(new IpConnectivityEvent[events.size()]); log.droppedEvents = dropped; if ((log.events.length > 0) || (dropped > 0)) { // Only write version number if log has some information at all. Loading @@ -55,7 +55,7 @@ final public class IpConnectivityEventBuilder { return IpConnectivityLog.toByteArray(log); } public static IpConnectivityEvent[] toProto(List<ConnectivityMetricsEvent> eventsIn) { public static List<IpConnectivityEvent> toProto(List<ConnectivityMetricsEvent> eventsIn) { final ArrayList<IpConnectivityEvent> eventsOut = new ArrayList<>(eventsIn.size()); for (ConnectivityMetricsEvent in : eventsIn) { final IpConnectivityEvent out = toProto(in); Loading @@ -64,7 +64,7 @@ final public class IpConnectivityEventBuilder { } eventsOut.add(out); } return eventsOut.toArray(new IpConnectivityEvent[eventsOut.size()]); return eventsOut; } public static IpConnectivityEvent toProto(ConnectivityMetricsEvent ev) { Loading
services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java +11 −3 Original line number Diff line number Diff line Loading @@ -36,14 +36,14 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.TokenBucket; import com.android.server.SystemService; import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.function.ToIntFunction; import static com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; /** {@hide} */ final public class IpConnectivityMetrics extends SystemService { private static final String TAG = IpConnectivityMetrics.class.getSimpleName(); Loading @@ -63,6 +63,8 @@ final public class IpConnectivityMetrics extends SystemService { // Maximum size of the event buffer. private static final int MAXIMUM_BUFFER_SIZE = DEFAULT_BUFFER_SIZE * 10; private static final int MAXIMUM_CONNECT_LATENCY_RECORDS = 20000; private static final int ERROR_RATE_LIMITED = -1; // Lock ensuring that concurrent manipulations of the event buffer are correct. Loading Loading @@ -160,9 +162,15 @@ final public class IpConnectivityMetrics extends SystemService { initBuffer(); } final List<IpConnectivityEvent> protoEvents = IpConnectivityEventBuilder.toProto(events); if (mNetdListener != null) { mNetdListener.flushStatistics(protoEvents); } final byte[] data; try { data = IpConnectivityEventBuilder.serialize(dropped, events); data = IpConnectivityEventBuilder.serialize(dropped, protoEvents); } catch (IOException e) { Log.e(TAG, "could not serialize events", e); return ""; Loading
services/core/java/com/android/server/connectivity/NetdEventListenerService.java +39 −5 Original line number Diff line number Diff line Loading @@ -19,25 +19,27 @@ package com.android.server.connectivity; import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.Network; import android.net.INetdEventCallback; import android.net.Network; import android.net.NetworkRequest; import android.net.metrics.DnsEvent; import android.net.metrics.INetdEventListener; import android.net.metrics.IpConnectivityLog; import android.os.RemoteException; import android.text.format.DateUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.TokenBucket; import com.android.server.connectivity.metrics.IpConnectivityLogClass.ConnectStatistics; import com.android.server.connectivity.metrics.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; import java.util.Arrays; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; /** * Implementation of the INetdEventListener interface. */ Loading @@ -52,6 +54,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // TODO: read this constant from system property private static final int MAX_LOOKUPS_PER_DNS_EVENT = 100; // Rate limit connect latency logging to 1 measurement per 15 seconds (5760 / day) with maximum // bursts of 5000 measurements. private static final int CONNECT_LATENCY_BURST_LIMIT = 5000; private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS; private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000; // Stores the results of a number of consecutive DNS lookups on the same network. // This class is not thread-safe and it is the responsibility of the service to call its methods // on one thread at a time. Loading Loading @@ -121,6 +129,12 @@ public class NetdEventListenerService extends INetdEventListener.Stub { } }; @GuardedBy("this") private final TokenBucket mConnectTb = new TokenBucket(CONNECT_LATENCY_FILL_RATE, CONNECT_LATENCY_BURST_LIMIT); @GuardedBy("this") private ConnectStats mConnectStats = makeConnectStats(); // Callback should only be registered/unregistered when logging is being enabled/disabled in DPM // by the device owner. It's DevicePolicyManager's responsibility to ensure that. @GuardedBy("this") Loading Loading @@ -175,13 +189,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub { // This method must not block or perform long-running operations. public synchronized void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port, int uid) throws RemoteException { maybeVerboseLog("onConnectEvent(%d, %d, %dms)", netId, error, latencyMs); maybeVerboseLog("onConnectEvent(%d, %d)", netId, latencyMs); mConnectStats.addEvent(error, latencyMs, ipAddr); if (mNetdEventCallback != null) { mNetdEventCallback.onConnectEvent(ipAddr, port, System.currentTimeMillis(), uid); } } public synchronized void flushStatistics(List<IpConnectivityEvent> events) { events.add(flushConnectStats()); // TODO: migrate DnsEventBatch to IpConnectivityLogClass.DNSLatencies } private IpConnectivityEvent flushConnectStats() { IpConnectivityEvent ev = new IpConnectivityEvent(); ev.connectStatistics = mConnectStats.toProto(); // TODO: add transport information mConnectStats = makeConnectStats(); return ev; } public synchronized void dump(PrintWriter writer) { IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println(TAG + ":"); Loading @@ -189,9 +218,14 @@ public class NetdEventListenerService extends INetdEventListener.Stub { for (DnsEventBatch batch : mEventBatches.values()) { pw.println(batch.toString()); } // TODO: also dump ConnectStats pw.decreaseIndent(); } private ConnectStats makeConnectStats() { return new ConnectStats(mConnectTb, CONNECT_LATENCY_MAXIMUM_RECORDS); } private static void maybeLog(String s, Object... args) { if (DBG) Log.d(TAG, String.format(s, args)); } Loading