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 #define DEBUG false // STOPSHIP if true
18 #include "logd/LogEvent.h"
19
20 #include <android-base/stringprintf.h>
21 #include <android/binder_ibinder.h>
22 #include <log/log.h>
23 #include <private/android_filesystem_config.h>
24
25 #include "annotations.h"
26 #include "stats_log_util.h"
27 #include "statslog_statsd.h"
28
29 namespace android {
30 namespace os {
31 namespace statsd {
32
33 // for TrainInfo experiment id serialization
34 const int FIELD_ID_EXPERIMENT_ID = 1;
35
36 using namespace android::util;
37 using android::base::StringPrintf;
38 using android::util::ProtoOutputStream;
39 using std::string;
40 using std::vector;
41
42 // stats_event.h socket types. Keep in sync.
43 /* ERRORS */
44 #define ERROR_NO_TIMESTAMP 0x1
45 #define ERROR_NO_ATOM_ID 0x2
46 #define ERROR_OVERFLOW 0x4
47 #define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8
48 #define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10
49 #define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20
50 #define ERROR_INVALID_ANNOTATION_ID 0x40
51 #define ERROR_ANNOTATION_ID_TOO_LARGE 0x80
52 #define ERROR_TOO_MANY_ANNOTATIONS 0x100
53 #define ERROR_TOO_MANY_FIELDS 0x200
54 #define ERROR_INVALID_VALUE_TYPE 0x400
55 #define ERROR_STRING_NOT_NULL_TERMINATED 0x800
56
57 /* TYPE IDS */
58 #define INT32_TYPE 0x00
59 #define INT64_TYPE 0x01
60 #define STRING_TYPE 0x02
61 #define LIST_TYPE 0x03
62 #define FLOAT_TYPE 0x04
63 #define BOOL_TYPE 0x05
64 #define BYTE_ARRAY_TYPE 0x06
65 #define OBJECT_TYPE 0x07
66 #define KEY_VALUE_PAIRS_TYPE 0x08
67 #define ATTRIBUTION_CHAIN_TYPE 0x09
68 #define ERROR_TYPE 0x0F
69
LogEvent(int32_t uid,int32_t pid)70 LogEvent::LogEvent(int32_t uid, int32_t pid)
71 : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) {
72 }
73
LogEvent(const string & trainName,int64_t trainVersionCode,bool requiresStaging,bool rollbackEnabled,bool requiresLowLatencyMonitor,int32_t state,const std::vector<uint8_t> & experimentIds,int32_t userId)74 LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging,
75 bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state,
76 const std::vector<uint8_t>& experimentIds, int32_t userId) {
77 mLogdTimestampNs = getWallClockNs();
78 mElapsedTimestampNs = getElapsedRealtimeNs();
79 mTagId = util::BINARY_PUSH_STATE_CHANGED;
80 mLogUid = AIBinder_getCallingUid();
81 mLogPid = AIBinder_getCallingPid();
82
83 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
84 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
85 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
86 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
87 mValues.push_back(
88 FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
89 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
90 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
91 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
92 }
93
LogEvent(int64_t wallClockTimestampNs,int64_t elapsedTimestampNs,const InstallTrainInfo & trainInfo)94 LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
95 const InstallTrainInfo& trainInfo) {
96 mLogdTimestampNs = wallClockTimestampNs;
97 mElapsedTimestampNs = elapsedTimestampNs;
98 mTagId = util::TRAIN_INFO;
99
100 mValues.push_back(
101 FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
102 std::vector<uint8_t> experimentIdsProto;
103 writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto);
104 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto)));
105 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName)));
106 mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
107 }
108
parseInt32(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)109 void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
110 int32_t value = readNextValue<int32_t>();
111 addToValues(pos, depth, value, last);
112 parseAnnotations(numAnnotations);
113 }
114
parseInt64(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)115 void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
116 int64_t value = readNextValue<int64_t>();
117 addToValues(pos, depth, value, last);
118 parseAnnotations(numAnnotations);
119 }
120
parseString(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)121 void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
122 int32_t numBytes = readNextValue<int32_t>();
123 if ((uint32_t)numBytes > mRemainingLen) {
124 mValid = false;
125 return;
126 }
127
128 string value = string((char*)mBuf, numBytes);
129 mBuf += numBytes;
130 mRemainingLen -= numBytes;
131 addToValues(pos, depth, value, last);
132 parseAnnotations(numAnnotations);
133 }
134
parseFloat(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)135 void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
136 float value = readNextValue<float>();
137 addToValues(pos, depth, value, last);
138 parseAnnotations(numAnnotations);
139 }
140
parseBool(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)141 void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
142 // cast to int32_t because FieldValue does not support bools
143 int32_t value = (int32_t)readNextValue<uint8_t>();
144 addToValues(pos, depth, value, last);
145 parseAnnotations(numAnnotations);
146 }
147
parseByteArray(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)148 void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
149 int32_t numBytes = readNextValue<int32_t>();
150 if ((uint32_t)numBytes > mRemainingLen) {
151 mValid = false;
152 return;
153 }
154
155 vector<uint8_t> value(mBuf, mBuf + numBytes);
156 mBuf += numBytes;
157 mRemainingLen -= numBytes;
158 addToValues(pos, depth, value, last);
159 parseAnnotations(numAnnotations);
160 }
161
parseKeyValuePairs(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)162 void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
163 int32_t numPairs = readNextValue<uint8_t>();
164
165 for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
166 last[1] = (pos[1] == numPairs);
167
168 // parse key
169 pos[2] = 1;
170 parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
171
172 // parse value
173 last[2] = true;
174
175 uint8_t typeInfo = readNextValue<uint8_t>();
176 switch (getTypeId(typeInfo)) {
177 case INT32_TYPE:
178 pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
179 parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
180 break;
181 case INT64_TYPE:
182 pos[2] = 3;
183 parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
184 break;
185 case STRING_TYPE:
186 pos[2] = 4;
187 parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
188 break;
189 case FLOAT_TYPE:
190 pos[2] = 5;
191 parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
192 break;
193 default:
194 mValid = false;
195 }
196 }
197
198 parseAnnotations(numAnnotations);
199
200 pos[1] = pos[2] = 1;
201 last[1] = last[2] = false;
202 }
203
parseAttributionChain(int32_t * pos,int32_t depth,bool * last,uint8_t numAnnotations)204 void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
205 uint8_t numAnnotations) {
206 const unsigned int firstUidInChainIndex = mValues.size();
207 const int32_t numNodes = readNextValue<uint8_t>();
208 for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
209 last[1] = (pos[1] == numNodes);
210
211 // parse uid
212 pos[2] = 1;
213 parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
214
215 // parse tag
216 pos[2] = 2;
217 last[2] = true;
218 parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
219 }
220
221 if (mValues.size() - 1 > INT8_MAX) {
222 mValid = false;
223 } else if (mValues.size() - 1 > firstUidInChainIndex) {
224 // At least one node was successfully parsed.
225 mAttributionChainStartIndex = static_cast<int8_t>(firstUidInChainIndex);
226 mAttributionChainEndIndex = static_cast<int8_t>(mValues.size() - 1);
227 }
228
229 if (mValid) {
230 parseAnnotations(numAnnotations, firstUidInChainIndex);
231 }
232
233 pos[1] = pos[2] = 1;
234 last[1] = last[2] = false;
235 }
236
237 // Assumes that mValues is not empty
checkPreviousValueType(Type expected)238 bool LogEvent::checkPreviousValueType(Type expected) {
239 return mValues[mValues.size() - 1].mValue.getType() == expected;
240 }
241
parseIsUidAnnotation(uint8_t annotationType)242 void LogEvent::parseIsUidAnnotation(uint8_t annotationType) {
243 if (mValues.empty() || mValues.size() - 1 > INT8_MAX || !checkPreviousValueType(INT)
244 || annotationType != BOOL_TYPE) {
245 mValid = false;
246 return;
247 }
248
249 bool isUid = readNextValue<uint8_t>();
250 if (isUid) mUidFieldIndex = static_cast<int8_t>(mValues.size() - 1);
251 mValues[mValues.size() - 1].mAnnotations.setUidField(isUid);
252 }
253
parseTruncateTimestampAnnotation(uint8_t annotationType)254 void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
255 if (!mValues.empty() || annotationType != BOOL_TYPE) {
256 mValid = false;
257 return;
258 }
259
260 mTruncateTimestamp = readNextValue<uint8_t>();
261 }
262
parsePrimaryFieldAnnotation(uint8_t annotationType)263 void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
264 if (mValues.empty() || annotationType != BOOL_TYPE) {
265 mValid = false;
266 return;
267 }
268
269 const bool primaryField = readNextValue<uint8_t>();
270 mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
271 }
272
parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,int firstUidInChainIndex)273 void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
274 int firstUidInChainIndex) {
275 if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) {
276 mValid = false;
277 return;
278 }
279
280 if (static_cast<int>(mValues.size() - 1) < firstUidInChainIndex) { // AttributionChain is empty.
281 mValid = false;
282 android_errorWriteLog(0x534e4554, "174485572");
283 return;
284 }
285
286 const bool primaryField = readNextValue<uint8_t>();
287 mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField);
288 }
289
parseExclusiveStateAnnotation(uint8_t annotationType)290 void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) {
291 if (mValues.empty() || annotationType != BOOL_TYPE) {
292 mValid = false;
293 return;
294 }
295
296 if (mValues.size() - 1 > INT8_MAX) {
297 android_errorWriteLog(0x534e4554, "174488848");
298 mValid = false;
299 return;
300 }
301
302 const bool exclusiveState = readNextValue<uint8_t>();
303 mExclusiveStateFieldIndex = static_cast<int8_t>(mValues.size() - 1);
304 mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState);
305 }
306
parseTriggerStateResetAnnotation(uint8_t annotationType)307 void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) {
308 if (mValues.empty() || annotationType != INT32_TYPE) {
309 mValid = false;
310 return;
311 }
312
313 mResetState = readNextValue<int32_t>();
314 }
315
parseStateNestedAnnotation(uint8_t annotationType)316 void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
317 if (mValues.empty() || annotationType != BOOL_TYPE) {
318 mValid = false;
319 return;
320 }
321
322 bool nested = readNextValue<uint8_t>();
323 mValues[mValues.size() - 1].mAnnotations.setNested(nested);
324 }
325
326 // firstUidInChainIndex is a default parameter that is only needed when parsing
327 // annotations for attribution chains.
parseAnnotations(uint8_t numAnnotations,int firstUidInChainIndex)328 void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) {
329 for (uint8_t i = 0; i < numAnnotations; i++) {
330 uint8_t annotationId = readNextValue<uint8_t>();
331 uint8_t annotationType = readNextValue<uint8_t>();
332
333 switch (annotationId) {
334 case ANNOTATION_ID_IS_UID:
335 parseIsUidAnnotation(annotationType);
336 break;
337 case ANNOTATION_ID_TRUNCATE_TIMESTAMP:
338 parseTruncateTimestampAnnotation(annotationType);
339 break;
340 case ANNOTATION_ID_PRIMARY_FIELD:
341 parsePrimaryFieldAnnotation(annotationType);
342 break;
343 case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
344 parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
345 break;
346 case ANNOTATION_ID_EXCLUSIVE_STATE:
347 parseExclusiveStateAnnotation(annotationType);
348 break;
349 case ANNOTATION_ID_TRIGGER_STATE_RESET:
350 parseTriggerStateResetAnnotation(annotationType);
351 break;
352 case ANNOTATION_ID_STATE_NESTED:
353 parseStateNestedAnnotation(annotationType);
354 break;
355 default:
356 mValid = false;
357 return;
358 }
359 }
360 }
361
362 // This parsing logic is tied to the encoding scheme used in StatsEvent.java and
363 // stats_event.c
parseBuffer(uint8_t * buf,size_t len)364 bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
365 mBuf = buf;
366 mRemainingLen = (uint32_t)len;
367
368 int32_t pos[] = {1, 1, 1};
369 bool last[] = {false, false, false};
370
371 // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
372 uint8_t typeInfo = readNextValue<uint8_t>();
373 if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
374
375 uint8_t numElements = readNextValue<uint8_t>();
376 if (numElements < 2 || numElements > 127) mValid = false;
377
378 typeInfo = readNextValue<uint8_t>();
379 if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
380 mElapsedTimestampNs = readNextValue<int64_t>();
381 numElements--;
382
383 typeInfo = readNextValue<uint8_t>();
384 if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
385 mTagId = readNextValue<int32_t>();
386 numElements--;
387 parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
388
389 for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) {
390 last[0] = (pos[0] == numElements);
391
392 typeInfo = readNextValue<uint8_t>();
393 uint8_t typeId = getTypeId(typeInfo);
394
395 switch (typeId) {
396 case BOOL_TYPE:
397 parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
398 break;
399 case INT32_TYPE:
400 parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
401 break;
402 case INT64_TYPE:
403 parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
404 break;
405 case FLOAT_TYPE:
406 parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
407 break;
408 case BYTE_ARRAY_TYPE:
409 parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
410 break;
411 case STRING_TYPE:
412 parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
413 break;
414 case KEY_VALUE_PAIRS_TYPE:
415 parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
416 break;
417 case ATTRIBUTION_CHAIN_TYPE:
418 parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
419 break;
420 case ERROR_TYPE:
421 /* mErrorBitmask =*/ readNextValue<int32_t>();
422 mValid = false;
423 break;
424 default:
425 mValid = false;
426 break;
427 }
428 }
429
430 if (mRemainingLen != 0) mValid = false;
431 mBuf = nullptr;
432 return mValid;
433 }
434
getTypeId(uint8_t typeInfo)435 uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
436 return typeInfo & 0x0F; // type id in lower 4 bytes
437 }
438
getNumAnnotations(uint8_t typeInfo)439 uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) {
440 return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
441 }
442
GetLong(size_t key,status_t * err) const443 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
444 // TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
445 int field = getSimpleField(key);
446 for (const auto& value : mValues) {
447 if (value.mField.getField() == field) {
448 if (value.mValue.getType() == LONG) {
449 return value.mValue.long_value;
450 } else if (value.mValue.getType() == INT) {
451 return value.mValue.int_value;
452 } else {
453 *err = BAD_TYPE;
454 return 0;
455 }
456 }
457 if ((size_t)value.mField.getPosAtDepth(0) > key) {
458 break;
459 }
460 }
461
462 *err = BAD_INDEX;
463 return 0;
464 }
465
GetInt(size_t key,status_t * err) const466 int LogEvent::GetInt(size_t key, status_t* err) const {
467 int field = getSimpleField(key);
468 for (const auto& value : mValues) {
469 if (value.mField.getField() == field) {
470 if (value.mValue.getType() == INT) {
471 return value.mValue.int_value;
472 } else {
473 *err = BAD_TYPE;
474 return 0;
475 }
476 }
477 if ((size_t)value.mField.getPosAtDepth(0) > key) {
478 break;
479 }
480 }
481
482 *err = BAD_INDEX;
483 return 0;
484 }
485
GetString(size_t key,status_t * err) const486 const char* LogEvent::GetString(size_t key, status_t* err) const {
487 int field = getSimpleField(key);
488 for (const auto& value : mValues) {
489 if (value.mField.getField() == field) {
490 if (value.mValue.getType() == STRING) {
491 return value.mValue.str_value.c_str();
492 } else {
493 *err = BAD_TYPE;
494 return 0;
495 }
496 }
497 if ((size_t)value.mField.getPosAtDepth(0) > key) {
498 break;
499 }
500 }
501
502 *err = BAD_INDEX;
503 return NULL;
504 }
505
GetBool(size_t key,status_t * err) const506 bool LogEvent::GetBool(size_t key, status_t* err) const {
507 int field = getSimpleField(key);
508 for (const auto& value : mValues) {
509 if (value.mField.getField() == field) {
510 if (value.mValue.getType() == INT) {
511 return value.mValue.int_value != 0;
512 } else if (value.mValue.getType() == LONG) {
513 return value.mValue.long_value != 0;
514 } else {
515 *err = BAD_TYPE;
516 return false;
517 }
518 }
519 if ((size_t)value.mField.getPosAtDepth(0) > key) {
520 break;
521 }
522 }
523
524 *err = BAD_INDEX;
525 return false;
526 }
527
GetFloat(size_t key,status_t * err) const528 float LogEvent::GetFloat(size_t key, status_t* err) const {
529 int field = getSimpleField(key);
530 for (const auto& value : mValues) {
531 if (value.mField.getField() == field) {
532 if (value.mValue.getType() == FLOAT) {
533 return value.mValue.float_value;
534 } else {
535 *err = BAD_TYPE;
536 return 0.0;
537 }
538 }
539 if ((size_t)value.mField.getPosAtDepth(0) > key) {
540 break;
541 }
542 }
543
544 *err = BAD_INDEX;
545 return 0.0;
546 }
547
GetStorage(size_t key,status_t * err) const548 std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
549 int field = getSimpleField(key);
550 for (const auto& value : mValues) {
551 if (value.mField.getField() == field) {
552 if (value.mValue.getType() == STORAGE) {
553 return value.mValue.storage_value;
554 } else {
555 *err = BAD_TYPE;
556 return vector<uint8_t>();
557 }
558 }
559 if ((size_t)value.mField.getPosAtDepth(0) > key) {
560 break;
561 }
562 }
563
564 *err = BAD_INDEX;
565 return vector<uint8_t>();
566 }
567
ToString() const568 string LogEvent::ToString() const {
569 string result;
570 result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
571 (long long)mElapsedTimestampNs, mTagId);
572 for (const auto& value : mValues) {
573 result +=
574 StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " ";
575 }
576 result += " }";
577 return result;
578 }
579
ToProto(ProtoOutputStream & protoOutput) const580 void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
581 writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
582 }
583
hasAttributionChain(std::pair<int,int> * indexRange) const584 bool LogEvent::hasAttributionChain(std::pair<int, int>* indexRange) const {
585 if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
586 return false;
587 }
588
589 if (nullptr != indexRange) {
590 indexRange->first = static_cast<int>(mAttributionChainStartIndex);
591 indexRange->second = static_cast<int>(mAttributionChainEndIndex);
592 }
593
594 return true;
595 }
596
writeExperimentIdsToProto(const std::vector<int64_t> & experimentIds,std::vector<uint8_t> * protoOut)597 void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
598 std::vector<uint8_t>* protoOut) {
599 ProtoOutputStream proto;
600 for (const auto& expId : experimentIds) {
601 proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
602 (long long)expId);
603 }
604
605 protoOut->resize(proto.size());
606 size_t pos = 0;
607 sp<ProtoReader> reader = proto.data();
608 while (reader->readBuffer() != NULL) {
609 size_t toRead = reader->currentToRead();
610 std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead);
611 pos += toRead;
612 reader->move(toRead);
613 }
614 }
615
616 } // namespace statsd
617 } // namespace os
618 } // namespace android
619