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