1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *      http://www.apache.org/licenses/LICENSE-2.0
7  * Unless required by applicable law or agreed to in writing, software
8  * distributed under the License is distributed on an "AS IS" BASIS,
9  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10  * See the License for the specific language governing permissions and
11  * limitations under the License.
12  */
13 
14 package androidx.core.os;
15 
16 import android.os.Build;
17 import android.os.Trace;
18 import android.util.Log;
19 
20 import androidx.annotation.RequiresApi;
21 
22 import org.jspecify.annotations.NonNull;
23 
24 import java.lang.reflect.Field;
25 import java.lang.reflect.Method;
26 
27 /**
28  * Writes trace events to the system trace buffer.  These trace events can be
29  * collected and visualized using the Systrace tool.
30  *
31  * <p>This tracing mechanism is independent of the method tracing mechanism
32  * offered by {@link android.os.Debug#startMethodTracing}.  In particular, it enables
33  * tracing of events that occur across multiple processes.
34  * <p>For information about using the Systrace tool, read <a
35  * href="{@docRoot}studio/profile/systrace/">Overview of system tracing</a>.
36  *
37  * @deprecated TraceCompat is deprecated in favor of androidx.tracing.Trace. Please use that
38  * instead.
39  */
40 @SuppressWarnings("JavaReflectionMemberAccess")
41 @Deprecated
42 public final class TraceCompat {
43 
44     private static final String TAG = "TraceCompat";
45 
46     private static long sTraceTagApp;
47     private static Method sIsTagEnabledMethod;
48     private static Method sAsyncTraceBeginMethod;
49     private static Method sAsyncTraceEndMethod;
50     private static Method sTraceCounterMethod;
51 
52     static {
53         if (Build.VERSION.SDK_INT < 29) {
54             try {
55                 Field traceTagAppField = Trace.class.getField("TRACE_TAG_APP");
56                 sTraceTagApp = traceTagAppField.getLong(null);
57 
58                 sIsTagEnabledMethod = Trace.class.getMethod("isTagEnabled", long.class);
59                 sAsyncTraceBeginMethod = Trace.class.getMethod("asyncTraceBegin", long.class,
60                         String.class, int.class);
61                 sAsyncTraceEndMethod = Trace.class.getMethod("asyncTraceEnd", long.class,
62                         String.class, int.class);
63                 sTraceCounterMethod = Trace.class.getMethod("traceCounter", long.class,
64                         String.class, int.class);
65             } catch (Exception e) {
66                 Log.i(TAG, "Unable to initialize via reflection.", e);
67             }
68         }
69     }
70 
71     /**
72      * Checks whether or not tracing is currently enabled. This is useful to avoid intermediate
73      * string creation for trace sections that require formatting. It is not necessary
74      * to guard all Trace method calls as they internally already check this. However it is
75      * recommended to use this to prevent creating any temporary objects that would then be
76      * passed to those methods to reduce runtime cost when tracing isn't enabled.
77      *
78      * @return true if tracing is currently enabled, false otherwise
79      */
80     @SuppressWarnings("ConstantConditions")
isEnabled()81     public static boolean isEnabled() {
82         if (Build.VERSION.SDK_INT >= 29) {
83             return Api29Impl.isEnabled();
84         } else {
85             try {
86                 return (boolean) sIsTagEnabledMethod.invoke(null, sTraceTagApp);
87             } catch (Exception e) {
88                 Log.v(TAG, "Unable to invoke isTagEnabled() via reflection.");
89             }
90         }
91 
92         // Never enabled on < API 18
93         return false;
94     }
95 
96     /**
97      * Writes a trace message to indicate that a given section of code has begun. This call must
98      * be followed by a corresponding call to {@link #endSection()} on the same thread.
99      *
100      * <p class="note"> At this time the vertical bar character '|', newline character '\n', and
101      * null character '\0' are used internally by the tracing mechanism.  If sectionName contains
102      * these characters they will be replaced with a space character in the trace.
103      *
104      * @param sectionName The name of the code section to appear in the trace.  This may be at
105      * most 127 Unicode code units long.
106      */
beginSection(@onNull String sectionName)107     public static void beginSection(@NonNull String sectionName) {
108         Trace.beginSection(sectionName);
109     }
110 
111     /**
112      * Writes a trace message to indicate that a given section of code has ended. This call must
113      * be preceeded by a corresponding call to {@link #beginSection(String)}. Calling this method
114      * will mark the end of the most recently begun section of code, so care must be taken to
115      * ensure that beginSection / endSection pairs are properly nested and called from the same
116      * thread.
117      */
endSection()118     public static void endSection() {
119         Trace.endSection();
120     }
121 
122     /**
123      * Writes a trace message to indicate that a given section of code has
124      * begun. Must be followed by a call to {@link #endAsyncSection(String, int)} with the same
125      * methodName and cookie. Unlike {@link #beginSection(String)} and {@link #endSection()},
126      * asynchronous events do not need to be nested. The name and cookie used to
127      * begin an event must be used to end it.
128      *
129      * @param methodName The method name to appear in the trace.
130      * @param cookie     Unique identifier for distinguishing simultaneous events
131      */
beginAsyncSection(@onNull String methodName, int cookie)132     public static void beginAsyncSection(@NonNull String methodName, int cookie) {
133         if (Build.VERSION.SDK_INT >= 29) {
134             Api29Impl.beginAsyncSection(methodName, cookie);
135         } else {
136             try {
137                 sAsyncTraceBeginMethod.invoke(null, sTraceTagApp, methodName, cookie);
138             } catch (Exception e) {
139                 Log.v(TAG, "Unable to invoke asyncTraceBegin() via reflection.");
140             }
141         }
142     }
143 
144     /**
145      * Writes a trace message to indicate that the current method has ended.
146      * Must be called exactly once for each call to {@link #beginAsyncSection(String, int)}
147      * using the same name and cookie.
148      *
149      * @param methodName The method name to appear in the trace.
150      * @param cookie     Unique identifier for distinguishing simultaneous events
151      */
endAsyncSection(@onNull String methodName, int cookie)152     public static void endAsyncSection(@NonNull String methodName, int cookie) {
153         if (Build.VERSION.SDK_INT >= 29) {
154             Api29Impl.endAsyncSection(methodName, cookie);
155         } else {
156             try {
157                 sAsyncTraceEndMethod.invoke(null, sTraceTagApp, methodName, cookie);
158             } catch (Exception e) {
159                 Log.v(TAG, "Unable to invoke endAsyncSection() via reflection.");
160             }
161         }
162     }
163 
164 
165     /**
166      * Writes trace message to indicate the value of a given counter.
167      *
168      * @param counterName  The counter name to appear in the trace.
169      * @param counterValue The counter value.
170      */
setCounter(@onNull String counterName, int counterValue)171     public static void setCounter(@NonNull String counterName, int counterValue) {
172         if (Build.VERSION.SDK_INT >= 29) {
173             Api29Impl.setCounter(counterName, counterValue);
174         } else {
175             try {
176                 sTraceCounterMethod.invoke(null, sTraceTagApp, counterName, counterValue);
177             } catch (Exception e) {
178                 Log.v(TAG, "Unable to invoke traceCounter() via reflection.");
179             }
180         }
181     }
182 
TraceCompat()183     private TraceCompat() {
184     }
185 
186     @RequiresApi(29)
187     static class Api29Impl {
Api29Impl()188         private Api29Impl() {
189             // This class is not instantiable.
190         }
191 
isEnabled()192         static boolean isEnabled() {
193             return Trace.isEnabled();
194         }
195 
endAsyncSection(String methodName, int cookie)196         static void endAsyncSection(String methodName, int cookie) {
197             Trace.endAsyncSection(methodName, cookie);
198         }
199 
beginAsyncSection(String methodName, int cookie)200         static void beginAsyncSection(String methodName, int cookie) {
201             Trace.beginAsyncSection(methodName, cookie);
202         }
203 
setCounter(String counterName, long counterValue)204         static void setCounter(String counterName, long counterValue) {
205             Trace.setCounter(counterName, counterValue);
206         }
207     }
208 }
209