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