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 17package com.android.server.wifi; 18 19import android.os.FileUtils; 20 21import com.android.internal.annotations.VisibleForTesting; 22 23import libcore.io.IoUtils; 24 25import java.io.FileInputStream; 26import java.io.IOException; 27import java.io.PrintWriter; 28 29/** 30 * Provides a facility for capturing kernel trace events related to Wifi control and data paths. 31 */ 32public class LastMileLogger { 33 public LastMileLogger(WifiInjector injector) { 34 this(injector, WIFI_EVENT_BUFFER_PATH, WIFI_EVENT_ENABLE_PATH, WIFI_EVENT_RELEASE_PATH); 35 } 36 37 @VisibleForTesting 38 public LastMileLogger(WifiInjector injector, String bufferPath, String enablePath, 39 String releasePath) { 40 mLog = injector.makeLog(TAG); 41 mEventBufferPath = bufferPath; 42 mEventEnablePath = enablePath; 43 mEventReleasePath = releasePath; 44 } 45 46 /** 47 * Informs LastMileLogger that a connection event has occurred. 48 * @param connectionId A non-negative connection identifier, or -1 to indicate unknown 49 * @param event an event defined in BaseWifiDiagnostics 50 */ 51 public void reportConnectionEvent(long connectionId, byte event) { 52 if (connectionId < 0) { 53 mLog.warn("Ignoring negative connection id: %").c(connectionId); 54 return; 55 } 56 57 switch (event) { 58 case BaseWifiDiagnostics.CONNECTION_EVENT_STARTED: 59 mPendingConnectionId = connectionId; 60 enableTracing(); 61 return; 62 case BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED: 63 mPendingConnectionId = -1; 64 disableTracing(); 65 return; 66 case BaseWifiDiagnostics.CONNECTION_EVENT_FAILED: 67 if (connectionId >= mPendingConnectionId) { 68 mPendingConnectionId = -1; 69 disableTracing(); 70 mLastMileLogForLastFailure = readTrace(); 71 return; 72 } else { 73 // Spurious failure message. Here's one scenario where this might happen: 74 // t=00sec start first connection attempt 75 // t=30sec start second connection attempt 76 // t=60sec timeout first connection attempt 77 // We should not stop tracing in this case, since the second connection attempt 78 // is still in progress. 79 return; 80 } 81 } 82 } 83 84 /** 85 * Dumps the contents of the log. 86 * @param pw the PrintWriter that will receive the dump 87 */ 88 public void dump(PrintWriter pw) { 89 dumpInternal(pw, "Last failed last-mile log", mLastMileLogForLastFailure); 90 dumpInternal(pw, "Latest last-mile log", readTrace()); 91 mLastMileLogForLastFailure = null; 92 } 93 94 private static final String TAG = "LastMileLogger"; 95 private static final String WIFI_EVENT_BUFFER_PATH = 96 "/sys/kernel/debug/tracing/instances/wifi/trace"; 97 private static final String WIFI_EVENT_ENABLE_PATH = 98 "/sys/kernel/debug/tracing/instances/wifi/tracing_on"; 99 private static final String WIFI_EVENT_RELEASE_PATH = 100 "/sys/kernel/debug/tracing/instances/wifi/free_buffer"; 101 102 private final String mEventBufferPath; 103 private final String mEventEnablePath; 104 private final String mEventReleasePath; 105 private WifiLog mLog; 106 private byte[] mLastMileLogForLastFailure; 107 private FileInputStream mLastMileTraceHandle; 108 private long mPendingConnectionId = -1; 109 110 private void enableTracing() { 111 if (!ensureFailSafeIsArmed()) { 112 mLog.wC("Failed to arm fail-safe."); 113 return; 114 } 115 116 try { 117 FileUtils.stringToFile(mEventEnablePath, "1"); 118 } catch (IOException e) { 119 mLog.warn("Failed to start event tracing: %").r(e.getMessage()).flush(); 120 } 121 } 122 123 private void disableTracing() { 124 try { 125 FileUtils.stringToFile(mEventEnablePath, "0"); 126 } catch (IOException e) { 127 mLog.warn("Failed to stop event tracing: %").r(e.getMessage()).flush(); 128 } 129 } 130 131 private byte[] readTrace() { 132 try { 133 return IoUtils.readFileAsByteArray(mEventBufferPath); 134 } catch (IOException e) { 135 mLog.warn("Failed to read event trace: %").r(e.getMessage()).flush(); 136 return new byte[0]; 137 } 138 } 139 140 private boolean ensureFailSafeIsArmed() { 141 if (mLastMileTraceHandle != null) { 142 return true; 143 } 144 145 try { 146 // This file provides fail-safe behavior for Last-Mile logging. Given that we: 147 // 1. Set the disable_on_free option in the trace_options pseudo-file 148 // (see wifi-events.rc), and 149 // 2. Hold the WIFI_EVENT_RELEASE_PATH open, 150 // 151 // Then, when this process dies, the kernel will automatically disable any 152 // tracing in the wifi trace instance. 153 // 154 // Note that, despite Studio's suggestion that |mLastMileTraceHandle| could be demoted 155 // to a local variable, we need to stick with a field. Otherwise, the handle could be 156 // garbage collected. 157 mLastMileTraceHandle = new FileInputStream(mEventReleasePath); 158 return true; 159 } catch (IOException e) { 160 mLog.warn("Failed to open free_buffer pseudo-file: %").r(e.getMessage()).flush(); 161 return false; 162 } 163 } 164 165 private static void dumpInternal(PrintWriter pw, String description, byte[] lastMileLog) { 166 if (lastMileLog == null || lastMileLog.length < 1) { 167 pw.format("No last mile log for \"%s\"\n", description); 168 return; 169 } 170 171 pw.format("-------------------------- %s ---------------------------\n", description); 172 pw.print(new String(lastMileLog)); 173 pw.println("--------------------------------------------------------------------"); 174 } 175} 176