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#include "hash.h"
18#include "stats_log_util.h"
19
20#include <logd/LogEvent.h>
21#include <private/android_filesystem_config.h>
22#include <utils/Log.h>
23#include <set>
24#include <stack>
25#include <utils/Log.h>
26#include <utils/SystemClock.h>
27
28using android::util::FIELD_COUNT_REPEATED;
29using android::util::FIELD_TYPE_BOOL;
30using android::util::FIELD_TYPE_FLOAT;
31using android::util::FIELD_TYPE_INT32;
32using android::util::FIELD_TYPE_INT64;
33using android::util::FIELD_TYPE_UINT64;
34using android::util::FIELD_TYPE_FIXED64;
35using android::util::FIELD_TYPE_MESSAGE;
36using android::util::FIELD_TYPE_STRING;
37using android::util::ProtoOutputStream;
38
39namespace android {
40namespace os {
41namespace statsd {
42
43// for DimensionsValue Proto
44const int DIMENSIONS_VALUE_FIELD = 1;
45const int DIMENSIONS_VALUE_VALUE_STR = 2;
46const int DIMENSIONS_VALUE_VALUE_INT = 3;
47const int DIMENSIONS_VALUE_VALUE_LONG = 4;
48// const int DIMENSIONS_VALUE_VALUE_BOOL = 5; // logd doesn't have bool data type.
49const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
50const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
51const int DIMENSIONS_VALUE_VALUE_STR_HASH = 8;
52
53const int DIMENSIONS_VALUE_TUPLE_VALUE = 1;
54
55// for PulledAtomStats proto
56const int FIELD_ID_PULLED_ATOM_STATS = 10;
57const int FIELD_ID_PULL_ATOM_ID = 1;
58const int FIELD_ID_TOTAL_PULL = 2;
59const int FIELD_ID_TOTAL_PULL_FROM_CACHE = 3;
60const int FIELD_ID_MIN_PULL_INTERVAL_SEC = 4;
61
62namespace {
63
64void writeDimensionToProtoHelper(const std::vector<FieldValue>& dims, size_t* index, int depth,
65                                 int prefix, std::set<string> *str_set,
66                                 ProtoOutputStream* protoOutput) {
67    size_t count = dims.size();
68    while (*index < count) {
69        const auto& dim = dims[*index];
70        const int valueDepth = dim.mField.getDepth();
71        const int valuePrefix = dim.mField.getPrefix(depth);
72        const int fieldNum = dim.mField.getPosAtDepth(depth);
73        if (valueDepth > 2) {
74            ALOGE("Depth > 2 not supported");
75            return;
76        }
77
78        if (depth == valueDepth && valuePrefix == prefix) {
79            uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
80                                                 DIMENSIONS_VALUE_TUPLE_VALUE);
81            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
82            switch (dim.mValue.getType()) {
83                case INT:
84                    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
85                                       dim.mValue.int_value);
86                    break;
87                case LONG:
88                    protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
89                                       (long long)dim.mValue.long_value);
90                    break;
91                case FLOAT:
92                    protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
93                                       dim.mValue.float_value);
94                    break;
95                case STRING:
96                    if (str_set == nullptr) {
97                        protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
98                                           dim.mValue.str_value);
99                    } else {
100                        str_set->insert(dim.mValue.str_value);
101                        protoOutput->write(
102                                FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
103                                (long long)Hash64(dim.mValue.str_value));
104                    }
105                    break;
106                default:
107                    break;
108            }
109            if (token != 0) {
110                protoOutput->end(token);
111            }
112            (*index)++;
113        } else if (valueDepth > depth && valuePrefix == prefix) {
114            // Writing the sub tree
115            uint64_t dimensionToken = protoOutput->start(
116                    FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
117            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
118            uint64_t tupleToken =
119                    protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
120            writeDimensionToProtoHelper(dims, index, valueDepth, dim.mField.getPrefix(valueDepth),
121                                        str_set, protoOutput);
122            protoOutput->end(tupleToken);
123            protoOutput->end(dimensionToken);
124        } else {
125            // Done with the prev sub tree
126            return;
127        }
128    }
129}
130
131void writeDimensionLeafToProtoHelper(const std::vector<FieldValue>& dims,
132                                     const int dimensionLeafField,
133                                     size_t* index, int depth,
134                                     int prefix, std::set<string> *str_set,
135                                     ProtoOutputStream* protoOutput) {
136    size_t count = dims.size();
137    while (*index < count) {
138        const auto& dim = dims[*index];
139        const int valueDepth = dim.mField.getDepth();
140        const int valuePrefix = dim.mField.getPrefix(depth);
141        if (valueDepth > 2) {
142            ALOGE("Depth > 2 not supported");
143            return;
144        }
145
146        if (depth == valueDepth && valuePrefix == prefix) {
147            uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
148                                                dimensionLeafField);
149            switch (dim.mValue.getType()) {
150                case INT:
151                    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
152                                       dim.mValue.int_value);
153                    break;
154                case LONG:
155                    protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
156                                       (long long)dim.mValue.long_value);
157                    break;
158                case FLOAT:
159                    protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
160                                       dim.mValue.float_value);
161                    break;
162                case STRING:
163                    if (str_set == nullptr) {
164                        protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
165                                           dim.mValue.str_value);
166                    } else {
167                        str_set->insert(dim.mValue.str_value);
168                        protoOutput->write(
169                                FIELD_TYPE_UINT64 | DIMENSIONS_VALUE_VALUE_STR_HASH,
170                                (long long)Hash64(dim.mValue.str_value));
171                    }
172                    break;
173                default:
174                    break;
175            }
176            if (token != 0) {
177                protoOutput->end(token);
178            }
179            (*index)++;
180        } else if (valueDepth > depth && valuePrefix == prefix) {
181            writeDimensionLeafToProtoHelper(dims, dimensionLeafField,
182                                            index, valueDepth, dim.mField.getPrefix(valueDepth),
183                                            str_set, protoOutput);
184        } else {
185            // Done with the prev sub tree
186            return;
187        }
188    }
189}
190
191void writeDimensionPathToProtoHelper(const std::vector<Matcher>& fieldMatchers,
192                                     size_t* index, int depth, int prefix,
193                                     ProtoOutputStream* protoOutput) {
194    size_t count = fieldMatchers.size();
195    while (*index < count) {
196        const Field& field = fieldMatchers[*index].mMatcher;
197        const int valueDepth = field.getDepth();
198        const int valuePrefix = field.getPrefix(depth);
199        const int fieldNum = field.getPosAtDepth(depth);
200        if (valueDepth > 2) {
201            ALOGE("Depth > 2 not supported");
202            return;
203        }
204
205        if (depth == valueDepth && valuePrefix == prefix) {
206            uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
207                                                 DIMENSIONS_VALUE_TUPLE_VALUE);
208            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
209            if (token != 0) {
210                protoOutput->end(token);
211            }
212            (*index)++;
213        } else if (valueDepth > depth && valuePrefix == prefix) {
214            // Writing the sub tree
215            uint64_t dimensionToken = protoOutput->start(
216                    FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | DIMENSIONS_VALUE_TUPLE_VALUE);
217            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, fieldNum);
218            uint64_t tupleToken =
219                    protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
220            writeDimensionPathToProtoHelper(fieldMatchers, index, valueDepth,
221                                            field.getPrefix(valueDepth), protoOutput);
222            protoOutput->end(tupleToken);
223            protoOutput->end(dimensionToken);
224        } else {
225            // Done with the prev sub tree
226            return;
227        }
228    }
229}
230
231}  // namespace
232
233void writeDimensionToProto(const HashableDimensionKey& dimension, std::set<string> *str_set,
234                           ProtoOutputStream* protoOutput) {
235    if (dimension.getValues().size() == 0) {
236        return;
237    }
238    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
239                       dimension.getValues()[0].mField.getTag());
240    uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
241    size_t index = 0;
242    writeDimensionToProtoHelper(dimension.getValues(), &index, 0, 0, str_set, protoOutput);
243    protoOutput->end(topToken);
244}
245
246void writeDimensionLeafNodesToProto(const HashableDimensionKey& dimension,
247                                    const int dimensionLeafFieldId,
248                                    std::set<string> *str_set,
249                                    ProtoOutputStream* protoOutput) {
250    if (dimension.getValues().size() == 0) {
251        return;
252    }
253    size_t index = 0;
254    writeDimensionLeafToProtoHelper(dimension.getValues(), dimensionLeafFieldId,
255                                    &index, 0, 0, str_set, protoOutput);
256}
257
258void writeDimensionPathToProto(const std::vector<Matcher>& fieldMatchers,
259                               ProtoOutputStream* protoOutput) {
260    if (fieldMatchers.size() == 0) {
261        return;
262    }
263    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD,
264                       fieldMatchers[0].mMatcher.getTag());
265    uint64_t topToken = protoOutput->start(FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
266    size_t index = 0;
267    writeDimensionPathToProtoHelper(fieldMatchers, &index, 0, 0, protoOutput);
268    protoOutput->end(topToken);
269}
270
271// Supported Atoms format
272// XYZ_Atom {
273//     repeated SubMsg field_1 = 1;
274//     SubMsg2 field_2 = 2;
275//     int32/float/string/int63 field_3 = 3;
276// }
277// logd's msg format, doesn't allow us to distinguish between the 2 cases below
278// Case (1):
279// Atom {
280//   SubMsg {
281//     int i = 1;
282//     int j = 2;
283//   }
284//   repeated SubMsg
285// }
286//
287// and case (2):
288// Atom {
289//   SubMsg {
290//     repeated int i = 1;
291//     repeated int j = 2;
292//   }
293//   optional SubMsg = 1;
294// }
295//
296//
297void writeFieldValueTreeToStreamHelper(const std::vector<FieldValue>& dims, size_t* index,
298                                       int depth, int prefix, ProtoOutputStream* protoOutput) {
299    size_t count = dims.size();
300    while (*index < count) {
301        const auto& dim = dims[*index];
302        const int valueDepth = dim.mField.getDepth();
303        const int valuePrefix = dim.mField.getPrefix(depth);
304        const int fieldNum = dim.mField.getPosAtDepth(depth);
305        if (valueDepth > 2) {
306            ALOGE("Depth > 2 not supported");
307            return;
308        }
309
310        if (depth == valueDepth && valuePrefix == prefix) {
311            switch (dim.mValue.getType()) {
312                case INT:
313                    protoOutput->write(FIELD_TYPE_INT32 | fieldNum, dim.mValue.int_value);
314                    break;
315                case LONG:
316                    protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
317                                       (long long)dim.mValue.long_value);
318                    break;
319                case FLOAT:
320                    protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, dim.mValue.float_value);
321                    break;
322                case STRING:
323                    protoOutput->write(FIELD_TYPE_STRING | fieldNum, dim.mValue.str_value);
324                    break;
325                default:
326                    break;
327            }
328            (*index)++;
329        } else if (valueDepth > depth && valuePrefix == prefix) {
330            // Writing the sub tree
331            uint64_t msg_token = 0ULL;
332            if (valueDepth == depth + 2) {
333                msg_token =
334                        protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
335            } else if (valueDepth == depth + 1) {
336                msg_token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
337            }
338            // Directly jump to the leaf value because the repeated position field is implied
339            // by the position of the sub msg in the parent field.
340            writeFieldValueTreeToStreamHelper(dims, index, valueDepth,
341                                              dim.mField.getPrefix(valueDepth), protoOutput);
342            if (msg_token != 0) {
343                protoOutput->end(msg_token);
344            }
345        } else {
346            // Done with the prev sub tree
347            return;
348        }
349    }
350}
351
352void writeFieldValueTreeToStream(int tagId, const std::vector<FieldValue>& values,
353                                 util::ProtoOutputStream* protoOutput) {
354    uint64_t atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | tagId);
355
356    size_t index = 0;
357    writeFieldValueTreeToStreamHelper(values, &index, 0, 0, protoOutput);
358    protoOutput->end(atomToken);
359}
360
361int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
362    int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
363    if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
364        uid != AID_ROOT) {
365        bucketSizeMillis = 5 * 60 * 1000LL;
366    }
367    return bucketSizeMillis;
368}
369
370int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
371    switch (unit) {
372        case ONE_MINUTE:
373            return 60 * 1000LL;
374        case FIVE_MINUTES:
375            return 5 * 60 * 1000LL;
376        case TEN_MINUTES:
377            return 10 * 60 * 1000LL;
378        case THIRTY_MINUTES:
379            return 30 * 60 * 1000LL;
380        case ONE_HOUR:
381            return 60 * 60 * 1000LL;
382        case THREE_HOURS:
383            return 3 * 60 * 60 * 1000LL;
384        case SIX_HOURS:
385            return 6 * 60 * 60 * 1000LL;
386        case TWELVE_HOURS:
387            return 12 * 60 * 60 * 1000LL;
388        case ONE_DAY:
389            return 24 * 60 * 60 * 1000LL;
390        case CTS:
391            return 1000;
392        case TIME_UNIT_UNSPECIFIED:
393        default:
394            return -1;
395    }
396}
397
398void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
399                              util::ProtoOutputStream* protoOutput) {
400    uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
401                                         FIELD_COUNT_REPEATED);
402    protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_PULL_ATOM_ID, (int32_t)pair.first);
403    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL, (long long)pair.second.totalPull);
404    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_TOTAL_PULL_FROM_CACHE,
405                       (long long)pair.second.totalPullFromCache);
406    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_PULL_INTERVAL_SEC,
407                       (long long)pair.second.minPullIntervalSec);
408    protoOutput->end(token);
409}
410
411int64_t getElapsedRealtimeNs() {
412    return ::android::elapsedRealtimeNano();
413}
414
415int64_t getElapsedRealtimeSec() {
416    return ::android::elapsedRealtimeNano() / NS_PER_SEC;
417}
418
419int64_t getElapsedRealtimeMillis() {
420    return ::android::elapsedRealtime();
421}
422
423int64_t getWallClockNs() {
424    return time(nullptr) * NS_PER_SEC;
425}
426
427int64_t getWallClockSec() {
428    return time(nullptr);
429}
430
431int64_t getWallClockMillis() {
432    return time(nullptr) * MS_PER_SEC;
433}
434
435int64_t truncateTimestampNsToFiveMinutes(int64_t timestampNs) {
436    return timestampNs / NS_PER_SEC / (5 * 60) * NS_PER_SEC * (5 * 60);
437}
438
439int64_t NanoToMillis(const int64_t nano) {
440    return nano / 1000000;
441}
442
443int64_t MillisToNano(const int64_t millis) {
444    return millis * 1000000;
445}
446
447}  // namespace statsd
448}  // namespace os
449}  // namespace android
450