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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.app.ActivityThread; 23 import android.util.Log; 24 import android.util.proto.ProtoOutputStream; 25 import android.view.inputmethod.InputMethodManager; 26 import android.view.inputmethod.InputMethodManagerGlobal; 27 28 import java.io.PrintWriter; 29 30 /** 31 * 32 * An abstract class that declares the methods for ime trace related operations - enable trace, 33 * schedule trace and add new trace to buffer. Both the client and server side classes can use 34 * it by getting an implementation through {@link ImeTracing#getInstance()}. 35 */ 36 public abstract class ImeTracing { 37 38 static final String TAG = "imeTracing"; 39 public static final String PROTO_ARG = "--proto-com-android-imetracing"; 40 41 /* Constants describing the component type that triggered a dump. */ 42 public static final int IME_TRACING_FROM_CLIENT = 0; 43 public static final int IME_TRACING_FROM_IMS = 1; 44 public static final int IME_TRACING_FROM_IMMS = 2; 45 46 private static ImeTracing sInstance; 47 static boolean sEnabled = false; 48 49 private final boolean mIsAvailable = InputMethodManagerGlobal.isImeTraceAvailable(); 50 51 protected boolean mDumpInProgress; 52 protected final Object mDumpInProgressLock = new Object(); 53 54 /** 55 * Returns an instance of {@link ImeTracingServerImpl} when called from a server side class 56 * and an instance of {@link ImeTracingClientImpl} when called from a client side class. 57 * Useful to schedule a dump for next frame or save a dump when certain methods are called. 58 * 59 * @return Instance of one of the children classes of {@link ImeTracing} 60 */ getInstance()61 public static ImeTracing getInstance() { 62 if (sInstance == null) { 63 if (android.tracing.Flags.perfettoIme()) { 64 sInstance = new ImeTracingPerfettoImpl(); 65 } else if (isSystemProcess()) { 66 sInstance = new ImeTracingServerImpl(); 67 } else { 68 sInstance = new ImeTracingClientImpl(); 69 } 70 } 71 return sInstance; 72 } 73 74 /** 75 * Transmits the information from client or InputMethodService side to the server, in order to 76 * be stored persistently to the current IME tracing dump. 77 * 78 * @param protoDump client or service side information to be stored by the server 79 * @param source where the information is coming from, refer to {@see #IME_TRACING_FROM_CLIENT} 80 * and {@see #IME_TRACING_FROM_IMS} 81 * @param where 82 */ sendToService(byte[] protoDump, int source, String where)83 protected void sendToService(byte[] protoDump, int source, String where) { 84 InputMethodManagerGlobal.startProtoDump(protoDump, source, where, 85 e -> Log.e(TAG, "Exception while sending ime-related dump to server", e)); 86 } 87 88 /** 89 * Start IME trace. 90 */ 91 @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING) startImeTrace()92 public final void startImeTrace() { 93 InputMethodManagerGlobal.startImeTrace(e -> Log.e(TAG, "Could not start ime trace.", e)); 94 } 95 96 /** 97 * Stop IME trace. 98 */ 99 @RequiresPermission(android.Manifest.permission.CONTROL_UI_TRACING) stopImeTrace()100 public final void stopImeTrace() { 101 InputMethodManagerGlobal.stopImeTrace(e -> Log.e(TAG, "Could not stop ime trace.", e)); 102 } 103 104 /** 105 * @param proto dump to be added to the buffer 106 */ addToBuffer(ProtoOutputStream proto, int source)107 public abstract void addToBuffer(ProtoOutputStream proto, int source); 108 109 /** 110 * Starts a proto dump of the client side information. 111 * 112 * @param where Place where the trace was triggered. 113 * @param immInstance The {@link InputMethodManager} instance to dump. 114 * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. 115 */ triggerClientDump(String where, InputMethodManager immInstance, @Nullable byte[] icProto)116 public abstract void triggerClientDump(String where, InputMethodManager immInstance, 117 @Nullable byte[] icProto); 118 119 /** 120 * A delegate for {@link #triggerServiceDump(String, ServiceDumper, byte[])}. 121 */ 122 @FunctionalInterface 123 public interface ServiceDumper { 124 /** 125 * Dumps internal data into {@link ProtoOutputStream}. 126 * 127 * @param proto {@link ProtoOutputStream} to be dumped into. 128 * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto 129 * format. 130 */ dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto)131 void dumpToProto(ProtoOutputStream proto, @Nullable byte[] icProto); 132 } 133 134 /** 135 * Starts a proto dump of the currently connected InputMethodService information. 136 * 137 * @param where Place where the trace was triggered. 138 * @param dumper {@link ServiceDumper} to be used to dump 139 * {@link android.inputmethodservice.InputMethodService}. 140 * @param icProto {@link android.view.inputmethod.InputConnection} call data in proto format. 141 */ triggerServiceDump(String where, ServiceDumper dumper, @Nullable byte[] icProto)142 public abstract void triggerServiceDump(String where, ServiceDumper dumper, 143 @Nullable byte[] icProto); 144 145 /** 146 * Starts a proto dump of the InputMethodManagerService information. 147 * 148 * @param where Place where the trace was triggered. 149 */ triggerManagerServiceDump(String where, @NonNull ServiceDumper dumper)150 public abstract void triggerManagerServiceDump(String where, @NonNull ServiceDumper dumper); 151 152 /** 153 * Being called while taking a bugreport so that tracing files can be included in the bugreport 154 * when the IME tracing is running. Does nothing otherwise. 155 * 156 * @param pw Print writer 157 */ saveForBugreport(@ullable PrintWriter pw)158 public void saveForBugreport(@Nullable PrintWriter pw) { 159 // does nothing by default. 160 } 161 162 /** 163 * Sets whether ime tracing is enabled. 164 * 165 * @param enabled Tells whether ime tracing should be enabled or disabled. 166 */ setEnabled(boolean enabled)167 public void setEnabled(boolean enabled) { 168 sEnabled = enabled; 169 } 170 171 /** 172 * @return {@code true} if dumping is enabled, {@code false} otherwise. 173 */ isEnabled()174 public boolean isEnabled() { 175 return sEnabled; 176 } 177 178 /** 179 * @return {@code true} if tracing is available, {@code false} otherwise. 180 */ isAvailable()181 public boolean isAvailable() { 182 return mIsAvailable; 183 } 184 185 /** 186 * Starts a new IME trace if one is not already started. 187 * 188 * @param pw Print writer 189 */ startTrace(@ullable PrintWriter pw)190 public abstract void startTrace(@Nullable PrintWriter pw); 191 192 /** 193 * Stops the IME trace if one was previously started and writes the current buffers to disk. 194 * 195 * @param pw Print writer 196 */ stopTrace(@ullable PrintWriter pw)197 public abstract void stopTrace(@Nullable PrintWriter pw); 198 isSystemProcess()199 private static boolean isSystemProcess() { 200 return ActivityThread.isSystem(); 201 } 202 logAndPrintln(@ullable PrintWriter pw, String msg)203 protected void logAndPrintln(@Nullable PrintWriter pw, String msg) { 204 Log.i(TAG, msg); 205 if (pw != null) { 206 pw.println(msg); 207 pw.flush(); 208 } 209 } 210 211 } 212