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 */
16package android.os;
17
18import java.io.ByteArrayOutputStream;
19import java.nio.charset.StandardCharsets;
20
21/**
22 * Wrapper class for sending data from Android OS to StatsD.
23 *
24 * @hide
25 */
26public 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     */
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
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
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
146    public int describeContents() {
147        return 0;
148    }
149}
150