1 /* 2 * Copyright (C) 2020 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.internal.inputmethod; 18 19 import static android.os.Build.IS_USER; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.os.SystemClock; 24 import android.util.Log; 25 import android.util.proto.ProtoOutputStream; 26 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto; 27 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodManagerServiceTraceFileProto; 28 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceFileProto; 29 import android.view.inputmethod.InputMethodManager; 30 31 import com.android.internal.annotations.GuardedBy; 32 import com.android.internal.util.TraceBuffer; 33 34 import java.io.File; 35 import java.io.IOException; 36 import java.io.PrintWriter; 37 import java.util.concurrent.TimeUnit; 38 39 /** 40 * An implementation of {@link ImeTracing} for the system_server process. 41 */ 42 class ImeTracingServerImpl extends ImeTracing { 43 private static final String TRACE_DIRNAME = "/data/misc/wmtrace/"; 44 private static final String TRACE_FILENAME_CLIENTS = "ime_trace_clients.winscope"; 45 private static final String TRACE_FILENAME_IMS = "ime_trace_service.winscope"; 46 private static final String TRACE_FILENAME_IMMS = "ime_trace_managerservice.winscope"; 47 private static final int BUFFER_CAPACITY = 4096 * 1024; 48 49 // Needed for winscope to auto-detect the dump type. Explained further in 50 // core.proto.android.view.inputmethod.inputmethodeditortrace.proto. 51 // This magic number corresponds to InputMethodClientsTraceFileProto. 52 private static final long MAGIC_NUMBER_CLIENTS_VALUE = 53 ((long) InputMethodClientsTraceFileProto.MAGIC_NUMBER_H << 32) 54 | InputMethodClientsTraceFileProto.MAGIC_NUMBER_L; 55 // This magic number corresponds to InputMethodServiceTraceFileProto. 56 private static final long MAGIC_NUMBER_IMS_VALUE = 57 ((long) InputMethodServiceTraceFileProto.MAGIC_NUMBER_H << 32) 58 | InputMethodServiceTraceFileProto.MAGIC_NUMBER_L; 59 // This magic number corresponds to InputMethodManagerServiceTraceFileProto. 60 private static final long MAGIC_NUMBER_IMMS_VALUE = 61 ((long) InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_H << 32) 62 | InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER_L; 63 64 private final TraceBuffer mBufferClients; 65 private final File mTraceFileClients; 66 private final TraceBuffer mBufferIms; 67 private final File mTraceFileIms; 68 private final TraceBuffer mBufferImms; 69 private final File mTraceFileImms; 70 71 private final Object mEnabledLock = new Object(); 72 ImeTracingServerImpl()73 ImeTracingServerImpl() { 74 mBufferClients = new TraceBuffer<>(BUFFER_CAPACITY); 75 mTraceFileClients = new File(TRACE_DIRNAME + TRACE_FILENAME_CLIENTS); 76 mBufferIms = new TraceBuffer<>(BUFFER_CAPACITY); 77 mTraceFileIms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMS); 78 mBufferImms = new TraceBuffer<>(BUFFER_CAPACITY); 79 mTraceFileImms = new File(TRACE_DIRNAME + TRACE_FILENAME_IMMS); 80 } 81 82 /** 83 * The provided dump is added to the corresponding dump buffer: 84 * {@link ImeTracingServerImpl#mBufferClients} or {@link ImeTracingServerImpl#mBufferIms}. 85 * 86 * @param proto dump to be added to the buffer 87 */ 88 @Override addToBuffer(ProtoOutputStream proto, int source)89 public void addToBuffer(ProtoOutputStream proto, int source) { 90 if (isAvailable() && isEnabled()) { 91 switch (source) { 92 case IME_TRACING_FROM_CLIENT: 93 mBufferClients.add(proto); 94 return; 95 case IME_TRACING_FROM_IMS: 96 mBufferIms.add(proto); 97 return; 98 case IME_TRACING_FROM_IMMS: 99 mBufferImms.add(proto); 100 return; 101 default: 102 // Source not recognised. 103 Log.w(TAG, "Request to add to buffer, but source not recognised."); 104 } 105 } 106 } 107 108 @Override triggerClientDump(String where, InputMethodManager immInstance, @Nullable byte[] icProto)109 public void triggerClientDump(String where, InputMethodManager immInstance, 110 @Nullable byte[] icProto) { 111 // Intentionally left empty, this is implemented in ImeTracingClientImpl 112 } 113 114 @Override triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto)115 public void triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto) { 116 // Intentionally left empty, this is implemented in ImeTracingClientImpl 117 } 118 119 @Override triggerManagerServiceDump(String where, @NonNull ServiceDumper dumper)120 public void triggerManagerServiceDump(String where, @NonNull ServiceDumper dumper) { 121 if (!isEnabled() || !isAvailable()) { 122 return; 123 } 124 125 synchronized (mDumpInProgressLock) { 126 if (mDumpInProgress) { 127 return; 128 } 129 mDumpInProgress = true; 130 } 131 132 try { 133 sendToService(null, IME_TRACING_FROM_IMMS, where); 134 } finally { 135 mDumpInProgress = false; 136 } 137 } 138 writeTracesToFilesLocked()139 private void writeTracesToFilesLocked() { 140 try { 141 long timeOffsetNs = 142 TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()) 143 - SystemClock.elapsedRealtimeNanos(); 144 145 ProtoOutputStream clientsProto = new ProtoOutputStream(); 146 clientsProto.write(InputMethodClientsTraceFileProto.MAGIC_NUMBER, 147 MAGIC_NUMBER_CLIENTS_VALUE); 148 clientsProto.write(InputMethodClientsTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS, 149 timeOffsetNs); 150 mBufferClients.writeTraceToFile(mTraceFileClients, clientsProto); 151 152 ProtoOutputStream imsProto = new ProtoOutputStream(); 153 imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER, 154 MAGIC_NUMBER_IMS_VALUE); 155 imsProto.write(InputMethodServiceTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS, 156 timeOffsetNs); 157 mBufferIms.writeTraceToFile(mTraceFileIms, imsProto); 158 159 ProtoOutputStream immsProto = new ProtoOutputStream(); 160 immsProto.write(InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER, 161 MAGIC_NUMBER_IMMS_VALUE); 162 immsProto.write( 163 InputMethodManagerServiceTraceFileProto.REAL_TO_ELAPSED_TIME_OFFSET_NANOS, 164 timeOffsetNs); 165 mBufferImms.writeTraceToFile(mTraceFileImms, immsProto); 166 167 resetBuffers(); 168 } catch (IOException e) { 169 Log.e(TAG, "Unable to write buffer to file", e); 170 } 171 } 172 173 @GuardedBy("mEnabledLock") 174 @Override startTrace(@ullable PrintWriter pw)175 public void startTrace(@Nullable PrintWriter pw) { 176 if (IS_USER) { 177 Log.w(TAG, "Warn: Tracing is not supported on user builds."); 178 return; 179 } 180 181 synchronized (mEnabledLock) { 182 if (isAvailable() && isEnabled()) { 183 Log.w(TAG, "Warn: Tracing is already started."); 184 return; 185 } 186 187 logAndPrintln(pw, "Starting tracing in " + TRACE_DIRNAME + ": " + TRACE_FILENAME_CLIENTS 188 + ", " + TRACE_FILENAME_IMS + ", " + TRACE_FILENAME_IMMS); 189 sEnabled = true; 190 resetBuffers(); 191 } 192 } 193 194 @Override stopTrace(@ullable PrintWriter pw)195 public void stopTrace(@Nullable PrintWriter pw) { 196 if (IS_USER) { 197 Log.w(TAG, "Warn: Tracing is not supported on user builds."); 198 return; 199 } 200 201 synchronized (mEnabledLock) { 202 if (!isAvailable() || !isEnabled()) { 203 Log.w(TAG, "Warn: Tracing is not available or not started."); 204 return; 205 } 206 207 logAndPrintln(pw, "Stopping tracing and writing traces in " + TRACE_DIRNAME + ": " 208 + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", " 209 + TRACE_FILENAME_IMMS); 210 sEnabled = false; 211 writeTracesToFilesLocked(); 212 } 213 } 214 215 /** 216 * {@inheritDoc} 217 */ 218 @Override saveForBugreport(@ullable PrintWriter pw)219 public void saveForBugreport(@Nullable PrintWriter pw) { 220 if (IS_USER) { 221 return; 222 } 223 synchronized (mEnabledLock) { 224 if (!isAvailable() || !isEnabled()) { 225 return; 226 } 227 // Temporarily stop accepting logs from trace event providers. There is a small chance 228 // that we may drop some trace events while writing the file, but we currently need to 229 // live with that. Note that addToBuffer() also has a bug that it doesn't do 230 // read-acquire so flipping sEnabled here doesn't even guarantee that addToBuffer() will 231 // temporarily stop accepting incoming events... 232 // TODO(b/175761228): Implement atomic snapshot to avoid downtime. 233 // TODO(b/175761228): Fix synchronization around sEnabled. 234 sEnabled = false; 235 logAndPrintln(pw, "Writing traces in " + TRACE_DIRNAME + ": " 236 + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", " 237 + TRACE_FILENAME_IMMS); 238 writeTracesToFilesLocked(); 239 sEnabled = true; 240 } 241 } 242 resetBuffers()243 private void resetBuffers() { 244 mBufferClients.resetBuffer(); 245 mBufferIms.resetBuffer(); 246 mBufferImms.resetBuffer(); 247 } 248 } 249