Home | History | Annotate | Download | only in connectivity
      1 /*
      2  * Copyright (C) 2017 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 
     17 package com.android.server.connectivity;
     18 
     19 import android.net.LinkProperties;
     20 import android.net.metrics.DefaultNetworkEvent;
     21 import android.os.SystemClock;
     22 
     23 import com.android.internal.annotations.GuardedBy;
     24 import com.android.internal.util.BitUtils;
     25 import com.android.internal.util.RingBuffer;
     26 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
     27 
     28 import java.io.PrintWriter;
     29 import java.util.ArrayList;
     30 import java.util.List;
     31 
     32 /**
     33  * Tracks events related to the default network for the purpose of default network metrics.
     34  * {@hide}
     35  */
     36 public class DefaultNetworkMetrics {
     37 
     38     private static final int ROLLING_LOG_SIZE = 64;
     39 
     40     public final long creationTimeMs = SystemClock.elapsedRealtime();
     41 
     42     // Event buffer used for metrics upload. The buffer is cleared when events are collected.
     43     @GuardedBy("this")
     44     private final List<DefaultNetworkEvent> mEvents = new ArrayList<>();
     45 
     46     // Rolling event buffer used for dumpsys and bugreports.
     47     @GuardedBy("this")
     48     private final RingBuffer<DefaultNetworkEvent> mEventsLog =
     49             new RingBuffer(DefaultNetworkEvent.class, ROLLING_LOG_SIZE);
     50 
     51     // Information about the current status of the default network.
     52     @GuardedBy("this")
     53     private DefaultNetworkEvent mCurrentDefaultNetwork;
     54     // True if the current default network has been validated.
     55     @GuardedBy("this")
     56     private boolean mIsCurrentlyValid;
     57     @GuardedBy("this")
     58     private long mLastValidationTimeMs;
     59     // Transport information about the last default network.
     60     @GuardedBy("this")
     61     private int mLastTransports;
     62 
     63     public DefaultNetworkMetrics() {
     64         newDefaultNetwork(creationTimeMs, null);
     65     }
     66 
     67     public synchronized void listEvents(PrintWriter pw) {
     68         pw.println("default network events:");
     69         long localTimeMs = System.currentTimeMillis();
     70         long timeMs = SystemClock.elapsedRealtime();
     71         for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
     72             printEvent(localTimeMs, pw, ev);
     73         }
     74         mCurrentDefaultNetwork.updateDuration(timeMs);
     75         // When printing default network events for bug reports, update validation time
     76         // and refresh the last validation timestmap for future validation time updates.
     77         if (mIsCurrentlyValid) {
     78             updateValidationTime(timeMs);
     79             mLastValidationTimeMs = timeMs;
     80         }
     81         printEvent(localTimeMs, pw, mCurrentDefaultNetwork);
     82     }
     83 
     84     public synchronized void listEventsAsProto(PrintWriter pw) {
     85         for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
     86             pw.print(IpConnectivityEventBuilder.toProto(ev));
     87         }
     88     }
     89 
     90     public synchronized void flushEvents(List<IpConnectivityEvent> out) {
     91         for (DefaultNetworkEvent ev : mEvents) {
     92             out.add(IpConnectivityEventBuilder.toProto(ev));
     93         }
     94         mEvents.clear();
     95     }
     96 
     97     public synchronized void logDefaultNetworkValidity(long timeMs, boolean isValid) {
     98         // Transition from valid to invalid: update validity duration since last update
     99         if (!isValid && mIsCurrentlyValid) {
    100             mIsCurrentlyValid = false;
    101             updateValidationTime(timeMs);
    102         }
    103 
    104         // Transition from invalid to valid: simply mark the validation timestamp.
    105         if (isValid && !mIsCurrentlyValid) {
    106             mIsCurrentlyValid = true;
    107             mLastValidationTimeMs = timeMs;
    108         }
    109     }
    110 
    111     private void updateValidationTime(long timeMs) {
    112         mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs;
    113     }
    114 
    115     public synchronized void logDefaultNetworkEvent(
    116             long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) {
    117         logCurrentDefaultNetwork(timeMs, oldNai);
    118         newDefaultNetwork(timeMs, newNai);
    119     }
    120 
    121     private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) {
    122         if (mIsCurrentlyValid) {
    123             updateValidationTime(timeMs);
    124         }
    125         DefaultNetworkEvent ev = mCurrentDefaultNetwork;
    126         ev.updateDuration(timeMs);
    127         ev.previousTransports = mLastTransports;
    128         // oldNai is null if the system had no default network before the transition.
    129         if (oldNai != null) {
    130             // The system acquired a new default network.
    131             fillLinkInfo(ev, oldNai);
    132             ev.finalScore = oldNai.getCurrentScore();
    133         }
    134         // Only change transport of the previous default network if the event currently logged
    135         // corresponds to an existing default network, and not to the absence of a default network.
    136         // This allows to log pairs of transports for successive default networks regardless of
    137         // whether or not the system experienced a period without any default network.
    138         if (ev.transports != 0) {
    139             mLastTransports = ev.transports;
    140         }
    141         mEvents.add(ev);
    142         mEventsLog.append(ev);
    143     }
    144 
    145     private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) {
    146         DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs);
    147         ev.durationMs = timeMs;
    148         // newNai is null if the system has no default network after the transition.
    149         if (newNai != null) {
    150             fillLinkInfo(ev, newNai);
    151             ev.initialScore = newNai.getCurrentScore();
    152             if (newNai.lastValidated) {
    153                 mIsCurrentlyValid = true;
    154                 mLastValidationTimeMs = timeMs;
    155             }
    156         } else {
    157             mIsCurrentlyValid = false;
    158         }
    159         mCurrentDefaultNetwork = ev;
    160     }
    161 
    162     private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) {
    163         LinkProperties lp = nai.linkProperties;
    164         ev.netId = nai.network().netId;
    165         ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes());
    166         ev.ipv4 |= lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
    167         ev.ipv6 |= lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
    168     }
    169 
    170     private static void printEvent(long localTimeMs, PrintWriter pw, DefaultNetworkEvent ev) {
    171         long localCreationTimeMs = localTimeMs - ev.durationMs;
    172         pw.println(String.format("%tT.%tL: %s", localCreationTimeMs, localCreationTimeMs, ev));
    173     }
    174 }
    175