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 android.util.imetracing; 18 19 import static android.os.Build.IS_USER; 20 21 import android.annotation.Nullable; 22 import android.inputmethodservice.AbstractInputMethodService; 23 import android.os.RemoteException; 24 import android.os.ServiceManager.ServiceNotFoundException; 25 import android.util.Log; 26 import android.util.proto.ProtoOutputStream; 27 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceFileProto; 28 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodManagerServiceTraceFileProto; 29 import android.view.inputmethod.InputMethodEditorTraceProto.InputMethodServiceTraceFileProto; 30 import android.view.inputmethod.InputMethodManager; 31 32 import com.android.internal.annotations.GuardedBy; 33 import com.android.internal.util.TraceBuffer; 34 35 import java.io.File; 36 import java.io.IOException; 37 import java.io.PrintWriter; 38 39 /** 40 * @hide 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() throws ServiceNotFoundException { 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, ProtoOutputStream icProto)109 public void triggerClientDump(String where, InputMethodManager immInstance, 110 ProtoOutputStream icProto) { 111 // Intentionally left empty, this is implemented in ImeTracingClientImpl 112 } 113 114 @Override triggerServiceDump(String where, AbstractInputMethodService service, ProtoOutputStream icProto)115 public void triggerServiceDump(String where, AbstractInputMethodService service, 116 ProtoOutputStream icProto) { 117 // Intentionally left empty, this is implemented in ImeTracingClientImpl 118 } 119 120 @Override triggerManagerServiceDump(String where)121 public void triggerManagerServiceDump(String where) { 122 if (!isEnabled() || !isAvailable()) { 123 return; 124 } 125 126 synchronized (mDumpInProgressLock) { 127 if (mDumpInProgress) { 128 return; 129 } 130 mDumpInProgress = true; 131 } 132 133 try { 134 sendToService(null, IME_TRACING_FROM_IMMS, where); 135 } catch (RemoteException e) { 136 Log.e(TAG, "Exception while sending ime-related manager service dump to server", e); 137 } finally { 138 mDumpInProgress = false; 139 } 140 } 141 writeTracesToFilesLocked()142 private void writeTracesToFilesLocked() { 143 try { 144 ProtoOutputStream clientsProto = new ProtoOutputStream(); 145 clientsProto.write(InputMethodClientsTraceFileProto.MAGIC_NUMBER, 146 MAGIC_NUMBER_CLIENTS_VALUE); 147 mBufferClients.writeTraceToFile(mTraceFileClients, clientsProto); 148 149 ProtoOutputStream imsProto = new ProtoOutputStream(); 150 imsProto.write(InputMethodServiceTraceFileProto.MAGIC_NUMBER, MAGIC_NUMBER_IMS_VALUE); 151 mBufferIms.writeTraceToFile(mTraceFileIms, imsProto); 152 153 ProtoOutputStream immsProto = new ProtoOutputStream(); 154 immsProto.write(InputMethodManagerServiceTraceFileProto.MAGIC_NUMBER, 155 MAGIC_NUMBER_IMMS_VALUE); 156 mBufferImms.writeTraceToFile(mTraceFileImms, immsProto); 157 158 resetBuffers(); 159 } catch (IOException e) { 160 Log.e(TAG, "Unable to write buffer to file", e); 161 } 162 } 163 164 @GuardedBy("mEnabledLock") 165 @Override startTrace(@ullable PrintWriter pw)166 public void startTrace(@Nullable PrintWriter pw) { 167 if (IS_USER) { 168 Log.w(TAG, "Warn: Tracing is not supported on user builds."); 169 return; 170 } 171 172 synchronized (mEnabledLock) { 173 if (isAvailable() && isEnabled()) { 174 Log.w(TAG, "Warn: Tracing is already started."); 175 return; 176 } 177 178 logAndPrintln(pw, "Starting tracing in " + TRACE_DIRNAME + ": " + TRACE_FILENAME_CLIENTS 179 + ", " + TRACE_FILENAME_IMS + ", " + TRACE_FILENAME_IMMS); 180 sEnabled = true; 181 resetBuffers(); 182 } 183 } 184 185 @Override stopTrace(@ullable PrintWriter pw)186 public void stopTrace(@Nullable PrintWriter pw) { 187 if (IS_USER) { 188 Log.w(TAG, "Warn: Tracing is not supported on user builds."); 189 return; 190 } 191 192 synchronized (mEnabledLock) { 193 if (!isAvailable() || !isEnabled()) { 194 Log.w(TAG, "Warn: Tracing is not available or not started."); 195 return; 196 } 197 198 logAndPrintln(pw, "Stopping tracing and writing traces in " + TRACE_DIRNAME + ": " 199 + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", " 200 + TRACE_FILENAME_IMMS); 201 sEnabled = false; 202 writeTracesToFilesLocked(); 203 } 204 } 205 206 /** 207 * {@inheritDoc} 208 */ 209 @Override saveForBugreport(@ullable PrintWriter pw)210 public void saveForBugreport(@Nullable PrintWriter pw) { 211 if (IS_USER) { 212 return; 213 } 214 synchronized (mEnabledLock) { 215 if (!isAvailable() || !isEnabled()) { 216 return; 217 } 218 // Temporarily stop accepting logs from trace event providers. There is a small chance 219 // that we may drop some trace events while writing the file, but we currently need to 220 // live with that. Note that addToBuffer() also has a bug that it doesn't do 221 // read-acquire so flipping sEnabled here doesn't even guarantee that addToBuffer() will 222 // temporarily stop accepting incoming events... 223 // TODO(b/175761228): Implement atomic snapshot to avoid downtime. 224 // TODO(b/175761228): Fix synchronization around sEnabled. 225 sEnabled = false; 226 logAndPrintln(pw, "Writing traces in " + TRACE_DIRNAME + ": " 227 + TRACE_FILENAME_CLIENTS + ", " + TRACE_FILENAME_IMS + ", " 228 + TRACE_FILENAME_IMMS); 229 writeTracesToFilesLocked(); 230 sEnabled = true; 231 } 232 } 233 resetBuffers()234 private void resetBuffers() { 235 mBufferClients.resetBuffer(); 236 mBufferIms.resetBuffer(); 237 mBufferImms.resetBuffer(); 238 } 239 } 240