blob: d6de815c8ef41e7eda063e5d1f9ee5e46c70fae1 [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 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;
}
}