1 /* 2 * Copyright (C) 2024 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.os; 18 19 import android.annotation.Nullable; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.content.Intent; 22 import android.content.pm.ServiceInfo; 23 import android.util.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import java.util.Collections; 28 import java.util.List; 29 import java.util.Objects; 30 31 32 /** 33 * The DebugStore class provides methods for recording various debug events related to service 34 * lifecycle, broadcast receivers and others. 35 * The DebugStore class facilitates debugging ANR issues by recording time-stamped events 36 * related to service lifecycles, broadcast receivers, and other framework operations. It logs 37 * the start and end times of operations within the ANR timer scope called by framework, 38 * enabling pinpointing of methods and events contributing to ANRs. 39 * 40 * Usage currently includes recording service starts, binds, and asynchronous operations initiated 41 * by broadcast receivers, providing a granular view of system behavior that facilitates 42 * identifying performance bottlenecks and optimizing issue resolution. 43 * 44 * @hide 45 */ 46 public class DebugStore { 47 private static final boolean DEBUG_EVENTS = false; 48 private static final String TAG = "DebugStore"; 49 50 private static DebugStoreNative sDebugStoreNative = new DebugStoreNativeImpl(); 51 52 @UnsupportedAppUsage 53 @VisibleForTesting setDebugStoreNative(DebugStoreNative nativeImpl)54 public static void setDebugStoreNative(DebugStoreNative nativeImpl) { 55 sDebugStoreNative = nativeImpl; 56 } 57 /** 58 * Records the start of a service. 59 * 60 * @param startId The start ID of the service. 61 * @param flags Additional flags for the service start. 62 * @param intent The Intent associated with the service start. 63 * @return A unique ID for the recorded event. 64 */ 65 @UnsupportedAppUsage recordServiceOnStart(int startId, int flags, @Nullable Intent intent)66 public static long recordServiceOnStart(int startId, int flags, @Nullable Intent intent) { 67 return sDebugStoreNative.beginEvent( 68 "SvcStart", 69 List.of( 70 "stId", 71 String.valueOf(startId), 72 "flg", 73 Integer.toHexString(flags), 74 "act", 75 Objects.toString(intent != null ? intent.getAction() : null), 76 "comp", 77 Objects.toString(intent != null ? intent.getComponent() : null), 78 "pkg", 79 Objects.toString(intent != null ? intent.getPackage() : null))); 80 } 81 82 /** 83 * Records the creation of a service. 84 * 85 * @param serviceInfo Information about the service being created. 86 * @return A unique ID for the recorded event. 87 */ 88 @UnsupportedAppUsage recordServiceCreate(@ullable ServiceInfo serviceInfo)89 public static long recordServiceCreate(@Nullable ServiceInfo serviceInfo) { 90 return sDebugStoreNative.beginEvent( 91 "SvcCreate", 92 List.of( 93 "name", 94 Objects.toString(serviceInfo != null ? serviceInfo.name : null), 95 "pkg", 96 Objects.toString(serviceInfo != null ? serviceInfo.packageName : null))); 97 } 98 99 /** 100 * Records the binding of a service. 101 * 102 * @param isRebind Indicates whether the service is being rebound. 103 * @param intent The Intent associated with the service binding. 104 * @return A unique identifier for the recorded event. 105 */ 106 @UnsupportedAppUsage recordServiceBind(boolean isRebind, @Nullable Intent intent)107 public static long recordServiceBind(boolean isRebind, @Nullable Intent intent) { 108 return sDebugStoreNative.beginEvent( 109 "SvcBind", 110 List.of( 111 "rebind", 112 String.valueOf(isRebind), 113 "act", 114 Objects.toString(intent != null ? intent.getAction() : null), 115 "cmp", 116 Objects.toString(intent != null ? intent.getComponent() : null), 117 "pkg", 118 Objects.toString(intent != null ? intent.getPackage() : null))); 119 } 120 121 /** 122 * Records an asynchronous operation initiated by a broadcast receiver through calling GoAsync. 123 * 124 * @param receiverClassName The class name of the broadcast receiver. 125 */ 126 @UnsupportedAppUsage recordGoAsync(int pendingResultId)127 public static void recordGoAsync(int pendingResultId) { 128 sDebugStoreNative.recordEvent( 129 "GoAsync", 130 List.of( 131 "tname", 132 Thread.currentThread().getName(), 133 "tid", 134 String.valueOf(Thread.currentThread().getId()), 135 "prid", 136 Integer.toHexString(pendingResultId))); 137 } 138 139 /** 140 * Records the completion of a broadcast operation through calling Finish. 141 * 142 * @param receiverClassName The class of the broadcast receiver that completed the operation. 143 */ 144 @UnsupportedAppUsage recordFinish(int pendingResultId)145 public static void recordFinish(int pendingResultId) { 146 sDebugStoreNative.recordEvent( 147 "Finish", 148 List.of( 149 "tname", 150 Thread.currentThread().getName(), 151 "tid", 152 String.valueOf(Thread.currentThread().getId()), 153 "prid", 154 Integer.toHexString(pendingResultId))); 155 } 156 157 /** 158 * Records the completion of a long-running looper message. 159 * 160 * @param messageCode The code representing the type of the message. 161 * @param targetClass The FQN of the class that handled the message. 162 * @param elapsedTimeMs The time that was taken to process the message, in milliseconds. 163 */ 164 @UnsupportedAppUsage recordLongLooperMessage(int messageCode, String targetClass, long elapsedTimeMs)165 public static void recordLongLooperMessage(int messageCode, String targetClass, 166 long elapsedTimeMs) { 167 sDebugStoreNative.recordEvent( 168 "LooperMsg", 169 List.of( 170 "code", 171 String.valueOf(messageCode), 172 "trgt", 173 Objects.toString(targetClass), 174 "elapsed", 175 String.valueOf(elapsedTimeMs))); 176 } 177 178 179 /** 180 * Records the reception of a broadcast by a manifest-declared receiver. 181 * 182 * @param intent The Intent associated with the broadcast. 183 * @return A unique ID for the recorded event. 184 */ 185 @UnsupportedAppUsage recordBroadcastReceive(@ullable Intent intent, int pendingResultId)186 public static long recordBroadcastReceive(@Nullable Intent intent, int pendingResultId) { 187 return sDebugStoreNative.beginEvent( 188 "BcRcv", 189 List.of( 190 "tname", 191 Thread.currentThread().getName(), 192 "tid", 193 String.valueOf(Thread.currentThread().getId()), 194 "act", 195 Objects.toString(intent != null ? intent.getAction() : null), 196 "cmp", 197 Objects.toString(intent != null ? intent.getComponent() : null), 198 "pkg", 199 Objects.toString(intent != null ? intent.getPackage() : null), 200 "prid", 201 Integer.toHexString(pendingResultId))); 202 } 203 204 /** 205 * Records the reception of a broadcast by a context-registered receiver. 206 * 207 * @param intent The Intent associated with the broadcast. 208 * @param pendingResultId The object ID of the PendingResult associated with the broadcast. 209 * @return A unique ID for the recorded event. 210 */ 211 @UnsupportedAppUsage recordBroadcastReceiveReg(@ullable Intent intent, int pendingResultId)212 public static long recordBroadcastReceiveReg(@Nullable Intent intent, int pendingResultId) { 213 return sDebugStoreNative.beginEvent( 214 "BcRcvReg", 215 List.of( 216 "tname", 217 Thread.currentThread().getName(), 218 "tid", 219 String.valueOf(Thread.currentThread().getId()), 220 "act", 221 Objects.toString(intent != null ? intent.getAction() : null), 222 "cmp", 223 Objects.toString(intent != null ? intent.getComponent() : null), 224 "pkg", 225 Objects.toString(intent != null ? intent.getPackage() : null), 226 "prid", 227 Integer.toHexString(pendingResultId))); 228 } 229 230 /** 231 * Records the binding of an application. 232 * 233 * @return A unique ID for the recorded event. 234 */ 235 @UnsupportedAppUsage recordHandleBindApplication()236 public static long recordHandleBindApplication() { 237 return sDebugStoreNative.beginEvent("BindApp", List.of()); 238 } 239 240 /** 241 * Records the scheduling of a receiver. 242 * 243 * @return A unique ID for the recorded event. 244 */ 245 @UnsupportedAppUsage recordScheduleReceiver()246 public static long recordScheduleReceiver() { 247 return sDebugStoreNative.beginEvent( 248 "SchRcv", 249 List.of( 250 "tname", Thread.currentThread().getName(), 251 "tid", String.valueOf(Thread.currentThread().getId()))); 252 } 253 254 /** 255 * Records the scheduling of a registered receiver. 256 * 257 * @return A unique ID for the recorded event. 258 */ 259 @UnsupportedAppUsage recordScheduleRegisteredReceiver()260 public static long recordScheduleRegisteredReceiver() { 261 return sDebugStoreNative.beginEvent( 262 "SchRcvReg", 263 List.of( 264 "tname", Thread.currentThread().getName(), 265 "tid", String.valueOf(Thread.currentThread().getId()))); 266 } 267 268 /** 269 * Ends a previously recorded event. 270 * 271 * @param id The unique ID of the event to be ended. 272 */ 273 @UnsupportedAppUsage recordEventEnd(long id)274 public static void recordEventEnd(long id) { 275 sDebugStoreNative.endEvent(id, Collections.emptyList()); 276 } 277 278 /** 279 * An interface for a class that acts as a wrapper for the static native methods 280 * of the Debug Store. 281 * 282 * It allows us to mock static native methods in our tests and should be removed 283 * once mocking static methods becomes easier. 284 */ 285 @VisibleForTesting 286 public interface DebugStoreNative { 287 /** 288 * Begins an event with the given name and attributes. 289 */ beginEvent(String eventName, List<String> attributes)290 long beginEvent(String eventName, List<String> attributes); 291 /** 292 * Ends an event with the given ID and attributes. 293 */ endEvent(long id, List<String> attributes)294 void endEvent(long id, List<String> attributes); 295 /** 296 * Records an event with the given name and attributes. 297 */ recordEvent(String eventName, List<String> attributes)298 void recordEvent(String eventName, List<String> attributes); 299 } 300 301 private static class DebugStoreNativeImpl implements DebugStoreNative { 302 @Override beginEvent(String eventName, List<String> attributes)303 public long beginEvent(String eventName, List<String> attributes) { 304 long id = DebugStore.beginEventNative(eventName, attributes); 305 if (DEBUG_EVENTS) { 306 Log.i( 307 TAG, 308 "beginEvent: " + id + " " + eventName + " " + attributeString(attributes)); 309 } 310 return id; 311 } 312 313 @Override endEvent(long id, List<String> attributes)314 public void endEvent(long id, List<String> attributes) { 315 if (DEBUG_EVENTS) { 316 Log.i(TAG, "endEvent: " + id + " " + attributeString(attributes)); 317 } 318 DebugStore.endEventNative(id, attributes); 319 } 320 321 @Override recordEvent(String eventName, List<String> attributes)322 public void recordEvent(String eventName, List<String> attributes) { 323 if (DEBUG_EVENTS) { 324 Log.i(TAG, "recordEvent: " + eventName + " " + attributeString(attributes)); 325 } 326 DebugStore.recordEventNative(eventName, attributes); 327 } 328 329 /** 330 * Returns a string like "[key1=foo, key2=bar]" 331 */ attributeString(List<String> attributes)332 private String attributeString(List<String> attributes) { 333 StringBuilder sb = new StringBuilder().append("["); 334 335 for (int i = 0; i < attributes.size(); i++) { 336 sb.append(attributes.get(i)); 337 338 if (i % 2 == 0) { 339 sb.append("="); 340 } else if (i < attributes.size() - 1) { 341 sb.append(", "); 342 } 343 } 344 return sb.append("]").toString(); 345 } 346 } 347 beginEventNative(String eventName, List<String> attributes)348 private static native long beginEventNative(String eventName, List<String> attributes); 349 endEventNative(long id, List<String> attributes)350 private static native void endEventNative(long id, List<String> attributes); 351 recordEventNative(String eventName, List<String> attributes)352 private static native void recordEventNative(String eventName, List<String> attributes); 353 } 354