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 DefaultNetworkMetrics()63 public DefaultNetworkMetrics() { 64 newDefaultNetwork(creationTimeMs, null); 65 } 66 listEvents(PrintWriter pw)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 /** 85 * Convert events in the ring buffer to a list of IpConnectivityEvent protos 86 */ listEventsAsProto()87 public synchronized List<IpConnectivityEvent> listEventsAsProto() { 88 List<IpConnectivityEvent> list = new ArrayList<>(); 89 for (DefaultNetworkEvent ev : mEventsLog.toArray()) { 90 list.add(IpConnectivityEventBuilder.toProto(ev)); 91 } 92 return list; 93 } 94 flushEvents(List<IpConnectivityEvent> out)95 public synchronized void flushEvents(List<IpConnectivityEvent> out) { 96 for (DefaultNetworkEvent ev : mEvents) { 97 out.add(IpConnectivityEventBuilder.toProto(ev)); 98 } 99 mEvents.clear(); 100 } 101 logDefaultNetworkValidity(long timeMs, boolean isValid)102 public synchronized void logDefaultNetworkValidity(long timeMs, boolean isValid) { 103 // Transition from valid to invalid: update validity duration since last update 104 if (!isValid && mIsCurrentlyValid) { 105 mIsCurrentlyValid = false; 106 updateValidationTime(timeMs); 107 } 108 109 // Transition from invalid to valid: simply mark the validation timestamp. 110 if (isValid && !mIsCurrentlyValid) { 111 mIsCurrentlyValid = true; 112 mLastValidationTimeMs = timeMs; 113 } 114 } 115 updateValidationTime(long timeMs)116 private void updateValidationTime(long timeMs) { 117 mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs; 118 } 119 logDefaultNetworkEvent( long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai)120 public synchronized void logDefaultNetworkEvent( 121 long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) { 122 logCurrentDefaultNetwork(timeMs, oldNai); 123 newDefaultNetwork(timeMs, newNai); 124 } 125 logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai)126 private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) { 127 if (mIsCurrentlyValid) { 128 updateValidationTime(timeMs); 129 } 130 DefaultNetworkEvent ev = mCurrentDefaultNetwork; 131 ev.updateDuration(timeMs); 132 ev.previousTransports = mLastTransports; 133 // oldNai is null if the system had no default network before the transition. 134 if (oldNai != null) { 135 // The system acquired a new default network. 136 fillLinkInfo(ev, oldNai); 137 ev.finalScore = oldNai.getCurrentScore(); 138 } 139 // Only change transport of the previous default network if the event currently logged 140 // corresponds to an existing default network, and not to the absence of a default network. 141 // This allows to log pairs of transports for successive default networks regardless of 142 // whether or not the system experienced a period without any default network. 143 if (ev.transports != 0) { 144 mLastTransports = ev.transports; 145 } 146 mEvents.add(ev); 147 mEventsLog.append(ev); 148 } 149 newDefaultNetwork(long timeMs, NetworkAgentInfo newNai)150 private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) { 151 DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs); 152 ev.durationMs = timeMs; 153 // newNai is null if the system has no default network after the transition. 154 if (newNai != null) { 155 fillLinkInfo(ev, newNai); 156 ev.initialScore = newNai.getCurrentScore(); 157 if (newNai.lastValidated) { 158 mIsCurrentlyValid = true; 159 mLastValidationTimeMs = timeMs; 160 } 161 } else { 162 mIsCurrentlyValid = false; 163 } 164 mCurrentDefaultNetwork = ev; 165 } 166 fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai)167 private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) { 168 LinkProperties lp = nai.linkProperties; 169 ev.netId = nai.network().netId; 170 ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes()); 171 ev.ipv4 |= lp.hasIpv4Address() && lp.hasIpv4DefaultRoute(); 172 ev.ipv6 |= lp.hasGlobalIpv6Address() && lp.hasIpv6DefaultRoute(); 173 } 174 printEvent(long localTimeMs, PrintWriter pw, DefaultNetworkEvent ev)175 private static void printEvent(long localTimeMs, PrintWriter pw, DefaultNetworkEvent ev) { 176 long localCreationTimeMs = localTimeMs - ev.durationMs; 177 pw.println(String.format("%tT.%tL: %s", localCreationTimeMs, localCreationTimeMs, ev)); 178 } 179 } 180