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.SystemApi; 26 import android.content.Context; 27 import android.os.IStatsManager; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 31 /** 32 * StatsLog provides an API for developers to send events to statsd. The events can be used to 33 * define custom metrics inside statsd. 34 */ 35 public final class StatsLog extends StatsLogInternal { 36 private static final String TAG = "StatsLog"; 37 private static final boolean DEBUG = false; 38 39 private static IStatsManager sService; 40 41 private static Object sLogLock = new Object(); 42 StatsLog()43 private StatsLog() { 44 } 45 46 /** 47 * Logs a start event. 48 * 49 * @param label developer-chosen label. 50 * @return True if the log request was sent to statsd. 51 */ logStart(int label)52 public static boolean logStart(int label) { 53 synchronized (sLogLock) { 54 try { 55 IStatsManager service = getIStatsManagerLocked(); 56 if (service == null) { 57 if (DEBUG) { 58 Slog.d(TAG, "Failed to find statsd when logging start"); 59 } 60 return false; 61 } 62 service.sendAppBreadcrumbAtom(label, 63 StatsLog.APP_BREADCRUMB_REPORTED__STATE__START); 64 return true; 65 } catch (RemoteException e) { 66 sService = null; 67 if (DEBUG) { 68 Slog.d(TAG, "Failed to connect to statsd when logging start"); 69 } 70 return false; 71 } 72 } 73 } 74 75 /** 76 * Logs a stop event. 77 * 78 * @param label developer-chosen label. 79 * @return True if the log request was sent to statsd. 80 */ logStop(int label)81 public static boolean logStop(int label) { 82 synchronized (sLogLock) { 83 try { 84 IStatsManager service = getIStatsManagerLocked(); 85 if (service == null) { 86 if (DEBUG) { 87 Slog.d(TAG, "Failed to find statsd when logging stop"); 88 } 89 return false; 90 } 91 service.sendAppBreadcrumbAtom(label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP); 92 return true; 93 } catch (RemoteException e) { 94 sService = null; 95 if (DEBUG) { 96 Slog.d(TAG, "Failed to connect to statsd when logging stop"); 97 } 98 return false; 99 } 100 } 101 } 102 103 /** 104 * Logs an event that does not represent a start or stop boundary. 105 * 106 * @param label developer-chosen label. 107 * @return True if the log request was sent to statsd. 108 */ logEvent(int label)109 public static boolean logEvent(int label) { 110 synchronized (sLogLock) { 111 try { 112 IStatsManager service = getIStatsManagerLocked(); 113 if (service == null) { 114 if (DEBUG) { 115 Slog.d(TAG, "Failed to find statsd when logging event"); 116 } 117 return false; 118 } 119 service.sendAppBreadcrumbAtom( 120 label, StatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED); 121 return true; 122 } catch (RemoteException e) { 123 sService = null; 124 if (DEBUG) { 125 Slog.d(TAG, "Failed to connect to statsd when logging event"); 126 } 127 return false; 128 } 129 } 130 } 131 132 /** 133 * Logs an event for binary push for module updates. 134 * 135 * @param trainName name of install train. 136 * @param trainVersionCode version code of the train. 137 * @param options optional flags about this install. 138 * The last 3 bits indicate options: 139 * 0x01: FLAG_REQUIRE_STAGING 140 * 0x02: FLAG_ROLLBACK_ENABLED 141 * 0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR 142 * @param state current install state. Defined as State enums in 143 * BinaryPushStateChanged atom in 144 * frameworks/base/cmds/statsd/src/atoms.proto 145 * @param experimentIds experiment ids. 146 * @return True if the log request was sent to statsd. 147 */ 148 @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) logBinaryPushStateChanged(@onNull String trainName, long trainVersionCode, int options, int state, @NonNull long[] experimentIds)149 public static boolean logBinaryPushStateChanged(@NonNull String trainName, 150 long trainVersionCode, int options, int state, 151 @NonNull long[] experimentIds) { 152 synchronized (sLogLock) { 153 try { 154 IStatsManager service = getIStatsManagerLocked(); 155 if (service == null) { 156 if (DEBUG) { 157 Slog.d(TAG, "Failed to find statsd when logging event"); 158 } 159 return false; 160 } 161 service.sendBinaryPushStateChangedAtom( 162 trainName, trainVersionCode, options, state, experimentIds); 163 return true; 164 } catch (RemoteException e) { 165 sService = null; 166 if (DEBUG) { 167 Slog.d(TAG, 168 "Failed to connect to StatsCompanionService when logging " 169 + "BinaryPushStateChanged"); 170 } 171 return false; 172 } 173 } 174 } 175 176 /** 177 * Logs an event for watchdog rollbacks. 178 * 179 * @param rollbackType state of the rollback. 180 * @param packageName package name being rolled back. 181 * @param packageVersionCode version of the package being rolled back. 182 * @param rollbackReason reason the package is being rolled back. 183 * @param failingPackageName the package name causing the failure. 184 * 185 * @return True if the log request was sent to statsd. 186 * 187 * @hide 188 */ 189 @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) logWatchdogRollbackOccurred(int rollbackType, String packageName, long packageVersionCode, int rollbackReason, String failingPackageName)190 public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName, 191 long packageVersionCode, int rollbackReason, String failingPackageName) { 192 synchronized (sLogLock) { 193 try { 194 IStatsManager service = getIStatsManagerLocked(); 195 if (service == null) { 196 if (DEBUG) { 197 Slog.d(TAG, "Failed to find statsd when logging event"); 198 } 199 return false; 200 } 201 202 service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName, 203 packageVersionCode, rollbackReason, failingPackageName); 204 return true; 205 } catch (RemoteException e) { 206 sService = null; 207 if (DEBUG) { 208 Slog.d(TAG, 209 "Failed to connect to StatsCompanionService when logging " 210 + "WatchdogRollbackOccurred"); 211 } 212 return false; 213 } 214 } 215 } 216 217 getIStatsManagerLocked()218 private static IStatsManager getIStatsManagerLocked() throws RemoteException { 219 if (sService != null) { 220 return sService; 221 } 222 sService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); 223 return sService; 224 } 225 226 /** 227 * Write an event to stats log using the raw format. 228 * 229 * @param buffer The encoded buffer of data to write. 230 * @param size The number of bytes from the buffer to write. 231 * @hide 232 */ 233 // TODO(b/144935988): Mark deprecated. 234 @SystemApi writeRaw(@onNull byte[] buffer, int size)235 public static void writeRaw(@NonNull byte[] buffer, int size) { 236 // TODO(b/144935988): make this no-op once clients have migrated to StatsEvent. 237 writeImpl(buffer, size, 0); 238 } 239 240 /** 241 * Write an event to stats log using the raw format. 242 * 243 * @param buffer The encoded buffer of data to write. 244 * @param size The number of bytes from the buffer to write. 245 * @param atomId The id of the atom to which the event belongs. 246 */ writeImpl(@onNull byte[] buffer, int size, int atomId)247 private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId); 248 249 /** 250 * Write an event to stats log using the raw format encapsulated in StatsEvent. 251 * After writing to stats log, release() is called on the StatsEvent object. 252 * No further action should be taken on the StatsEvent object following this call. 253 * 254 * @param statsEvent The StatsEvent object containing the encoded buffer of data to write. 255 * @hide 256 */ write(@onNull final StatsEvent statsEvent)257 public static void write(@NonNull final StatsEvent statsEvent) { 258 writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId()); 259 statsEvent.release(); 260 } 261 enforceDumpCallingPermission(Context context)262 private static void enforceDumpCallingPermission(Context context) { 263 context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission."); 264 } 265 enforcesageStatsCallingPermission(Context context)266 private static void enforcesageStatsCallingPermission(Context context) { 267 context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, 268 "Need PACKAGE_USAGE_STATS permission."); 269 } 270 } 271