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.IStatsd;
28 import android.os.Process;
29 import android.util.proto.ProtoOutputStream;
30 
31 import com.android.internal.statsd.StatsdStatsLog;
32 
33 /**
34  * StatsLog provides an API for developers to send events to statsd. The events can be used to
35  * define custom metrics inside statsd.
36  */
37 public final class StatsLog {
38 
39     // Load JNI library
40     static {
41         System.loadLibrary("stats_jni");
42     }
43     private static final String TAG = "StatsLog";
44     private static final boolean DEBUG = false;
45     private static final int EXPERIMENT_IDS_FIELD_ID = 1;
46 
StatsLog()47     private StatsLog() {
48     }
49 
50     /**
51      * Logs a start event.
52      *
53      * @param label developer-chosen label.
54      * @return True if the log request was sent to statsd.
55      */
logStart(int label)56     public static boolean logStart(int label) {
57         int callingUid = Process.myUid();
58         StatsdStatsLog.write(
59                 StatsdStatsLog.APP_BREADCRUMB_REPORTED,
60                 callingUid,
61                 label,
62                 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
63         return true;
64     }
65 
66     /**
67      * Logs a stop event.
68      *
69      * @param label developer-chosen label.
70      * @return True if the log request was sent to statsd.
71      */
logStop(int label)72     public static boolean logStop(int label) {
73         int callingUid = Process.myUid();
74         StatsdStatsLog.write(
75                 StatsdStatsLog.APP_BREADCRUMB_REPORTED,
76                 callingUid,
77                 label,
78                 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
79         return true;
80     }
81 
82     /**
83      * Logs an event that does not represent a start or stop boundary.
84      *
85      * @param label developer-chosen label.
86      * @return True if the log request was sent to statsd.
87      */
logEvent(int label)88     public static boolean logEvent(int label) {
89         int callingUid = Process.myUid();
90         StatsdStatsLog.write(
91                 StatsdStatsLog.APP_BREADCRUMB_REPORTED,
92                 callingUid,
93                 label,
94                 StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
95         return true;
96     }
97 
98     /**
99      * Logs an event for binary push for module updates.
100      *
101      * @param trainName        name of install train.
102      * @param trainVersionCode version code of the train.
103      * @param options          optional flags about this install.
104      *                         The last 3 bits indicate options:
105      *                             0x01: FLAG_REQUIRE_STAGING
106      *                             0x02: FLAG_ROLLBACK_ENABLED
107      *                             0x04: FLAG_REQUIRE_LOW_LATENCY_MONITOR
108      * @param state            current install state. Defined as State enums in
109      *                         BinaryPushStateChanged atom in
110      *                         frameworks/base/cmds/statsd/src/atoms.proto
111      * @param experimentIds    experiment ids.
112      * @return True if the log request was sent to statsd.
113      */
114     @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
logBinaryPushStateChanged(@onNull String trainName, long trainVersionCode, int options, int state, @NonNull long[] experimentIds)115     public static boolean logBinaryPushStateChanged(@NonNull String trainName,
116             long trainVersionCode, int options, int state,
117             @NonNull long[] experimentIds) {
118         ProtoOutputStream proto = new ProtoOutputStream();
119         for (long id : experimentIds) {
120             proto.write(
121                     ProtoOutputStream.FIELD_TYPE_INT64
122                     | ProtoOutputStream.FIELD_COUNT_REPEATED
123                     | EXPERIMENT_IDS_FIELD_ID,
124                     id);
125         }
126         StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED,
127                 trainName,
128                 trainVersionCode,
129                 (options & IStatsd.FLAG_REQUIRE_STAGING) > 0,
130                 (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0,
131                 (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0,
132                 state,
133                 proto.getBytes(),
134                 0,
135                 0,
136                 false);
137         return true;
138     }
139 
140     /**
141      * Write an event to stats log using the raw format.
142      *
143      * @param buffer    The encoded buffer of data to write.
144      * @param size      The number of bytes from the buffer to write.
145      * @hide
146      */
147     // TODO(b/144935988): Mark deprecated.
148     @SystemApi
writeRaw(@onNull byte[] buffer, int size)149     public static void writeRaw(@NonNull byte[] buffer, int size) {
150         // TODO(b/144935988): make this no-op once clients have migrated to StatsEvent.
151         writeImpl(buffer, size, 0);
152     }
153 
154     /**
155      * Write an event to stats log using the raw format.
156      *
157      * @param buffer    The encoded buffer of data to write.
158      * @param size      The number of bytes from the buffer to write.
159      * @param atomId    The id of the atom to which the event belongs.
160      */
writeImpl(@onNull byte[] buffer, int size, int atomId)161     private static native void writeImpl(@NonNull byte[] buffer, int size, int atomId);
162 
163     /**
164      * Write an event to stats log using the raw format encapsulated in StatsEvent.
165      * After writing to stats log, release() is called on the StatsEvent object.
166      * No further action should be taken on the StatsEvent object following this call.
167      *
168      * @param statsEvent    The StatsEvent object containing the encoded buffer of data to write.
169      * @hide
170      */
171     @SystemApi
write(@onNull final StatsEvent statsEvent)172     public static void write(@NonNull final StatsEvent statsEvent) {
173         writeImpl(statsEvent.getBytes(), statsEvent.getNumBytes(), statsEvent.getAtomId());
174         statsEvent.release();
175     }
176 
enforceDumpCallingPermission(Context context)177     private static void enforceDumpCallingPermission(Context context) {
178         context.enforceCallingPermission(android.Manifest.permission.DUMP, "Need DUMP permission.");
179     }
180 
enforcesageStatsCallingPermission(Context context)181     private static void enforcesageStatsCallingPermission(Context context) {
182         context.enforceCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS,
183                 "Need PACKAGE_USAGE_STATS permission.");
184     }
185 }
186