ConnectStats.java revision 0d4a398b7846f26e4452180915ecb5a9d4566148
1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.net.metrics; 18 19import android.system.OsConstants; 20import android.util.IntArray; 21import android.util.SparseIntArray; 22import com.android.internal.util.TokenBucket; 23import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ConnectStatistics; 24import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.Pair; 25 26/** 27 * A class that aggregates connect() statistics and helps build 28 * IpConnectivityLogClass.ConnectStatistics instances. 29 * 30 * {@hide} 31 */ 32public class ConnectStats { 33 private final static int EALREADY = OsConstants.EALREADY; 34 private final static int EINPROGRESS = OsConstants.EINPROGRESS; 35 36 /** How many events resulted in a given errno. */ 37 private final SparseIntArray mErrnos = new SparseIntArray(); 38 /** Latencies of blocking connects. TODO: add non-blocking connects latencies. */ 39 private final IntArray mLatencies = new IntArray(); 40 /** TokenBucket for rate limiting latency recording. */ 41 private final TokenBucket mLatencyTb; 42 /** Maximum number of latency values recorded. */ 43 private final int mMaxLatencyRecords; 44 /** Total count of successful connects. */ 45 private int mConnectCount = 0; 46 /** Total count of successful connects with IPv6 socket address. */ 47 private int mIpv6ConnectCount = 0; 48 49 public ConnectStats(TokenBucket tb, int maxLatencyRecords) { 50 mLatencyTb = tb; 51 mMaxLatencyRecords = maxLatencyRecords; 52 } 53 54 public ConnectStatistics toProto() { 55 ConnectStatistics stats = new ConnectStatistics(); 56 stats.connectCount = mConnectCount; 57 stats.ipv6AddrCount = mIpv6ConnectCount; 58 stats.latenciesMs = mLatencies.toArray(); 59 stats.errnosCounters = toPairArrays(mErrnos); 60 return stats; 61 } 62 63 public void addEvent(int errno, int latencyMs, String ipAddr) { 64 if (isSuccess(errno)) { 65 countConnect(ipAddr); 66 countLatency(errno, latencyMs); 67 } else { 68 countError(errno); 69 } 70 } 71 72 private void countConnect(String ipAddr) { 73 mConnectCount++; 74 if (isIPv6(ipAddr)) mIpv6ConnectCount++; 75 } 76 77 private void countLatency(int errno, int ms) { 78 if (isNonBlocking(errno)) { 79 // Ignore connect() on non-blocking sockets 80 return; 81 } 82 if (!mLatencyTb.get()) { 83 // Rate limited 84 return; 85 } 86 if (mLatencies.size() >= mMaxLatencyRecords) { 87 // Hard limit the total number of latency measurements. 88 return; 89 } 90 mLatencies.add(ms); 91 } 92 93 private void countError(int errno) { 94 final int newcount = mErrnos.get(errno, 0) + 1; 95 mErrnos.put(errno, newcount); 96 } 97 98 private static boolean isSuccess(int errno) { 99 return (errno == 0) || isNonBlocking(errno); 100 } 101 102 private static boolean isNonBlocking(int errno) { 103 // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS. 104 // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY. 105 return (errno == EINPROGRESS) || (errno == EALREADY); 106 } 107 108 private static boolean isIPv6(String ipAddr) { 109 return ipAddr.contains(":"); 110 } 111 112 private static Pair[] toPairArrays(SparseIntArray counts) { 113 final int s = counts.size(); 114 Pair[] pairs = new Pair[s]; 115 for (int i = 0; i < s; i++) { 116 Pair p = new Pair(); 117 p.key = counts.keyAt(i); 118 p.value = counts.valueAt(i); 119 pairs[i] = p; 120 } 121 return pairs; 122 } 123} 124