/* * 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 DEBUG false // STOPSHIP if true #include "logd/LogEvent.h" #include #include #include #include #include "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; // stats_event.h socket types. Keep in sync. /* ERRORS */ #define ERROR_NO_TIMESTAMP 0x1 #define ERROR_NO_ATOM_ID 0x2 #define ERROR_OVERFLOW 0x4 #define ERROR_ATTRIBUTION_CHAIN_TOO_LONG 0x8 #define ERROR_TOO_MANY_KEY_VALUE_PAIRS 0x10 #define ERROR_ANNOTATION_DOES_NOT_FOLLOW_FIELD 0x20 #define ERROR_INVALID_ANNOTATION_ID 0x40 #define ERROR_ANNOTATION_ID_TOO_LARGE 0x80 #define ERROR_TOO_MANY_ANNOTATIONS 0x100 #define ERROR_TOO_MANY_FIELDS 0x200 #define ERROR_INVALID_VALUE_TYPE 0x400 #define ERROR_STRING_NOT_NULL_TERMINATED 0x800 /* TYPE IDS */ #define INT32_TYPE 0x00 #define INT64_TYPE 0x01 #define STRING_TYPE 0x02 #define LIST_TYPE 0x03 #define FLOAT_TYPE 0x04 #define BOOL_TYPE 0x05 #define BYTE_ARRAY_TYPE 0x06 #define OBJECT_TYPE 0x07 #define KEY_VALUE_PAIRS_TYPE 0x08 #define ATTRIBUTION_CHAIN_TYPE 0x09 #define ERROR_TYPE 0x0F LogEvent::LogEvent(int32_t uid, int32_t pid) : mLogdTimestampNs(time(nullptr)), mLogUid(uid), mLogPid(pid) { } LogEvent::LogEvent(const string& trainName, int64_t trainVersionCode, bool requiresStaging, bool rollbackEnabled, bool requiresLowLatencyMonitor, int32_t state, const std::vector& 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 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(); 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(); 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(); 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(); 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(); 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(); if ((uint32_t)numBytes > mRemainingLen) { mValid = false; return; } vector 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(); 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(); 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) { const unsigned int firstUidInChainIndex = mValues.size(); const int32_t numNodes = readNextValue(); 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() - 1 > INT8_MAX) { mValid = false; } else if (mValues.size() - 1 > firstUidInChainIndex) { // At least one node was successfully parsed. mAttributionChainStartIndex = static_cast(firstUidInChainIndex); mAttributionChainEndIndex = static_cast(mValues.size() - 1); } if (mValid) { parseAnnotations(numAnnotations, firstUidInChainIndex); } pos[1] = pos[2] = 1; last[1] = last[2] = false; } // Assumes that mValues is not empty bool LogEvent::checkPreviousValueType(Type expected) { return mValues[mValues.size() - 1].mValue.getType() == expected; } void LogEvent::parseIsUidAnnotation(uint8_t annotationType) { if (mValues.empty() || mValues.size() - 1 > INT8_MAX || !checkPreviousValueType(INT) || annotationType != BOOL_TYPE) { mValid = false; return; } bool isUid = readNextValue(); if (isUid) mUidFieldIndex = static_cast(mValues.size() - 1); mValues[mValues.size() - 1].mAnnotations.setUidField(isUid); } void LogEvent::parseTruncateTimestampAnnotation(uint8_t annotationType) { if (!mValues.empty() || annotationType != BOOL_TYPE) { mValid = false; return; } mTruncateTimestamp = readNextValue(); } void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) { if (mValues.empty() || annotationType != BOOL_TYPE) { mValid = false; return; } const bool primaryField = readNextValue(); mValues[mValues.size() - 1].mAnnotations.setPrimaryField(primaryField); } void LogEvent::parsePrimaryFieldFirstUidAnnotation(uint8_t annotationType, int firstUidInChainIndex) { if (mValues.empty() || annotationType != BOOL_TYPE || -1 == firstUidInChainIndex) { mValid = false; return; } if (static_cast(mValues.size() - 1) < firstUidInChainIndex) { // AttributionChain is empty. mValid = false; android_errorWriteLog(0x534e4554, "174485572"); return; } const bool primaryField = readNextValue(); mValues[firstUidInChainIndex].mAnnotations.setPrimaryField(primaryField); } void LogEvent::parseExclusiveStateAnnotation(uint8_t annotationType) { if (mValues.empty() || annotationType != BOOL_TYPE) { mValid = false; return; } if (mValues.size() - 1 > INT8_MAX) { android_errorWriteLog(0x534e4554, "174488848"); mValid = false; return; } const bool exclusiveState = readNextValue(); mExclusiveStateFieldIndex = static_cast(mValues.size() - 1); mValues[getExclusiveStateFieldIndex()].mAnnotations.setExclusiveState(exclusiveState); } void LogEvent::parseTriggerStateResetAnnotation(uint8_t annotationType) { if (mValues.empty() || annotationType != INT32_TYPE) { mValid = false; return; } mResetState = readNextValue(); } void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) { if (mValues.empty() || annotationType != BOOL_TYPE) { mValid = false; return; } bool nested = readNextValue(); mValues[mValues.size() - 1].mAnnotations.setNested(nested); } // firstUidInChainIndex is a default parameter that is only needed when parsing // annotations for attribution chains. void LogEvent::parseAnnotations(uint8_t numAnnotations, int firstUidInChainIndex) { for (uint8_t i = 0; i < numAnnotations; i++) { uint8_t annotationId = readNextValue(); uint8_t annotationType = readNextValue(); switch (annotationId) { case ANNOTATION_ID_IS_UID: parseIsUidAnnotation(annotationType); break; case ANNOTATION_ID_TRUNCATE_TIMESTAMP: parseTruncateTimestampAnnotation(annotationType); break; case ANNOTATION_ID_PRIMARY_FIELD: parsePrimaryFieldAnnotation(annotationType); break; case ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID: parsePrimaryFieldFirstUidAnnotation(annotationType, firstUidInChainIndex); break; case ANNOTATION_ID_EXCLUSIVE_STATE: parseExclusiveStateAnnotation(annotationType); break; case ANNOTATION_ID_TRIGGER_STATE_RESET: parseTriggerStateResetAnnotation(annotationType); break; case ANNOTATION_ID_STATE_NESTED: parseStateNestedAnnotation(annotationType); break; default: mValid = false; return; } } } // This parsing logic is tied to the encoding scheme used in StatsEvent.java and // stats_event.c bool LogEvent::parseBuffer(uint8_t* buf, size_t len) { mBuf = buf; mRemainingLen = (uint32_t)len; int32_t pos[] = {1, 1, 1}; bool last[] = {false, false, false}; // Beginning of buffer is OBJECT_TYPE | NUM_FIELDS | TIMESTAMP | ATOM_ID uint8_t typeInfo = readNextValue(); if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false; uint8_t numElements = readNextValue(); if (numElements < 2 || numElements > 127) mValid = false; typeInfo = readNextValue(); if (getTypeId(typeInfo) != INT64_TYPE) mValid = false; mElapsedTimestampNs = readNextValue(); numElements--; typeInfo = readNextValue(); if (getTypeId(typeInfo) != INT32_TYPE) mValid = false; mTagId = readNextValue(); numElements--; parseAnnotations(getNumAnnotations(typeInfo)); // atom-level annotations for (pos[0] = 1; pos[0] <= numElements && mValid; pos[0]++) { last[0] = (pos[0] == numElements); typeInfo = readNextValue(); 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 ERROR_TYPE: /* mErrorBitmask =*/ readNextValue(); mValid = false; break; default: mValid = false; break; } } if (mRemainingLen != 0) mValid = false; mBuf = nullptr; return mValid; } uint8_t LogEvent::getTypeId(uint8_t typeInfo) { return typeInfo & 0x0F; // type id in lower 4 bytes } uint8_t LogEvent::getNumAnnotations(uint8_t typeInfo) { return (typeInfo >> 4) & 0x0F; // num annotations in upper 4 bytes } 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 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(); } } if ((size_t)value.mField.getPosAtDepth(0) > key) { break; } } *err = BAD_INDEX; return vector(); } string LogEvent::ToString() const { string result; result += StringPrintf("{ uid(%d) %lld %lld (%d)", mLogUid, (long long)mLogdTimestampNs, (long long)mElapsedTimestampNs, mTagId); for (const auto& value : mValues) { result += StringPrintf("%#x", value.mField.getField()) + "->" + value.mValue.toString() + " "; } result += " }"; return result; } void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput); } bool LogEvent::hasAttributionChain(std::pair* indexRange) const { if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) { return false; } if (nullptr != indexRange) { indexRange->first = static_cast(mAttributionChainStartIndex); indexRange->second = static_cast(mAttributionChainEndIndex); } return true; } void writeExperimentIdsToProto(const std::vector& experimentIds, std::vector* 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 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