/* * 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 android.net.metrics; import android.net.NetworkCapabilities; import android.system.OsConstants; import android.util.IntArray; import android.util.SparseIntArray; import com.android.internal.util.BitUtils; import com.android.internal.util.TokenBucket; /** * A class that aggregates connect() statistics. * {@hide} */ public class ConnectStats { private final static int EALREADY = OsConstants.EALREADY; private final static int EINPROGRESS = OsConstants.EINPROGRESS; /** Network id of the network associated with the event, or 0 if unspecified. */ public final int netId; /** Transports of the network associated with the event, as defined in NetworkCapabilities. */ public final long transports; /** How many events resulted in a given errno. */ public final SparseIntArray errnos = new SparseIntArray(); /** Latencies of successful blocking connects. TODO: add non-blocking connects latencies. */ public final IntArray latencies = new IntArray(); /** TokenBucket for rate limiting latency recording. */ public final TokenBucket mLatencyTb; /** Maximum number of latency values recorded. */ public final int mMaxLatencyRecords; /** Total count of successful connects. */ public int connectCount = 0; /** Total count of successful connects done in blocking mode. */ public int connectBlockingCount = 0; /** Total count of successful connects with IPv6 socket address. */ public int ipv6ConnectCount = 0; public ConnectStats(int netId, long transports, TokenBucket tb, int maxLatencyRecords) { this.netId = netId; this.transports = transports; mLatencyTb = tb; mMaxLatencyRecords = maxLatencyRecords; } public void addEvent(int errno, int latencyMs, String ipAddr) { if (isSuccess(errno)) { countConnect(errno, ipAddr); countLatency(errno, latencyMs); } else { countError(errno); } } private void countConnect(int errno, String ipAddr) { connectCount++; if (!isNonBlocking(errno)) { connectBlockingCount++; } if (isIPv6(ipAddr)) { ipv6ConnectCount++; } } private void countLatency(int errno, int ms) { if (isNonBlocking(errno)) { // Ignore connect() on non-blocking sockets return; } if (!mLatencyTb.get()) { // Rate limited return; } if (latencies.size() >= mMaxLatencyRecords) { // Hard limit the total number of latency measurements. return; } latencies.add(ms); } private void countError(int errno) { final int newcount = errnos.get(errno, 0) + 1; errnos.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(":"); } @Override public String toString() { StringBuilder builder = new StringBuilder("ConnectStats(").append(netId).append(", "); for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } builder.append(String.format("%d success, ", connectCount)); builder.append(String.format("%d blocking, ", connectBlockingCount)); builder.append(String.format("%d IPv6 dst", ipv6ConnectCount)); for (int i = 0; i < errnos.size(); i++) { String errno = OsConstants.errnoName(errnos.keyAt(i)); int count = errnos.valueAt(i); builder.append(String.format(", %s: %d", errno, count)); } return builder.append(")").toString(); } }