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