Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1d9bd9c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement System Properties Section"

parents a834309b 0dfa752e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ cc_library {
                "core/proto/android/os/kernelwake.proto",
                "core/proto/android/os/pagetypeinfo.proto",
                "core/proto/android/os/procrank.proto",
                "core/proto/android/os/system_properties.proto",
                "core/proto/android/service/graphicsstats.proto",
                "tools/streaming_proto/stream.proto",
            ],
@@ -86,6 +87,7 @@ gensrcs {
        "core/proto/android/os/kernelwake.proto",
        "core/proto/android/os/pagetypeinfo.proto",
        "core/proto/android/os/procrank.proto",
        "core/proto/android/os/system_properties.proto",
    ],

    // Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
+81 −68
Original line number Diff line number Diff line
@@ -30,22 +30,26 @@ bool isValidChar(char c) {
        || (v == (uint8_t)'_');
}

static std::string trim(const std::string& s, const std::string& chars) {
    const auto head = s.find_first_not_of(chars);
std::string trim(const std::string& s, const std::string& charset) {
    const auto head = s.find_first_not_of(charset);
    if (head == std::string::npos) return "";

    const auto tail = s.find_last_not_of(chars);
    const auto tail = s.find_last_not_of(charset);
    return s.substr(head, tail - head + 1);
}

static std::string trimDefault(const std::string& s) {
static inline std::string toLowerStr(const std::string& s) {
    std::string res(s);
    std::transform(res.begin(), res.end(), res.begin(), ::tolower);
    return res;
}

static inline std::string trimDefault(const std::string& s) {
    return trim(s, DEFAULT_WHITESPACE);
}

static std::string trimHeader(const std::string& s) {
    std::string res = trimDefault(s);
    std::transform(res.begin(), res.end(), res.begin(), ::tolower);
    return res;
static inline std::string trimHeader(const std::string& s) {
    return toLowerStr(trimDefault(s));
}

// This is similiar to Split in android-base/file.h, but it won't add empty string
@@ -188,97 +192,106 @@ bool Reader::ok(std::string* error) {
}

// ==============================================================================
static int
lookupName(const char** names, const int size, const char* name)
Table::Table(const char* names[], const uint64_t ids[], const int count)
        :mEnums(),
         mEnumValuesByName()
{
    for (int i=0; i<size; i++) {
        if (strcmp(name, names[i]) == 0) {
            return i;
    map<std::string, uint64_t> fields;
    for (int i = 0; i < count; i++) {
        fields[names[i]] = ids[i];
    }
    }
    return -1;
}

EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount)
        :mEnumNames(enumNames),
         mEnumValues(enumValues),
         mEnumCount(enumCount)
{
    mFields = fields;
}

EnumTypeMap::~EnumTypeMap()
Table::~Table()
{
}

int
EnumTypeMap::parseValue(const std::string& value)
void
Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
{
    int index = lookupName(mEnumNames, mEnumCount, value.c_str());
    if (index < 0) return mEnumValues[0]; // Assume value 0 is default
    return mEnumValues[index];
}
    if (mFields.find(field) == mFields.end()) return;

Table::Table(const char* names[], const uint64_t ids[], const int count)
        :mFieldNames(names),
         mFieldIds(ids),
         mFieldCount(count),
         mEnums()
{
    map<std::string, int> enu;
    for (int i = 0; i < enumSize; i++) {
        enu[enumNames[i]] = enumValues[i];
    }

Table::~Table()
{
    mEnums[field] = enu;
}

void
Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize)
Table::addEnumNameToValue(const char* enumName, const int enumValue)
{
    int index = lookupName(mFieldNames, mFieldCount, field);
    if (index < 0) return;

    EnumTypeMap enu(enumNames, enumValues, enumSize);
    mEnums[index] = enu;
    mEnumValuesByName[enumName] = enumValue;
}

bool
Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
{
    int index = lookupName(mFieldNames, mFieldCount, name.c_str());
    if (index < 0) return false;
    if (mFields.find(name) == mFields.end()) return false;

    uint64_t found = mFieldIds[index];
    switch (found & FIELD_TYPE_MASK) {
        case FIELD_TYPE_DOUBLE:
        case FIELD_TYPE_FLOAT:
    uint64_t found = mFields[name];
    record_t repeats; // used for repeated fields
    switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
        case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
            proto->write(found, toDouble(value));
            break;
        case FIELD_TYPE_STRING:
        case FIELD_TYPE_BYTES:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
            proto->write(found, value);
            break;
        case FIELD_TYPE_INT64:
        case FIELD_TYPE_SINT64:
        case FIELD_TYPE_UINT64:
        case FIELD_TYPE_FIXED64:
        case FIELD_TYPE_SFIXED64:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
            proto->write(found, toLongLong(value));
            break;
        case FIELD_TYPE_BOOL:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
            if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
                proto->write(found, true);
                break;
            }
            if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
                proto->write(found, false);
                break;
            }
            return false;
        case FIELD_TYPE_ENUM:
            if (mEnums.find(index) == mEnums.end()) {
                // forget to add enum type mapping
        case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
            // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
            if (mEnums.find(name) != mEnums.end()) {
                if (mEnums[name].find(value) != mEnums[name].end()) {
                    proto->write(found, mEnums[name][value]);
                } else {
                    proto->write(found, 0); // TODO: should get the default enum value (Unknown)
                }
            } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
                proto->write(found, mEnumValuesByName[value]);
            } else {
                return false;
            }
            proto->write(found, mEnums[index].parseValue(value));
            break;
        case FIELD_TYPE_INT32:
        case FIELD_TYPE_SINT32:
        case FIELD_TYPE_UINT32:
        case FIELD_TYPE_FIXED32:
        case FIELD_TYPE_SFIXED32:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
        case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
            proto->write(found, toInt(value));
            break;
        // REPEATED TYPE below:
        case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
            repeats = parseRecord(value, COMMA_DELIMITER);
            for (size_t i=0; i<repeats.size(); i++) {
                proto->write(found, toInt(repeats[i]));
            }
            break;
        case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
            repeats = parseRecord(value, COMMA_DELIMITER);
            for (size_t i=0; i<repeats.size(); i++) {
                proto->write(found, repeats[i]);
            }
            break;
        default:
            return false;
    }
+10 −20
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ const std::string COMMA_DELIMITER = ",";
// returns true if c is a-zA-Z0-9 or underscore _
bool isValidChar(char c);

// trim the string with the given charset
std::string trim(const std::string& s, const std::string& charset);

/**
 * When a text has a table format like this
 * line 1: HeadA HeadB HeadC
@@ -98,21 +101,6 @@ private:
    std::string mStatus;
};

class EnumTypeMap
{
public:
    EnumTypeMap() {};
    EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount);
    ~EnumTypeMap();

    int parseValue(const std::string& value);

private:
    const char** mEnumNames;
    const uint32_t* mEnumValues;
    int mEnumCount;
};

/**
 * The class contains a mapping between table headers to its field ids.
 * And allow users to insert the field values to proto based on its header name.
@@ -124,14 +112,16 @@ public:
    ~Table();

    // Add enum names to values for parsing purpose.
    void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize);
    void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize);

    // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts
    void addEnumNameToValue(const char* enumName, const int enumValue);

    bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
private:
    const char** mFieldNames;
    const uint64_t* mFieldIds;
    const int mFieldCount;
    map<int, EnumTypeMap> mEnums;
    map<std::string, uint64_t> mFields;
    map<std::string, map<std::string, int>> mEnums;
    map<std::string, int> mEnumValuesByName;
};

#endif  // INCIDENT_HELPER_UTIL_H
+3 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "parsers/KernelWakesParser.h"
#include "parsers/PageTypeInfoParser.h"
#include "parsers/ProcrankParser.h"
#include "parsers/SystemPropertiesParser.h"

#include <android-base/file.h>
#include <getopt.h>
@@ -49,6 +50,8 @@ static TextParserBase* selectParser(int section) {
            return new ReverseParser();
/* ========================================================================= */
        // IDs larger than 1 are section ids reserved in incident.proto
        case 1000:
            return new SystemPropertiesParser();
        case 2000:
            return new ProcrankParser();
        case 2001:
+89 −0
Original line number Diff line number Diff line
/*
 * 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 LOG_TAG "incident_helper"

#include <android/util/ProtoOutputStream.h>

#include "frameworks/base/core/proto/android/os/system_properties.proto.h"
#include "ih_util.h"
#include "SystemPropertiesParser.h"

using namespace android::os;

const string LINE_DELIMITER = "]: [";

// system properties' names sometimes are not valid proto field names, make the names valid.
static string convertToFieldName(const string& name) {
    int len = (int)name.length();
    char cstr[len + 1];
    strcpy(cstr, name.c_str());
    for (int i = 0; i < len; i++) {
        if (!isValidChar(cstr[i])) {
            cstr[i] = '_';
        }
    }
    return string(cstr);
}

status_t
SystemPropertiesParser::Parse(const int in, const int out) const
{
    Reader reader(in);
    string line;
    string name;  // the name of the property
    string value; // the string value of the property

    ProtoOutputStream proto;
    Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT);
    table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING);
    table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED);

    // parse line by line
    while (reader.readLine(&line)) {
        if (line.empty()) continue;

        line = line.substr(1, line.size() - 2); // trim []
        size_t index = line.find(LINE_DELIMITER); // split by "]: ["
        if (index == string::npos) {
            fprintf(stderr, "Bad Line %s\n", line.c_str());
            continue;
        }
        name = line.substr(0, index);
        value = trim(line.substr(index + 4), DEFAULT_WHITESPACE);
        if (value.empty()) continue;

        // if the property name couldn't be found in proto definition or the value has mistype,
        // add to extra properties with its name and value
        if (!table.insertField(&proto, convertToFieldName(name), value)) {
            long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
            proto.write(SystemPropertiesProto::Property::NAME, name);
            proto.write(SystemPropertiesProto::Property::VALUE, value);
            proto.end(token);
        }
    }

    if (!reader.ok(&line)) {
        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
        return -1;
    }

    if (!proto.flush(out)) {
        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
        return -1;
    }
    fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
    return NO_ERROR;
}
Loading