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