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.NonNull; 24 import android.annotation.RequiresPermission; 25 import android.annotation.SuppressLint; 26 import android.annotation.SystemApi; 27 import android.content.Context; 28 import android.os.IStatsd; 29 import android.os.Process; 30 import android.util.proto.ProtoOutputStream; 31 32 import com.android.internal.statsd.StatsdStatsLog; 33 34 /** 35 * StatsLog provides an API for developers to send events to statsd. The events can be used to 36 * define custom metrics inside statsd. 37 */ 38 public final class StatsLog { 39 40 // Load JNI library 41 static { 42 System.loadLibrary("stats_jni"); 43 } 44 private static final String TAG = "StatsLog"; 45 private static final boolean DEBUG = false; 46 private static final int EXPERIMENT_IDS_FIELD_ID = 1; 47 48 /** 49 * Annotation ID constant for logging UID field. 50 * 51 * @hide 52 */ 53 @SuppressLint("NoByteOrShort") 54 @SystemApi 55 public static final byte ANNOTATION_ID_IS_UID = 1; 56 57 /** 58 * Annotation ID constant to indicate logged atom event's timestamp should be truncated. 59 * 60 * @hide 61 */ 62 @SuppressLint("NoByteOrShort") 63 @SystemApi 64 public static final byte ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2; 65 66 /** 67 * Annotation ID constant for a state atom's primary field. 68 * 69 * @hide 70 */ 71 @SuppressLint("NoByteOrShort") 72 @SystemApi 73 public static final byte ANNOTATION_ID_PRIMARY_FIELD = 3; 74 75 /** 76 * Annotation ID constant for state atom's state field. 77 * 78 * @hide 79 */ 80 @SuppressLint("NoByteOrShort") 81 @SystemApi 82 public static final byte ANNOTATION_ID_EXCLUSIVE_STATE = 4; 83 84 /** 85 * Annotation ID constant to indicate the first UID in the attribution chain 86 * is a primary field. 87 * Should only be used for attribution chain fields. 88 * 89 * @hide 90 */ 91 @SuppressLint("NoByteOrShort") 92 @SystemApi 93 public static final byte ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID = 5; 94 95 /** 96 * Annotation ID constant to indicate which state is default for the state atom. 97 * 98 * @hide 99 */ 100 @SuppressLint("NoByteOrShort") 101 @SystemApi 102 public static final byte ANNOTATION_ID_DEFAULT_STATE = 6; 103 104 /** 105 * Annotation ID constant to signal all states should be reset to the default state. 106 * 107 * @hide 108 */ 109 @SuppressLint("NoByteOrShort") 110 @SystemApi 111 public static final byte ANNOTATION_ID_TRIGGER_STATE_RESET = 7; 112 113 /** 114 * Annotation ID constant to indicate state changes need to account for nesting. 115 * This should only be used with binary state atoms. 116 * 117 * @hide 118 */ 119 @SuppressLint("NoByteOrShort") 120 @SystemApi 121 public static final byte ANNOTATION_ID_STATE_NESTED = 8; 122 123 private StatsLog() { 124 } 125 126 /** 127 * Logs a start event. 128 * 129 * @param label developer-chosen label. 130 * @return True if the log request was sent to statsd. 131 */ 132 public static boolean logStart(int label) { 133 int callingUid = Process.myUid(); 134 StatsdStatsLog.write( 135 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 136 callingUid, 137 label, 138 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START); 139 return true; 140 } 141 142 /** 143 * Logs a stop event. 144 * 145 * @param label developer-chosen label. 146 * @return True if the log request was sent to statsd. 147 */ 148 public static boolean logStop(int label) { 149 int callingUid = Process.myUid(); 150 StatsdStatsLog.write( 151 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 152 callingUid, 153 label, 154 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); 155 return true; 156 } 157 158 /** 159 * Logs an event that does not represent a start or stop boundary. 160 * 161 * @param label developer-chosen label. 162 * @return True if the log request was sent to statsd. 163 */ 164 public static boolean logEvent(int label) { 165 int callingUid = Process.myUid(); 166 StatsdStatsLog.write( 167 StatsdStatsLog.APP_BREADCRUMB_REPORTED, 168 callingUid, 169 label, 170 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); 171 return true; 172 } 173 174 /** 175 * Logs an event for binary push for module updates. 176 * 177 * @param trainName name of install train. 178 * @param trainVersionCode version code of the train. 179 * @param options optional flags about this install. 180 * The last 3 bits indicate options: 181 * 0x01: FLAG_REQUIRE_STAGING 182 * 0x02: FLAG_ROLLBACK_ENABLED 183 * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR 184 * @param state current install state. Defined as State enums in 185 * BinaryPushStateChanged atom in 186 * frameworks/proto_logging/stats/atoms.proto 187 * @param experimentIds experiment ids. 188 * @return True if the log request was sent to statsd. 189 */ 190 @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) 191 public static boolean logBinaryPushStateChanged(@NonNull String trainName, 192 long trainVersionCode, int options, int state, 193 @NonNull long[] experimentIds) { 194 ProtoOutputStream proto = new ProtoOutputStream(); 195 for (long id : experimentIds) { 196 proto.write( 197 ProtoOutputStream.FIELD_TYPE_INT64 198 | ProtoOutputStream.FIELD_COUNT_REPEATED 199 | EXPERIMENT_IDS_FIELD_ID, 200 id); 201 } 202 StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED, 203 trainName, 204 trainVersionCode, 205 (options & IStatsd.FLAG_REQUIRE_STAGING) > 0, 206 (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0, 207 (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0, 208 state, 209 proto.getBytes(), 210 0, 211 0, 212 false); 213 return true; 214 } 215 216 /** 217 * Write an event to stats log using the raw format. 218 * 219 * @param buffer The encoded buffer of data to write. 220 * @param size The number of bytes from the buffer to write. 221 * @hide 222 * @deprecated Use {@link write(final StatsEvent statsEvent)} instead. 223 * 224 */ 225 @Deprecated 226 @SystemApi 227 public static void writeRaw(@NonNull byte[] buffer, int size) { 228 writeImpl(buffer, size, 0); 229 } 230 231 /** 232 * Write an event to stats log using the raw format. 233 * 234 * @param buffer The encoded buffer of data to write. 235 * @param size The number of bytes from the buffer to write. 236 * @param atomId The id of the atom to which the event belongs. 237 */ 238 private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId); 239 240 /** 241 * Write an event to stats log using the raw format encapsulated in StatsEvent. 242 * After writing to stats log, release() is called on the StatsEvent object. 243 * No further action should be taken on the StatsEvent object following this call. 244 * 245 * @param statsEvent The StatsEvent object containing the encoded buffer of data to write. 246 * @hide 247 */ 248 @SystemApi 249 public static void write(@NonNull final StatsEvent statsEvent) { 250 writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId()); 251 statsEvent.release(); 252 } 253 254 private static void enforceDumpCallingPermission(Context context) { 255 context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission."); 256 } 257 258 private static void enforcesageStatsCallingPermission(Context context) { 259 context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, 260 "Need PACKAGE_USAGE_STATS permission."); 261 } 262 } 263