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 package android.os;
17 
18 import java.io.ByteArrayOutputStream;
19 import java.nio.charset.StandardCharsets;
20 
21 /**
22  * Wrapper class for sending data from Android OS to StatsD.
23  *
24  * @hide
25  */
26 public final class StatsLogEventWrapper implements Parcelable {
27     private ByteArrayOutputStream mStorage = new ByteArrayOutputStream();
28 
29     // Below are constants copied from log/log.h
30     private static final int EVENT_TYPE_INT = 0;  /* int32_t */
31     private static final int EVENT_TYPE_LONG = 1; /* int64_t */
32     private static final int EVENT_TYPE_STRING = 2;
33     private static final int EVENT_TYPE_LIST = 3;
34     private static final int EVENT_TYPE_FLOAT = 4;
35 
36     // Keep this in sync with system/core/logcat/event.logtags
37     private static final int STATS_BUFFER_TAG_ID = 1937006964;
38     /**
39      * Creates a log_event that is binary-encoded as implemented in
40      * system/core/liblog/log_event_list.c; this allows us to use the same parsing logic in statsd
41      * for pushed and pulled data. The write* methods must be called in the same order as their
42      * field number. There is no checking that the correct number of write* methods is called.
43      * We also write an END_LIST character before beginning to write to parcel, but this END_LIST
44      * may be unnecessary.
45      *
46      * @param tag    The integer representing the tag for this event.
47      * @param fields The number of fields specified in this event.
48      */
StatsLogEventWrapper(long elapsedNanos, int tag, int fields)49     public StatsLogEventWrapper(long elapsedNanos, int tag, int fields) {
50         // Write four bytes from tag, starting with least-significant bit.
51         // For pulled data, this tag number is not really used. We use the same tag number as
52         // pushed ones to be consistent.
53         write4Bytes(STATS_BUFFER_TAG_ID);
54         mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
55         mStorage.write(fields + 2); // Indicate number of elements in this list. +1 for the tag
56         // The first element is the elapsed realtime.
57         writeLong(elapsedNanos);
58         // The second element is the real atom tag number
59         writeInt(tag);
60     }
61 
62     /**
63      * Boilerplate for Parcel.
64      */
65     public static final Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
66             Parcelable.Creator<StatsLogEventWrapper>() {
67                 public StatsLogEventWrapper createFromParcel(Parcel in) {
68                     return new StatsLogEventWrapper(in);
69                 }
70 
71                 public StatsLogEventWrapper[] newArray(int size) {
72                     return new StatsLogEventWrapper[size];
73                 }
74             };
75 
write4Bytes(int val)76     private void write4Bytes(int val) {
77         mStorage.write(val);
78         mStorage.write(val >>> 8);
79         mStorage.write(val >>> 16);
80         mStorage.write(val >>> 24);
81     }
82 
write8Bytes(long val)83     private void write8Bytes(long val) {
84         write4Bytes((int) (val & 0xFFFFFFFF)); // keep the lowe 32-bits
85         write4Bytes((int) (val >>> 32)); // Write the high 32-bits.
86     }
87 
88     /**
89      * Adds 32-bit integer to output.
90      */
writeInt(int val)91     public void writeInt(int val) {
92         mStorage.write(EVENT_TYPE_INT);
93         write4Bytes(val);
94     }
95 
96     /**
97      * Adds 64-bit long to output.
98      */
writeLong(long val)99     public void writeLong(long val) {
100         mStorage.write(EVENT_TYPE_LONG);
101         write8Bytes(val);
102     }
103 
104     /**
105      * Adds a 4-byte floating point value to output.
106      */
writeFloat(float val)107     public void writeFloat(float val) {
108         int v = Float.floatToIntBits(val);
109         mStorage.write(EVENT_TYPE_FLOAT);
110         write4Bytes(v);
111     }
112 
113     /**
114      * Adds a string to the output.
115      */
writeString(String val)116     public void writeString(String val) {
117         mStorage.write(EVENT_TYPE_STRING);
118         write4Bytes(val.length());
119         byte[] bytes = val.getBytes(StandardCharsets.UTF_8);
120         mStorage.write(bytes, 0, bytes.length);
121     }
122 
StatsLogEventWrapper(Parcel in)123     private StatsLogEventWrapper(Parcel in) {
124         readFromParcel(in);
125     }
126 
127     /**
128      * Writes the stored fields to a byte array. Will first write a new-line character to denote
129      * END_LIST before writing contents to byte array.
130      */
writeToParcel(Parcel out, int flags)131     public void writeToParcel(Parcel out, int flags) {
132         mStorage.write(10); // new-line character is same as END_LIST
133         out.writeByteArray(mStorage.toByteArray());
134     }
135 
136     /**
137      * Not implemented.
138      */
readFromParcel(Parcel in)139     public void readFromParcel(Parcel in) {
140         // Not needed since this java class is for sending to statsd only.
141     }
142 
143     /**
144      * Boilerplate for Parcel.
145      */
describeContents()146     public int describeContents() {
147         return 0;
148     }
149 }
150