blob: b320b755f3f465c5990972dea8f2bc9527a67520 [file] [log] [blame]
/*
* 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 events */
public int eventCount = 0;
/** 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;
}
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();
}
}