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