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 #pragma once
18 
19 #include "FieldValue.h"
20 
21 #include <android/util/ProtoOutputStream.h>
22 #include <private/android_logger.h>
23 
24 #include <string>
25 #include <vector>
26 
27 namespace android {
28 namespace os {
29 namespace statsd {
30 
31 struct InstallTrainInfo {
32     int64_t trainVersionCode;
33     std::string trainName;
34     int32_t status;
35     std::vector<int64_t> experimentIds;
36     bool requiresStaging;
37     bool rollbackEnabled;
38     bool requiresLowLatencyMonitor;
39 };
40 
41 /**
42  * This class decodes the structured, serialized encoding of an atom into a
43  * vector of FieldValues.
44  */
45 class LogEvent {
46 public:
47     /**
48      * \param uid user id of the logging caller
49      * \param pid process id of the logging caller
50      */
51     explicit LogEvent(int32_t uid, int32_t pid);
52 
53     /**
54      * Parses the atomId, timestamp, and vector of values from a buffer
55      * containing the StatsEvent/AStatsEvent encoding of an atom.
56      *
57      * \param buf a buffer that begins at the start of the serialized atom (it
58      * should not include the android_log_header_t or the StatsEventTag)
59      * \param len size of the buffer
60      *
61      * \return success of the initialization
62      */
63     bool parseBuffer(uint8_t* buf, size_t len);
64 
65     // Constructs a BinaryPushStateChanged LogEvent from API call.
66     explicit LogEvent(const std::string& trainName, int64_t trainVersionCode, bool requiresStaging,
67                       bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
68                       const std::vector<uint8_t>& experimentIds, int32_t userId);
69 
70     explicit LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
71                       const InstallTrainInfo& installTrainInfo);
72 
~LogEvent()73     ~LogEvent() {}
74 
75     /**
76      * Get the timestamp associated with this event.
77      */
GetLogdTimestampNs()78     inline int64_t GetLogdTimestampNs() const { return mLogdTimestampNs; }
GetElapsedTimestampNs()79     inline int64_t GetElapsedTimestampNs() const { return mElapsedTimestampNs; }
80 
81     /**
82      * Get the tag for this event.
83      */
GetTagId()84     inline int GetTagId() const { return mTagId; }
85 
86     /**
87      * Get the uid of the logging client.
88      * Returns -1 if the uid is unknown/has not been set.
89      */
GetUid()90     inline int32_t GetUid() const { return mLogUid; }
91 
92     /**
93      * Get the pid of the logging client.
94      * Returns -1 if the pid is unknown/has not been set.
95      */
GetPid()96     inline int32_t GetPid() const { return mLogPid; }
97 
98     /**
99      * Get the nth value, starting at 1.
100      *
101      * Returns BAD_INDEX if the index is larger than the number of elements.
102      * Returns BAD_TYPE if the index is available but the data is the wrong type.
103      */
104     int64_t GetLong(size_t key, status_t* err) const;
105     int GetInt(size_t key, status_t* err) const;
106     const char* GetString(size_t key, status_t* err) const;
107     bool GetBool(size_t key, status_t* err) const;
108     float GetFloat(size_t key, status_t* err) const;
109     std::vector<uint8_t> GetStorage(size_t key, status_t* err) const;
110 
111     /**
112      * Return a string representation of this event.
113      */
114     std::string ToString() const;
115 
116     /**
117      * Write this object to a ProtoOutputStream.
118      */
119     void ToProto(android::util::ProtoOutputStream& out) const;
120 
121     /**
122      * Set elapsed timestamp if the original timestamp is missing.
123      */
setElapsedTimestampNs(int64_t timestampNs)124     void setElapsedTimestampNs(int64_t timestampNs) {
125         mElapsedTimestampNs = timestampNs;
126     }
127 
128     /**
129      * Set the timestamp if the original logd timestamp is missing.
130      */
setLogdWallClockTimestampNs(int64_t timestampNs)131     void setLogdWallClockTimestampNs(int64_t timestampNs) {
132         mLogdTimestampNs = timestampNs;
133     }
134 
size()135     inline int size() const {
136         return mValues.size();
137     }
138 
getValues()139     const std::vector<FieldValue>& getValues() const {
140         return mValues;
141     }
142 
getMutableValues()143     std::vector<FieldValue>* getMutableValues() {
144         return &mValues;
145     }
146 
147     // Default value = false
shouldTruncateTimestamp()148     inline bool shouldTruncateTimestamp() const {
149         return mTruncateTimestamp;
150     }
151 
152     // Returns the index of the uid field within the FieldValues vector if the
153     // uid exists. If there is no uid field, returns -1.
154     //
155     // If the index within the atom definition is desired, do the following:
156     //    int vectorIndex = LogEvent.getUidFieldIndex();
157     //    if (vectorIndex != -1) {
158     //        FieldValue& v = LogEvent.getValues()[vectorIndex];
159     //        int atomIndex = v.mField.getPosAtDepth(0);
160     //    }
161     // Note that atomIndex is 1-indexed.
getUidFieldIndex()162     inline int getUidFieldIndex() {
163         return static_cast<int>(mUidFieldIndex);
164     }
165 
166     // Returns whether this LogEvent has an AttributionChain.
167     // If it does and indexRange is not a nullptr, populate indexRange with the start and end index
168     // of the AttributionChain within mValues.
169     bool hasAttributionChain(std::pair<int, int>* indexRange = nullptr) const;
170 
171     // Returns the index of the exclusive state field within the FieldValues vector if
172     // an exclusive state exists. If there is no exclusive state field, returns -1.
173     //
174     // If the index within the atom definition is desired, do the following:
175     //    int vectorIndex = LogEvent.getExclusiveStateFieldIndex();
176     //    if (vectorIndex != -1) {
177     //        FieldValue& v = LogEvent.getValues()[vectorIndex];
178     //        int atomIndex = v.mField.getPosAtDepth(0);
179     //    }
180     // Note that atomIndex is 1-indexed.
getExclusiveStateFieldIndex()181     inline int getExclusiveStateFieldIndex() const {
182         return static_cast<int>(mExclusiveStateFieldIndex);
183     }
184 
185     // If a reset state is not sent in the StatsEvent, returns -1. Note that a
186     // reset state is sent if and only if a reset should be triggered.
getResetState()187     inline int getResetState() const {
188         return mResetState;
189     }
190 
makeCopy()191     inline LogEvent makeCopy() {
192         return LogEvent(*this);
193     }
194 
195     template <class T>
updateValue(size_t key,T & value,Type type)196     status_t updateValue(size_t key, T& value, Type type) {
197         int field = getSimpleField(key);
198         for (auto& fieldValue : mValues) {
199             if (fieldValue.mField.getField() == field) {
200                 if (fieldValue.mValue.getType() == type) {
201                     fieldValue.mValue = Value(value);
202                    return OK;
203                } else {
204                    return BAD_TYPE;
205                 }
206             }
207         }
208         return BAD_INDEX;
209     }
210 
isValid()211     bool isValid() const {
212         return mValid;
213     }
214 
215 private:
216     /**
217      * Only use this if copy is absolutely needed.
218      */
219     LogEvent(const LogEvent&) = default;
220 
221     void parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
222     void parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
223     void parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
224     void parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
225     void parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
226     void parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
227     void parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
228     void parseAttributionChain(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations);
229 
230     void parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex = -1);
231     void parseIsUidAnnotation(uint8_t annotationType);
232     void parseTruncateTimestampAnnotation(uint8_t annotationType);
233     void parsePrimaryFieldAnnotation(uint8_t annotationType);
234     void parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex);
235     void parseExclusiveStateAnnotation(uint8_t annotationType);
236     void parseTriggerStateResetAnnotation(uint8_t annotationType);
237     void parseStateNestedAnnotation(uint8_t annotationType);
238     bool checkPreviousValueType(Type expected);
239 
240     /**
241      * The below two variables are only valid during the execution of
242      * parseBuffer. There are no guarantees about the state of these variables
243      * before/after.
244      */
245     uint8_t* mBuf;
246     uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
247 
248     bool mValid = true; // stores whether the event we received from the socket is valid
249 
250     /**
251      * Side-effects:
252      *    If there is enough space in buffer to read value of type T
253      *        - move mBuf past the value that was just read
254      *        - decrement mRemainingLen by size of T
255      *    Else
256      *        - set mValid to false
257      */
258     template <class T>
readNextValue()259     T readNextValue() {
260         T value;
261         if (mRemainingLen < sizeof(T)) {
262             mValid = false;
263             value = 0; // all primitive types can successfully cast 0
264         } else {
265             // When alignof(T) == 1, hopefully the compiler can optimize away
266             // this conditional as always true.
267             if ((reinterpret_cast<uintptr_t>(mBuf) % alignof(T)) == 0) {
268                 // We're properly aligned, and can safely make this assignment.
269                 value = *((T*)mBuf);
270             } else {
271                 // We need to use memcpy.  It's slower, but safe.
272                 memcpy(&value, mBuf, sizeof(T));
273             }
274             mBuf += sizeof(T);
275             mRemainingLen -= sizeof(T);
276         }
277         return value;
278     }
279 
280     template <class T>
addToValues(int32_t * pos,int32_t depth,T & value,bool * last)281     void addToValues(int32_t* pos, int32_t depth, T& value, bool* last) {
282         Field f = Field(mTagId, pos, depth);
283         // do not decorate last position at depth 0
284         for (int i = 1; i < depth; i++) {
285             if (last[i]) f.decorateLastPos(i);
286         }
287 
288         Value v = Value(value);
289         mValues.push_back(FieldValue(f, v));
290     }
291 
292     uint8_t getTypeId(uint8_t typeInfo);
293     uint8_t getNumAnnotations(uint8_t typeInfo);
294 
295     // The items are naturally sorted in DFS order as we read them. this allows us to do fast
296     // matching.
297     std::vector<FieldValue> mValues;
298 
299     // The timestamp set by the logd.
300     int64_t mLogdTimestampNs;
301 
302     // The elapsed timestamp set by statsd log writer.
303     int64_t mElapsedTimestampNs;
304 
305     // The atom tag of the event (defaults to 0 if client does not
306     // appropriately set the atom id).
307     int mTagId = 0;
308 
309     // The uid of the logging client (defaults to -1).
310     int32_t mLogUid = -1;
311 
312     // The pid of the logging client (defaults to -1).
313     int32_t mLogPid = -1;
314 
315     // Annotations
316     bool mTruncateTimestamp = false;
317     int mResetState = -1;
318 
319     // Indexes within the FieldValue vector can be stored in 7 bits because
320     // that's the assumption enforced by the encoding used in FieldValue.
321     int8_t mUidFieldIndex = -1;
322     int8_t mAttributionChainStartIndex = -1;
323     int8_t mAttributionChainEndIndex = -1;
324     int8_t mExclusiveStateFieldIndex = -1;
325 };
326 
327 void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
328 
329 }  // namespace statsd
330 }  // namespace os
331 }  // namespace android
332