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