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