| /* |
| * 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 |