blob: 4f031724763f94dca081ee9df1e12bdfca0e9b6c [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 DEBUG false // STOPSHIP if true
#include "logd/LogEvent.h"
#include <android-base/stringprintf.h>
#include <android/binder_ibinder.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#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<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) {
const unsigned int firstUidInChainIndex = mValues.size();
const int32_t numNodes = readNextValue<uint8_t>();
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<int8_t>(firstUidInChainIndex);
mAttributionChainEndIndex = static_cast<int8_t>(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<uint8_t>();
if (isUid) mUidFieldIndex = static_cast<int8_t>(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<uint8_t>();
}
void LogEvent::parsePrimaryFieldAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
const bool primaryField = readNextValue<uint8_t>();
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<int>(mValues.size() - 1) < firstUidInChainIndex) { // AttributionChain is empty.
mValid = false;
android_errorWriteLog(0x534e4554, "174485572");
return;
}
const bool primaryField = readNextValue<uint8_t>();
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<uint8_t>();
mExclusiveStateFieldIndex = static_cast<int8_t>(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<int32_t>();
}
void LogEvent::parseStateNestedAnnotation(uint8_t annotationType) {
if (mValues.empty() || annotationType != BOOL_TYPE) {
mValid = false;
return;
}
bool nested = readNextValue<uint8_t>();
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>();
uint8_t annotationType = readNextValue<uint8_t>();
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<uint8_t>();
if (getTypeId(typeInfo) != OBJECT_TYPE) mValid = false;
uint8_t numElements = readNextValue<uint8_t>();
if (numElements < 2 || numElements > 127) mValid = false;
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT64_TYPE) mValid = false;
mElapsedTimestampNs = readNextValue<int64_t>();
numElements--;
typeInfo = readNextValue<uint8_t>();
if (getTypeId(typeInfo) != INT32_TYPE) mValid = false;
mTagId = readNextValue<int32_t>();
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>();
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<int32_t>();
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<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);
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<int, int>* indexRange) const {
if (mAttributionChainStartIndex == -1 || mAttributionChainEndIndex == -1) {
return false;
}
if (nullptr != indexRange) {
indexRange->first = static_cast<int>(mAttributionChainStartIndex);
indexRange->second = static_cast<int>(mAttributionChainEndIndex);
}
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