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