1 /* 2 * Copyright (C) 2017 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 android.util; 18 19 import static android.Manifest.permission.DUMP; 20 import static android.Manifest.permission.PACKAGE_USAGE_STATS; 21 22 import android.Manifest; 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SuppressLint; 27 import android.annotation.SystemApi; 28 import android.content.Context; 29 import android.os.Build; 30 import android.os.IStatsd; 31 import android.os.Process; 32 import android.util.proto.ProtoOutputStream; 33 34 import androidx.annotation.RequiresApi; 35 36 import com.android.internal.statsd.StatsdStatsLog; 37 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 41 /** 42 * StatsLog provides an API for developers to send events to statsd. The events can be used to 43 * define custom metrics inside statsd. 44 */ 45 public final class StatsLog { 46 47 // Load JNI library 48 static { 49 System.loadLibrary("stats_jni"); 50 } 51 private static final String TAG = "StatsLog"; 52 private static final boolean DEBUG = false; 53 private static final int EXPERIMENT_IDS_FIELD_ID = 1; 54 55 /** 56 * Annotation ID constant for logging UID field. 57 * 58 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 59 * accept byte as the type for annotation ids to save space. 60 * 61 * @hide 62 */ 63 @SuppressLint("NoByteOrShort") 64 @SystemApi 65 public static final byte ANNOTATION_ID_IS_UID = 1; 66 67 /** 68 * Annotation ID constant to indicate logged atom event's timestamp should be truncated. 69 * 70 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 71 * accept byte as the type for annotation ids to save space. 72 * 73 * @hide 74 */ 75 @SuppressLint("NoByteOrShort") 76 @SystemApi 77 public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; 78 79 /** 80 * Annotation ID constant for a state atom's primary field. 81 * 82 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 83 * accept byte as the type for annotation ids to save space. 84 * 85 * @hide 86 */ 87 @SuppressLint("NoByteOrShort") 88 @SystemApi 89 public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3; 90 91 /** 92 * Annotation ID constant for state atom's state field. 93 * 94 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 95 * accept byte as the type for annotation ids to save space. 96 * 97 * @hide 98 */ 99 @SuppressLint("NoByteOrShort") 100 @SystemApi 101 public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4; 102 103 /** 104 * Annotation ID constant to indicate the first UID in the attribution chain 105 * is a primary field. 106 * Should only be used for attribution chain fields. 107 * 108 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 109 * accept byte as the type for annotation ids to save space. 110 * 111 * @hide 112 */ 113 @SuppressLint("NoByteOrShort") 114 @SystemApi 115 public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; 116 117 /** 118 * Annotation ID constant to indicate which state is default for the state atom. 119 * 120 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 121 * accept byte as the type for annotation ids to save space. 122 * 123 * @hide 124 */ 125 @SuppressLint("NoByteOrShort") 126 @SystemApi 127 public static final byte ANNOTATION_ID_DEFAULT_STATE = 6; 128 129 /** 130 * Annotation ID constant to signal all states should be reset to the default state. 131 * 132 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 133 * accept byte as the type for annotation ids to save space. 134 * 135 * @hide 136 */ 137 @SuppressLint("NoByteOrShort") 138 @SystemApi 139 public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7; 140 141 /** 142 * Annotation ID constant to indicate state changes need to account for nesting. 143 * This should only be used with binary state atoms. 144 * 145 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 146 * accept byte as the type for annotation ids to save space. 147 * 148 * @hide 149 */ 150 @SuppressLint("NoByteOrShort") 151 @SystemApi 152 public static final byte ANNOTATION_ID_STATE_NESTED = 8; 153 154 /** 155 * Annotation ID constant to indicate the restriction category of an atom. 156 * This annotation must only be attached to the atom id. This is an int annotation. 157 * 158 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 159 * accept byte as the type for annotation ids to save space. 160 * 161 * @hide 162 */ 163 @SuppressLint("NoByteOrShort") 164 @SystemApi 165 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 166 public static final byte ANNOTATION_ID_RESTRICTION_CATEGORY = 9; 167 168 /** 169 * Annotation ID to indicate that a field of an atom contains peripheral device info. 170 * This is a bool annotation. 171 * 172 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 173 * accept byte as the type for annotation ids to save space. 174 * 175 * @hide 176 */ 177 @SuppressLint("NoByteOrShort") 178 @SystemApi 179 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 180 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO = 10; 181 182 /** 183 * Annotation ID to indicate that a field of an atom contains app usage information. 184 * This is a bool annotation. 185 * 186 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 187 * accept byte as the type for annotation ids to save space. 188 * 189 * @hide 190 */ 191 @SuppressLint("NoByteOrShort") 192 @SystemApi 193 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 194 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE = 11; 195 196 /** 197 * Annotation ID to indicate that a field of an atom contains app activity information. 198 * This is a bool annotation. 199 * 200 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 201 * accept byte as the type for annotation ids to save space. 202 * 203 * @hide 204 */ 205 @SuppressLint("NoByteOrShort") 206 @SystemApi 207 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 208 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY = 12; 209 210 /** 211 * Annotation ID to indicate that a field of an atom contains health connect information. 212 * This is a bool annotation. 213 * 214 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 215 * accept byte as the type for annotation ids to save space. 216 * 217 * @hide 218 */ 219 @SuppressLint("NoByteOrShort") 220 @SystemApi 221 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 222 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT = 13; 223 224 /** 225 * Annotation ID to indicate that a field of an atom contains accessibility information. 226 * This is a bool annotation. 227 * 228 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 229 * accept byte as the type for annotation ids to save space. 230 * 231 * @hide 232 */ 233 @SuppressLint("NoByteOrShort") 234 @SystemApi 235 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 236 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY = 14; 237 238 /** 239 * Annotation ID to indicate that a field of an atom contains system search information. 240 * This is a bool annotation. 241 * 242 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 243 * accept byte as the type for annotation ids to save space. 244 * 245 * @hide 246 */ 247 @SuppressLint("NoByteOrShort") 248 @SystemApi 249 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 250 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH = 15; 251 252 /** 253 * Annotation ID to indicate that a field of an atom contains user engagement information. 254 * This is a bool annotation. 255 * 256 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 257 * accept byte as the type for annotation ids to save space. 258 * 259 * @hide 260 */ 261 @SuppressLint("NoByteOrShort") 262 @SystemApi 263 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 264 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT = 16; 265 266 /** 267 * Annotation ID to indicate that a field of an atom contains ambient sensing information. 268 * This is a bool annotation. 269 * 270 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 271 * accept byte as the type for annotation ids to save space. 272 * 273 * @hide 274 */ 275 @SuppressLint("NoByteOrShort") 276 @SystemApi 277 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 278 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING = 17; 279 280 /** 281 * Annotation ID to indicate that a field of an atom contains demographic classification 282 * information. This is a bool annotation. 283 * 284 * The ID is a byte since StatsEvent.addBooleanAnnotation() and StatsEvent.addIntAnnotation() 285 * accept byte as the type for annotation ids to save space. 286 * 287 * @hide 288 */ 289 @SuppressLint("NoByteOrShort") 290 @SystemApi 291 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 292 public static final byte ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION = 18; 293 294 295 /** @hide */ 296 @IntDef(prefix = { "RESTRICTION_CATEGORY_" }, value = { 297 RESTRICTION_CATEGORY_DIAGNOSTIC, 298 RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE, 299 RESTRICTION_CATEGORY_AUTHENTICATION, 300 RESTRICTION_CATEGORY_FRAUD_AND_ABUSE}) 301 @Retention(RetentionPolicy.SOURCE) 302 public @interface RestrictionCategory {} 303 304 /** 305 * Restriction category for atoms about diagnostics. 306 * 307 * @hide 308 */ 309 @SystemApi 310 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 311 public static final int RESTRICTION_CATEGORY_DIAGNOSTIC = 1; 312 313 /** 314 * Restriction category for atoms about system intelligence. 315 * 316 * @hide 317 */ 318 @SystemApi 319 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 320 public static final int RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE = 2; 321 322 /** 323 * Restriction category for atoms about authentication. 324 * 325 * @hide 326 */ 327 @SystemApi 328 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 329 public static final int RESTRICTION_CATEGORY_AUTHENTICATION = 3; 330 331 /** 332 * Restriction category for atoms about fraud and abuse. 333 * 334 * @hide 335 */ 336 @SystemApi 337 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 338 public static final int RESTRICTION_CATEGORY_FRAUD_AND_ABUSE = 4; 339 StatsLog()340 private StatsLog() { 341 } 342 343 /** 344 * Logs a start event. 345 * 346 * @param label developer-chosen label. 347 * @return True if the log request was sent to statsd. 348 */ logStart(int label)349 public static boolean logStart(int label) { 350 int callingUid = Process.myUid(); 351 StatsdStatsLog.write( 352 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 353 callingUid, 354 label, 355 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); 356 return true; 357 } 358 359 /** 360 * Logs a stop event. 361 * 362 * @param label developer-chosen label. 363 * @return True if the log request was sent to statsd. 364 */ logStop(int label)365 public static boolean logStop(int label) { 366 int callingUid = Process.myUid(); 367 StatsdStatsLog.write( 368 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 369 callingUid, 370 label, 371 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); 372 return true; 373 } 374 375 /** 376 * Logs an event that does not represent a start or stop boundary. 377 * 378 * @param label developer-chosen label. 379 * @return True if the log request was sent to statsd. 380 */ logEvent(int label)381 public static boolean logEvent(int label) { 382 int callingUid = Process.myUid(); 383 StatsdStatsLog.write( 384 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 385 callingUid, 386 label, 387 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); 388 return true; 389 } 390 391 /** 392 * Logs an event for binary push for module updates. 393 * 394 * @param trainName name of install train. 395 * @param trainVersionCode version code of the train. 396 * @param options optional flags about this install. 397 * The last 3 bits indicate options: 398 * 0x01: FLAG_REQUIRE_STAGING 399 * 0x02: FLAG_ROLLBACK_ENABLED 400 * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR 401 * @param state current install state. Defined as State enums in 402 * BinaryPushStateChanged atom in 403 * frameworks/proto_logging/stats/atoms.proto 404 * @param experimentIds experiment ids. 405 * @return True if the log request was sent to statsd. 406 */ 407 @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) logBinaryPushStateChanged(@onNull String trainName, long trainVersionCode, int options, int state, @NonNull long[] experimentIds)408 public static boolean logBinaryPushStateChanged(@NonNull String trainName, 409 long trainVersionCode, int options, int state, 410 @NonNull long[] experimentIds) { 411 ProtoOutputStream proto = new ProtoOutputStream(); 412 for (long id : experimentIds) { 413 proto.write( 414 ProtoOutputStream.FIELD_TYPE_INT64 415 | ProtoOutputStream.FIELD_COUNT_REPEATED 416 | EXPERIMENT_IDS_FIELD_ID, 417 id); 418 } 419 StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED, 420 trainName, 421 trainVersionCode, 422 (options & IStatsd.FLAG_REQUIRE_STAGING) > 0, 423 (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0, 424 (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0, 425 state, 426 proto.getBytes(), 427 0, 428 0, 429 false); 430 return true; 431 } 432 433 /** 434 * Write an event to stats log using the raw format. 435 * 436 * @param buffer The encoded buffer of data to write. 437 * @param size The number of bytes from the buffer to write. 438 * @hide 439 * @deprecated Use {@link write(final StatsEvent statsEvent)} instead. 440 * 441 */ 442 @Deprecated 443 @SystemApi writeRaw(@onNull byte[] buffer, int size)444 public static void writeRaw(@NonNull byte[] buffer, int size) { 445 writeImpl(buffer, size, 0); 446 } 447 448 /** 449 * Write an event to stats log using the raw format. 450 * 451 * @param buffer The encoded buffer of data to write. 452 * @param size The number of bytes from the buffer to write. 453 * @param atomId The id of the atom to which the event belongs. 454 */ writeImpl(@onNull byte[] buffer, int size, int atomId)455 private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId); 456 457 /** 458 * Write an event to stats log using the raw format encapsulated in StatsEvent. 459 * After writing to stats log, release() is called on the StatsEvent object. 460 * No further action should be taken on the StatsEvent object following this call. 461 * 462 * @param statsEvent The StatsEvent object containing the encoded buffer of data to write. 463 * @hide 464 */ 465 @SystemApi write(@onNull final StatsEvent statsEvent)466 public static void write(@NonNull final StatsEvent statsEvent) { 467 writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId()); 468 statsEvent.release(); 469 } 470 } 471