blob: affb08088c8d9dfefe955ea27c8bf2854fafe44f [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define STATSD_DEBUG false // STOPSHIP if true
#include "Log.h"
#include "logd/LogEvent.h"
#include <android-base/stringprintf.h>
#include <android/binder_ibinder.h>
#include <private/android_filesystem_config.h>
#include "flags/FlagProvider.h"
#include "stats_annotations.h"
#include "stats_log_util.h"
#include "statslog_statsd.h"
namespace android {
namespace os {
namespace statsd {
// for TrainInfo experiment id serialization
const int FIELD_ID_EXPERIMENT_ID = 1;
using namespace android::util;
using android::base::StringPrintf;
using android::util::ProtoOutputStream;
using std::string;
using std::vector;
namespace {
uint8_t getTypeId(uint8_t typeInfo) {
return typeInfo & 0x0F; // type id in lower 4 bytes
}
uint8_t getNumAnnotations(uint8_t typeInfo) {
return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes
}
} // namespace
LogEvent::LogEvent(int32_t uid, int32_t pid)
: mLogdTimestampNs(getWallClockNs()), mLogUid(uid), mLogPid(pid) {
}
LogEvent::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) {
mLogdTimestampNs = getWallClockNs();
mElapsedTimestampNs = getElapsedRealtimeNs();
mTagId = util::BINARY_PUSH_STATE_CHANGED;
mLogUid = AIBinder_getCallingUid();
mLogPid = AIBinder_getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value((int)requiresStaging)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value((int)rollbackEnabled)));
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(5)), Value((int)requiresLowLatencyMonitor)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(6)), Value(state)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(7)), Value(experimentIds)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(8)), Value(userId)));
}
LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,
const InstallTrainInfo& trainInfo) {
mLogdTimestampNs = wallClockTimestampNs;
mElapsedTimestampNs = elapsedTimestampNs;
mTagId = util::TRAIN_INFO;
mValues.push_back(
FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
std::vector<uint8_t> experimentIdsProto;
writeExperimentIdsToProto(trainInfo.experimentIds, &experimentIdsProto);
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(experimentIdsProto)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(3)), Value(trainInfo.trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
}
void LogEvent::parseInt32(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t value = readNextValue<int32_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseInt64(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int64_t value = readNextValue<int64_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseString(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
return;
}
string value = string((char*)mBuf, numBytes);
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseFloat(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
float value = readNextValue<float>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseBool(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
// cast to int32_t because FieldValue does not support bools
int32_t value = (int32_t)readNextValue<uint8_t>();
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseByteArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numBytes = readNextValue<int32_t>();
if ((uint32_t)numBytes > mRemainingLen) {
mValid = false;
return;
}
vector<uint8_t> value(mBuf, mBuf + numBytes);
mBuf += numBytes;
mRemainingLen -= numBytes;
addToValues(pos, depth, value, last);
parseAnnotations(numAnnotations);
}
void LogEvent::parseKeyValuePairs(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
int32_t numPairs = readNextValue<uint8_t>();
for (pos[1] = 1; pos[1] <= numPairs; pos[1]++) {
last[1] = (pos[1] == numPairs);
// parse key
pos[2] = 1;
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse value
last[2] = true;
uint8_t typeInfo = readNextValue<uint8_t>();
switch (getTypeId(typeInfo)) {
case INT32_TYPE:
pos[2] = 2; // pos[2] determined by index of type in KeyValuePair in atoms.proto
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case INT64_TYPE:
pos[2] = 3;
parseInt64(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case STRING_TYPE:
pos[2] = 4;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
case FLOAT_TYPE:
pos[2] = 5;
parseFloat(pos, /*depth=*/2, last, /*numAnnotations=*/0);
break;
default:
mValid = false;
}
}
parseAnnotations(numAnnotations);
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
void LogEvent::parseAttributionChain(int32_t* pos, int32_t depth, bool* last,
uint8_t numAnnotations) {
std::optional<size_t> firstUidInChainIndex = mValues.size();
const uint8_t numNodes = readNextValue<uint8_t>();
if (numNodes > INT8_MAX) mValid = false;
for (pos[1] = 1; pos[1] <= numNodes; pos[1]++) {
last[1] = (pos[1] == numNodes);
// parse uid
pos[2] = 1;
parseInt32(pos, /*depth=*/2, last, /*numAnnotations=*/0);
// parse tag
pos[2] = 2;
last[2] = true;
parseString(pos, /*depth=*/2, last, /*numAnnotations=*/0);
}
if (mValues.size() > (firstUidInChainIndex.value() + 1)) {
// At least one node was successfully parsed.
mAttributionChainStartIndex = firstUidInChainIndex;
mAttributionChainEndIndex = mValues.size() - 1;
} else {
firstUidInChainIndex = std::nullopt;
mValid = false;
}
if (mValid) {
parseAnnotations(numAnnotations, /*numElements*/ std::nullopt, firstUidInChainIndex);
}
pos[1] = pos[2] = 1;
last[1] = last[2] = false;
}
void LogEvent::parseArray(int32_t* pos, int32_t depth, bool* last, uint8_t numAnnotations) {
const uint8_t numElements = readNextValue<uint8_t>();
const uint8_t typeInfo = readNextValue<uint8_t>();
const uint8_t typeId = getTypeId(typeInfo);
if (numElements > INT8_MAX) mValid = false;
for (pos[1] = 1; pos[1] <= numElements; pos[1]++) {
last[1] = (pos[1] == numElements);
// The top-level array is at depth 0, and all of its elements are at depth 1.
// Once nested fields are supported, array elements will be at top-level depth + 1.
switch (typeId) {
case INT32_TYPE:
parseInt32(pos, /*depth=*/1, last, /*numAnnotations=*/0);
break;
case INT64_TYPE:
parseInt64(pos, /*depth=*/1, last, /*numAnnotations=*/0);
break;
case FLOAT_TYPE:
parseFloat(pos, /*depth=*/1, last, /*numAnnotations=*/0);
break;
case BOOL_TYPE:
parseBool(pos, /*depth=*/1, last, /*numAnnotations=*/0);
break;
case STRING_TYPE:
parseString(pos, /*depth=*/1, last, /*numAnnotations=*/0);
break;
default:
mValid = false;
break;
}
}
parseAnnotations(numAnnotations, numElements);
pos[1] = 1;
last[1] = false;
}
// Assumes that mValues is not empty
bool LogEvent::checkPreviousValueType(Type expected) const {
return mValues[mValues.size() - 1].mValue.getType() == expected;
}
void LogEvent::parseIsUidAnnotation(uint8_t annotationType, std::optional<uint8_t> numElements) {
// Need to set numElements if not an array.
if (!numElements) {
numElements = 1;
}
// If array is empty, skip uid parsing.
if (numElements == 0 && annotationType == BOOL_TYPE) {
readNextValue<uint8_t>();
return;
}
// Allowed types: INT, repeated INT
if (mValues.empty() || numElements > mValues.size() || !checkPreviousValueType(INT) ||
annotationType != BOOL_TYPE) {
VLOG("Atom ID %d error while parseIsUidAnnotation()", mTagId);
mValid = false;
return;
}
bool isUid = readNextValue<uint8_t>();
if (isUid) {
mNumUidFields += numElements.value();
}
for (int i = 1; i <= numElements; i++) {
mValues[mValues.size() - i].mAnnotations.setUidField(isUid);
}
}
void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) {
if (!mValues.empty() || annotationType != BOOL_TYPE) {
VLOG("Atom ID %d error while parseTruncateTimestampAnnotation()", mTagId);
mValid = false;
return;
}
mTruncateTimestamp = readNextValue<uint8_t>();
}
void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements,
std::optional<size_t> firstUidInChainIndex) {
// Allowed types: all types except for attribution chains and repeated fields.
if (mValues.empty() || annotationType != BOOL_TYPE || firstUidInChainIndex || numElements) {
VLOG("Atom ID %d error while parsePrimaryFieldAnnotation()", mTagId);
mValid = false;
return;
}
const bool primaryField = readNextValue<uint8_t>();
mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField);
}
void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType,
std::optional<size_t> firstUidInChainIndex) {
// Allowed types: attribution chains
if (mValues.empty() || annotationType != BOOL_TYPE || !firstUidInChainIndex) {
VLOG("Atom ID %d error while parsePrimaryFieldFirstUidAnnotation()", mTagId);
mValid = false;
return;
}
if (mValues.size() < firstUidInChainIndex.value() + 1) { // AttributionChain is empty.
VLOG("Atom ID %d error while parsePrimaryFieldFirstUidAnnotation()", mTagId);
mValid = false;
android_errorWriteLog(0x534e4554, "174485572");
return;
}
const bool primaryField = readNextValue<uint8_t>();
mValues[firstUidInChainIndex.value()].mAnnotations.setPrimaryField(primaryField);
}
void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements) {
// Allowed types: BOOL
if (mValues.empty() || annotationType != BOOL_TYPE || !checkPreviousValueType(INT) ||
numElements) {
VLOG("Atom ID %d error while parseExclusiveStateAnnotation()", mTagId);
mValid = false;
return;
}
const bool exclusiveState = readNextValue<uint8_t>();
mExclusiveStateFieldIndex = mValues.size() - 1;
mValues[getExclusiveStateFieldIndex().value()].mAnnotations.setExclusiveState(exclusiveState);
}
void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements) {
// Allowed types: INT
if (mValues.empty() || annotationType != INT32_TYPE || !checkPreviousValueType(INT) ||
numElements) {
VLOG("Atom ID %d error while parseTriggerStateResetAnnotation()", mTagId);
mValid = false;
return;
}
mResetState = readNextValue<int32_t>();
}
void LogEvent::parseStateNestedAnnotation(uint8_t annotationType,
std::optional<uint8_t> numElements) {
// Allowed types: BOOL
if (mValues.empty() || annotationType != BOOL_TYPE || !checkPreviousValueType(INT) ||
numElements) {
VLOG("Atom ID %d error while parseStateNestedAnnotation()", mTagId);
mValid = false;
return;
}
bool nested = readNextValue<uint8_t>();
mValues[mValues.size() - 1].mAnnotations.setNested(nested);
}
void LogEvent::parseRestrictionCategoryAnnotation(uint8_t annotationType) {
// Allowed types: INT, field value should be empty since this is atom-level annotation.
if (!mValues.empty() || annotationType != INT32_TYPE) {
mValid = false;
return;
}
int value = readNextValue<int32_t>();
// should be one of predefined category in StatsLog.java
switch (value) {
case ASTATSLOG_RESTRICTION_CATEGORY_DIAGNOSTIC:
case ASTATSLOG_RESTRICTION_CATEGORY_SYSTEM_INTELLIGENCE:
case ASTATSLOG_RESTRICTION_CATEGORY_AUTHENTICATION:
case ASTATSLOG_RESTRICTION_CATEGORY_FRAUD_AND_ABUSE:
break;
default:
mValid = false;
return;
}
mRestrictionCategory = static_cast<StatsdRestrictionCategory>(value);
return;
}
void LogEvent::parseFieldRestrictionAnnotation(uint8_t annotationType) {
// Allowed types: BOOL
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
// Read the value so that the rest of the event is correctly parsed
// TODO: store the field annotations once the metrics need to parse them.
readNextValue<uint8_t>();
return;
}
// firstUidInChainIndex is a default parameter that is only needed when parsing
// annotations for attribution chains.
// numElements is a default param that is only needed when parsing annotations for repeated fields
void LogEvent::parseAnnotations(uint8_t numAnnotations, std::optional<uint8_t> numElements,
std::optional<size_t> firstUidInChainIndex) {
for (uint8_t i = 0; i < numAnnotations; i++) {
uint8_t annotationId = readNextValue<uint8_t>();
uint8_t annotationType = readNextValue<uint8_t>();
switch (annotationId) {
case ASTATSLOG_ANNOTATION_ID_IS_UID:
parseIsUidAnnotation(annotationType, numElements);
break;
case ASTATSLOG_ANNOTATION_ID_TRUNCATE_TIMESTAMP:
parseTruncateTimestampAnnotation(annotationType);
break;
case ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD:
parsePrimaryFieldAnnotation(annotationType, numElements, firstUidInChainIndex);
break;
case ASTATSLOG_ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID:
parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex);
break;
case ASTATSLOG_ANNOTATION_ID_EXCLUSIVE_STATE:
parseExclusiveStateAnnotation(annotationType, numElements);
break;
case ASTATSLOG_ANNOTATION_ID_TRIGGER_STATE_RESET:
parseTriggerStateResetAnnotation(annotationType, numElements);
break;
case ASTATSLOG_ANNOTATION_ID_STATE_NESTED:
parseStateNestedAnnotation(annotationType, numElements);
break;
case ASTATSLOG_ANNOTATION_ID_RESTRICTION_CATEGORY:
if (isAtLeastU()) {
parseRestrictionCategoryAnnotation(annotationType);
} else {
mValid = false;
}
break;
// Currently field restrictions are ignored, so we parse but do not store them.
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING:
case ASTATSLOG_ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION:
if (isAtLeastU()) {
parseFieldRestrictionAnnotation(annotationType);
} else {
mValid = false;
}
break;
default:
VLOG("Atom ID %d error while parseAnnotations() - wrong annotationId(%d)", mTagId,
annotationId);
mValid = false;
return;
}
}
}
LogEvent::BodyBufferInfo LogEvent::parseHeader(const uint8_t* buf, size_t len) {
BodyBufferInfo bodyInfo;
mParsedHeaderOnly = true;
mBuf = buf;
mRemainingLen = (uint32_t)len;
// Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID
uint8_t typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != OBJECT_TYPE) {
mValid = false;
mBuf = nullptr;
return bodyInfo;
}
uint8_t numElements = readNextValue<uint8_t>();
if (numElements < 2 || numElements > INT8_MAX) {
mValid = false;
mBuf = nullptr;
return bodyInfo;
}
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT64_TYPE) {
mValid = false;
mBuf = nullptr;
return bodyInfo;
}
mElapsedTimestampNs = readNextValue<int64_t>();
numElements--;
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT32_TYPE) {
mValid = false;
mBuf = nullptr;
return bodyInfo;
}
mTagId = readNextValue<int32_t>();
numElements--;
parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations
bodyInfo.numElements = numElements;
bodyInfo.buffer = mBuf;
bodyInfo.bufferSize = mRemainingLen;
mBuf = nullptr;
return bodyInfo;
}
bool LogEvent::parseBody(const BodyBufferInfo& bodyInfo) {
mParsedHeaderOnly = false;
mBuf = bodyInfo.buffer;
mRemainingLen = (uint32_t)bodyInfo.bufferSize;
int32_t pos[] = {1, 1, 1};
bool last[] = {false, false, false};
// While this number is not guaranteed to be correct due to repeated fields and
// attribution chains, it still positively affects performance and reduces the number
// of vector buffer reallocations.
mValues.reserve(bodyInfo.numElements);
for (pos[0] = 1; pos[0] <= bodyInfo.numElements && mValid; pos[0]++) {
last[0] = (pos[0] == bodyInfo.numElements);
uint8_t typeInfo = readNextValue<uint8_t>();
uint8_t typeId = getTypeId(typeInfo);
switch (typeId) {
case BOOL_TYPE:
parseBool(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT32_TYPE:
parseInt32(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case INT64_TYPE:
parseInt64(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case FLOAT_TYPE:
parseFloat(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case BYTE_ARRAY_TYPE:
parseByteArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case STRING_TYPE:
parseString(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case KEY_VALUE_PAIRS_TYPE:
parseKeyValuePairs(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ATTRIBUTION_CHAIN_TYPE:
parseAttributionChain(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case LIST_TYPE:
parseArray(pos, /*depth=*/0, last, getNumAnnotations(typeInfo));
break;
case ERROR_TYPE:
/* mErrorBitmask =*/readNextValue<int32_t>();
mValid = false;
break;
default:
mValid = false;
break;
}
}
if (mRemainingLen != 0) mValid = false;
mBuf = nullptr;
return mValid;
}
// This parsing logic is tied to the encoding scheme used in StatsEvent.java and
// stats_event.c
bool LogEvent::parseBuffer(const uint8_t* buf, size_t len) {
BodyBufferInfo bodyInfo = parseHeader(buf, len);
// emphasize intention to parse the body, however atom data could be incomplete
// if header/body parsing was failed due to invalid buffer content for example
mParsedHeaderOnly = false;
// early termination if header is invalid
if (!mValid) {
mBuf = nullptr;
return false;
}
return parseBody(bodyInfo);
}
int64_t LogEvent::GetLong(size_t key, status_t* err) const {
// TODO(b/110561208): encapsulate the magical operations in Field struct as static functions
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == LONG) {
return value.mValue.long_value;
} else if (value.mValue.getType() == INT) {
return value.mValue.int_value;
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0;
}
int LogEvent::GetInt(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == INT) {
return value.mValue.int_value;
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0;
}
const char* LogEvent::GetString(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == STRING) {
return value.mValue.str_value.c_str();
} else {
*err = BAD_TYPE;
return 0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return NULL;
}
bool LogEvent::GetBool(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == INT) {
return value.mValue.int_value != 0;
} else if (value.mValue.getType() == LONG) {
return value.mValue.long_value != 0;
} else {
*err = BAD_TYPE;
return false;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return false;
}
float LogEvent::GetFloat(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == FLOAT) {
return value.mValue.float_value;
} else {
*err = BAD_TYPE;
return 0.0;
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return 0.0;
}
std::vector<uint8_t> LogEvent::GetStorage(size_t key, status_t* err) const {
int field = getSimpleField(key);
for (const auto& value : mValues) {
if (value.mField.getField() == field) {
if (value.mValue.getType() == STORAGE) {
return value.mValue.storage_value;
} else {
*err = BAD_TYPE;
return vector<uint8_t>();
}
}
if ((size_t)value.mField.getPosAtDepth(0) > key) {
break;
}
}
*err = BAD_INDEX;
return vector<uint8_t>();
}
string LogEvent::ToString() const {
string result;
result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs,
(long long)mElapsedTimestampNs, mTagId);
string annotations;
if (mTruncateTimestamp) {
annotations = "TRUNCATE_TS";
}
if (mResetState != -1) {
annotations += annotations.size() ? ", RESET_STATE" : "RESET_STATE";
}
if (annotations.size()) {
result += " [" + annotations + "] ";
}
if (isParsedHeaderOnly()) {
result += " ParsedHeaderOnly }";
return result;
}
for (const auto& value : mValues) {
result += StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString();
result += value.mAnnotations.toString() + " ";
}
result += " }";
return result;
}
void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}
bool LogEvent::hasAttributionChain(std::pair<size_t, size_t>* indexRange) const {
if (!mAttributionChainStartIndex || !mAttributionChainEndIndex) {
return false;
}
if (nullptr != indexRange) {
indexRange->first = mAttributionChainStartIndex.value();
indexRange->second = mAttributionChainEndIndex.value();
}
return true;
}
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds,
std::vector<uint8_t>* protoOut) {
ProtoOutputStream proto;
for (const auto& expId : experimentIds) {
proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
(long long)expId);
}
protoOut->resize(proto.size());
size_t pos = 0;
sp<ProtoReader> reader = proto.data();
while (reader->readBuffer() != NULL) {
size_t toRead = reader->currentToRead();
std::memcpy(protoOut->data() + pos, reader->readBuffer(), toRead);
pos += toRead;
reader->move(toRead);
}
}
} // namespace statsd
} // namespace os
} // namespace android