• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.wifi;
18 
19 
20 import android.os.Handler;
21 import android.util.ArrayMap;
22 
23 import com.android.internal.annotations.VisibleForTesting;
24 import com.android.server.wifi.util.FileUtils;
25 
26 import java.io.File;
27 import java.io.FileInputStream;
28 import java.io.IOException;
29 import java.io.PrintWriter;
30 import java.nio.file.Files;
31 import java.nio.file.Paths;
32 import java.util.Map;
33 
34 /**
35  * Provides a facility for capturing kernel trace events related to Wifi control and data paths.
36  */
37 public class LastMileLogger {
38     private final Handler mBackgroundHandler;
LastMileLogger(WifiInjector injector, Handler handler)39     public LastMileLogger(WifiInjector injector, Handler handler) {
40         mBackgroundHandler = handler;
41         File tracefsEnablePath = new File(WIFI_EVENT_ENABLE_PATH);
42         if (tracefsEnablePath.exists()) {
43             initLastMileLogger(injector, WIFI_EVENT_BUFFER_PATH, WIFI_EVENT_ENABLE_PATH,
44                     WIFI_EVENT_RELEASE_PATH);
45         } else {
46             initLastMileLogger(injector, WIFI_EVENT_BUFFER_PATH_DEBUGFS,
47                     WIFI_EVENT_ENABLE_PATH_DEBUGFS, WIFI_EVENT_RELEASE_PATH_DEBUGFS);
48         }
49     }
50 
51     @VisibleForTesting
LastMileLogger(WifiInjector injector, String bufferPath, String enablePath, String releasePath, Handler handler)52     public LastMileLogger(WifiInjector injector, String bufferPath, String enablePath,
53                           String releasePath, Handler handler) {
54         mBackgroundHandler = handler;
55         initLastMileLogger(injector, bufferPath, enablePath, releasePath);
56     }
57 
58     /**
59      * Informs LastMileLogger that a connection event has occurred.
60      * @param event an event defined in WifiDiagnostics
61      */
reportConnectionEvent(String ifaceName, byte event)62     public void reportConnectionEvent(String ifaceName, byte event) {
63         boolean wasTracingEnabled = anyConnectionInProgress();
64 
65         mIfaceToConnectionStatus.put(ifaceName, event);
66 
67         boolean shouldTracingBeEnabled = anyConnectionInProgress();
68 
69         mBackgroundHandler.post(() -> {
70             if (!wasTracingEnabled && shouldTracingBeEnabled) {
71                 enableTracing();
72             } else if (wasTracingEnabled && !shouldTracingBeEnabled) {
73                 disableTracing();
74             }
75             if (event == WifiDiagnostics.CONNECTION_EVENT_FAILED
76                     || event == WifiDiagnostics.CONNECTION_EVENT_TIMEOUT) {
77                 mLastMileLogForLastFailure = readTrace();
78             }
79         });
80     }
81 
anyConnectionInProgress()82     private boolean anyConnectionInProgress() {
83         for (byte status : mIfaceToConnectionStatus.values()) {
84             if (status == WifiDiagnostics.CONNECTION_EVENT_STARTED) {
85                 return true;
86             }
87         }
88         return false;
89     }
90 
91     /**
92      * Dumps the contents of the log.
93      * @param pw the PrintWriter that will receive the dump
94      */
dump(PrintWriter pw)95     public void dump(PrintWriter pw) {
96         dumpInternal(pw, "Last failed last-mile log", mLastMileLogForLastFailure);
97         dumpInternal(pw, "Latest last-mile log", readTrace());
98     }
99 
100     private static final String TAG = "LastMileLogger";
101     private static final String WIFI_EVENT_BUFFER_PATH =
102             "/sys/kernel/tracing/instances/wifi/trace";
103     private static final String WIFI_EVENT_ENABLE_PATH =
104             "/sys/kernel/tracing/instances/wifi/tracing_on";
105     private static final String WIFI_EVENT_RELEASE_PATH =
106             "/sys/kernel/tracing/instances/wifi/free_buffer";
107     private static final String WIFI_EVENT_BUFFER_PATH_DEBUGFS =
108             "/sys/kernel/debug/tracing/instances/wifi/trace";
109     private static final String WIFI_EVENT_ENABLE_PATH_DEBUGFS =
110             "/sys/kernel/debug/tracing/instances/wifi/tracing_on";
111     private static final String WIFI_EVENT_RELEASE_PATH_DEBUGFS =
112             "/sys/kernel/debug/tracing/instances/wifi/free_buffer";
113 
114     private String mEventBufferPath;
115     private String mEventEnablePath;
116     private String mEventReleasePath;
117     private WifiLog mLog;
118     private byte[] mLastMileLogForLastFailure;
119     private FileInputStream mLastMileTraceHandle;
120     /**
121      * String key: iface name
122      * byte value: Connection status, one of WifiDiagnostics.CONNECTION_EVENT_*
123      */
124     private final Map<String, Byte> mIfaceToConnectionStatus = new ArrayMap<>();
125 
initLastMileLogger(WifiInjector injector, String bufferPath, String enablePath, String releasePath)126     private void initLastMileLogger(WifiInjector injector, String bufferPath, String enablePath,
127                           String releasePath) {
128         mLog = injector.makeLog(TAG);
129         mEventBufferPath = bufferPath;
130         mEventEnablePath = enablePath;
131         mEventReleasePath = releasePath;
132     }
133 
enableTracing()134     private void enableTracing() {
135         if (!ensureFailSafeIsArmed()) {
136             mLog.wC("Failed to arm fail-safe.");
137             return;
138         }
139 
140         try {
141             FileUtils.stringToFile(mEventEnablePath, "1");
142         } catch (IOException e) {
143             mLog.warn("Failed to start event tracing: %").r(e.getMessage()).flush();
144         }
145     }
146 
disableTracing()147     private void disableTracing() {
148         try {
149             FileUtils.stringToFile(mEventEnablePath, "0");
150         } catch (IOException e) {
151             mLog.warn("Failed to stop event tracing: %").r(e.getMessage()).flush();
152         }
153     }
154 
readTrace()155     private byte[] readTrace() {
156         try {
157             return Files.readAllBytes(Paths.get(mEventBufferPath));
158         } catch (IOException e) {
159             mLog.warn("Failed to read event trace: %").r(e.getMessage()).flush();
160             return new byte[0];
161         }
162     }
163 
ensureFailSafeIsArmed()164     private boolean ensureFailSafeIsArmed() {
165         if (mLastMileTraceHandle != null) {
166             return true;
167         }
168 
169         try {
170             // This file provides fail-safe behavior for Last-Mile logging. Given that we:
171             // 1. Set the disable_on_free option in the trace_options pseudo-file
172             //    (see wifi-events.rc), and
173             // 2. Hold the WIFI_EVENT_RELEASE_PATH open,
174             //
175             // Then, when this process dies, the kernel will automatically disable any
176             // tracing in the wifi trace instance.
177             //
178             // Note that, despite Studio's suggestion that |mLastMileTraceHandle| could be demoted
179             // to a local variable, we need to stick with a field. Otherwise, the handle could be
180             // garbage collected.
181             mLastMileTraceHandle = new FileInputStream(mEventReleasePath);
182             return true;
183         } catch (IOException e) {
184             mLog.warn("Failed to open free_buffer pseudo-file: %").r(e.getMessage()).flush();
185             return false;
186         }
187     }
188 
dumpInternal(PrintWriter pw, String description, byte[] lastMileLog)189     private static void dumpInternal(PrintWriter pw, String description, byte[] lastMileLog) {
190         if (lastMileLog == null || lastMileLog.length < 1) {
191             pw.format("No last mile log for \"%s\"\n", description);
192             return;
193         }
194 
195         pw.format("-------------------------- %s ---------------------------\n", description);
196         pw.print(new String(lastMileLog));
197         pw.println("--------------------------------------------------------------------");
198     }
199 }
200