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