10d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi/*
20d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * Copyright (C) 2016 The Android Open Source Project
30d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi *
40d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * Licensed under the Apache License, Version 2.0 (the "License");
50d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * you may not use this file except in compliance with the License.
60d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * You may obtain a copy of the License at
70d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi *
80d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi *      http://www.apache.org/licenses/LICENSE-2.0
90d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi *
100d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * Unless required by applicable law or agreed to in writing, software
110d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * distributed under the License is distributed on an "AS IS" BASIS,
120d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * See the License for the specific language governing permissions and
140d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * limitations under the License.
150d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi */
160d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
170d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichipackage android.net.metrics;
180d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
195eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichiimport android.net.NetworkCapabilities;
200d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichiimport android.system.OsConstants;
210d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichiimport android.util.IntArray;
220d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichiimport android.util.SparseIntArray;
23ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi
245eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichiimport com.android.internal.util.BitUtils;
250d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichiimport com.android.internal.util.TokenBucket;
260d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
270d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi/**
285eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi * A class that aggregates connect() statistics.
290d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi * {@hide}
300d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi */
310d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichipublic class ConnectStats {
320d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private final static int EALREADY     = OsConstants.EALREADY;
330d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private final static int EINPROGRESS  = OsConstants.EINPROGRESS;
340d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
355eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    /** Network id of the network associated with the event, or 0 if unspecified. */
365eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final int netId;
375eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    /** Transports of the network associated with the event, as defined in NetworkCapabilities. */
385eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final long transports;
390d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    /** How many events resulted in a given errno. */
405eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final SparseIntArray errnos = new SparseIntArray();
415eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    /** Latencies of successful blocking connects. TODO: add non-blocking connects latencies. */
425eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final IntArray latencies = new IntArray();
430d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    /** TokenBucket for rate limiting latency recording. */
445eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final TokenBucket mLatencyTb;
450d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    /** Maximum number of latency values recorded. */
465eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public final int mMaxLatencyRecords;
47ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi    /** Total count of events */
48ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi    public int eventCount = 0;
490d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    /** Total count of successful connects. */
505eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public int connectCount = 0;
51a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi    /** Total count of successful connects done in blocking mode. */
525eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public int connectBlockingCount = 0;
530d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    /** Total count of successful connects with IPv6 socket address. */
545eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public int ipv6ConnectCount = 0;
550d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
565eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi    public ConnectStats(int netId, long transports, TokenBucket tb, int maxLatencyRecords) {
575eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        this.netId = netId;
585eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        this.transports = transports;
590d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        mLatencyTb = tb;
600d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        mMaxLatencyRecords = maxLatencyRecords;
610d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
620d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
63ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi    boolean addEvent(int errno, int latencyMs, String ipAddr) {
64ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi        eventCount++;
650d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        if (isSuccess(errno)) {
66a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi            countConnect(errno, ipAddr);
670d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            countLatency(errno, latencyMs);
68ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi            return true;
690d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        } else {
700d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            countError(errno);
71ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi            return false;
720d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        }
730d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
740d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
75a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi    private void countConnect(int errno, String ipAddr) {
765eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        connectCount++;
77a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        if (!isNonBlocking(errno)) {
785eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi            connectBlockingCount++;
79a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        }
80a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        if (isIPv6(ipAddr)) {
815eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi            ipv6ConnectCount++;
82a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        }
830d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
840d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
850d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private void countLatency(int errno, int ms) {
860d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        if (isNonBlocking(errno)) {
870d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            // Ignore connect() on non-blocking sockets
880d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            return;
890d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        }
900d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        if (!mLatencyTb.get()) {
910d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            // Rate limited
920d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            return;
930d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        }
945eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        if (latencies.size() >= mMaxLatencyRecords) {
950d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            // Hard limit the total number of latency measurements.
960d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi            return;
970d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        }
985eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        latencies.add(ms);
990d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
1000d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
1010d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private void countError(int errno) {
1025eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        final int newcount = errnos.get(errno, 0) + 1;
1035eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        errnos.put(errno, newcount);
1040d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
1050d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
1060d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private static boolean isSuccess(int errno) {
1070d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        return (errno == 0) || isNonBlocking(errno);
1080d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
1090d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
110ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi    static boolean isNonBlocking(int errno) {
1110d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        // On non-blocking TCP sockets, connect() immediately returns EINPROGRESS.
1120d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        // On non-blocking TCP sockets that are connecting, connect() immediately returns EALREADY.
1130d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        return (errno == EINPROGRESS) || (errno == EALREADY);
1140d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
1150d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
1160d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    private static boolean isIPv6(String ipAddr) {
1170d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi        return ipAddr.contains(":");
1180d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi    }
1190d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi
120a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi    @Override
121a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi    public String toString() {
122c867f787bc7b6ecd49fe3a9c638b34befc3922ecHugo Benichi        StringBuilder builder =
123c867f787bc7b6ecd49fe3a9c638b34befc3922ecHugo Benichi                new StringBuilder("ConnectStats(").append("netId=").append(netId).append(", ");
1245eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        for (int t : BitUtils.unpackBits(transports)) {
1255eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi            builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
1265eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        }
127ab20975c7485a0451d77e19d48ab8fe09e483927Hugo Benichi        builder.append(String.format("%d events, ", eventCount));
1285eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        builder.append(String.format("%d success, ", connectCount));
1295eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        builder.append(String.format("%d blocking, ", connectBlockingCount));
1305eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        builder.append(String.format("%d IPv6 dst", ipv6ConnectCount));
1315eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi        for (int i = 0; i < errnos.size(); i++) {
1325eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi            String errno = OsConstants.errnoName(errnos.keyAt(i));
1335eb9053c4e8c788b83cfcfd7bfd39f628dd4ca45Hugo Benichi            int count = errnos.valueAt(i);
134a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi            builder.append(String.format(", %s: %d", errno, count));
135a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        }
136a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi        return builder.append(")").toString();
137a2decca4f25b6d3adfd396cdc475807985783babHugo Benichi    }
1380d4a398b7846f26e4452180915ecb5a9d4566148Hugo Benichi}
139