/*
 * 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}
/** * 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.
/** 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.
/** Transports of the network associated with the event, as defined in NetworkCapabilities. */
public final long transports;
How many events resulted in a given errno.
/** 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.
/** Latencies of successful blocking connects. TODO: add non-blocking connects latencies. */
public final IntArray latencies = new IntArray();
TokenBucket for rate limiting latency recording.
/** TokenBucket for rate limiting latency recording. */
public final TokenBucket mLatencyTb;
Maximum number of latency values recorded.
/** Maximum number of latency values recorded. */
public final int mMaxLatencyRecords;
Total count of events
/** Total count of events */
public int eventCount = 0;
Total count of successful connects.
/** Total count of successful connects. */
public int connectCount = 0;
Total count of successful connects done in blocking mode.
/** Total count of successful connects done in blocking mode. */
public int connectBlockingCount = 0;
Total count of successful connects with IPv6 socket address.
/** 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; } boolean addEvent(int errno, int latencyMs, String ipAddr) { eventCount++; if (isSuccess(errno)) { countConnect(errno, ipAddr); countLatency(errno, latencyMs); return true; } else { countError(errno); return false; } } 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); } 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(netId).append(", "); for (int t : BitUtils.unpackBits(transports)) { builder.append(NetworkCapabilities.transportNameOf(t)).append(", "); } builder.append(String.format("%d events, ", eventCount)); 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(); } }