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 
listEventsAsProto(PrintWriter pw)84     public synchronized void listEventsAsProto(PrintWriter pw) {
85         for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
86             pw.print(IpConnectivityEventBuilder.toProto(ev));
87         }
88     }
89 
flushEvents(List<IpConnectivityEvent> out)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 
logDefaultNetworkValidity(long timeMs, boolean isValid)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 
updateValidationTime(long timeMs)111     private void updateValidationTime(long timeMs) {
112         mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs;
113     }
114 
logDefaultNetworkEvent( long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai)115     public synchronized void logDefaultNetworkEvent(
116             long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) {
117         logCurrentDefaultNetwork(timeMs, oldNai);
118         newDefaultNetwork(timeMs, newNai);
119     }
120 
logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai)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 
newDefaultNetwork(long timeMs, NetworkAgentInfo newNai)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 
fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai)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 
printEvent(long localTimeMs, PrintWriter pw, DefaultNetworkEvent ev)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