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 * 183 * @return True if the log request was sent to statsd. 184 * 185 * @hide 186 */ 187 @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS}) logWatchdogRollbackOccurred(int rollbackType, String packageName, long packageVersionCode)188 public static boolean logWatchdogRollbackOccurred(int rollbackType, String packageName, 189 long packageVersionCode) { 190 synchronized (sLogLock) { 191 try { 192 IStatsManager service = getIStatsManagerLocked(); 193 if (service == null) { 194 if (DEBUG) { 195 Slog.d(TAG, "Failed to find statsd when logging event"); 196 } 197 return false; 198 } 199 200 service.sendWatchdogRollbackOccurredAtom(rollbackType, packageName, 201 packageVersionCode); 202 return true; 203 } catch (RemoteException e) { 204 sService = null; 205 if (DEBUG) { 206 Slog.d(TAG, 207 "Failed to connect to StatsCompanionService when logging " 208 + "WatchdogRollbackOccurred"); 209 } 210 return false; 211 } 212 } 213 } 214 215 getIStatsManagerLocked()216 private static IStatsManager getIStatsManagerLocked() throws RemoteException { 217 if (sService != null) { 218 return sService; 219 } 220 sService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats")); 221 return sService; 222 } 223 224 /** 225 * Write an event to stats log using the raw format. 226 * 227 * @param buffer The encoded buffer of data to write.. 228 * @param size The number of bytes from the buffer to write. 229 * @hide 230 */ 231 @SystemApi writeRaw(@onNull byte[] buffer, int size)232 public static native void writeRaw(@NonNull byte[] buffer, int size); 233 enforceDumpCallingPermission(Context context)234 private static void enforceDumpCallingPermission(Context context) { 235 context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission."); 236 } 237 enforcesageStatsCallingPermission(Context context)238 private static void enforcesageStatsCallingPermission(Context context) { 239 context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS, 240 "Need PACKAGE_USAGE_STATS permission."); 241 } 242 } 243