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

Commit 38d503fe authored by Jeff Hamilton's avatar Jeff Hamilton Committed by Android (Google) Code Review
Browse files

Merge "Add an API to read experiment IDs."

parents ee10d8bc fa2f91c2
Loading
Loading
Loading
Loading
+40 −23
Original line number Diff line number Diff line
@@ -65,8 +65,6 @@ constexpr const char* kOpUsage = "android:get_usage_stats";

// for StatsDataDumpProto
const int FIELD_ID_REPORTS_LIST = 1;
// for TrainInfo experiment id serialization
const int FIELD_ID_EXPERIMENT_ID = 1;

static binder::Status ok() {
    return binder::Status::ok();
@@ -1181,7 +1179,7 @@ Status StatsService::unregisterPullerCallback(int32_t atomTag, const String16& p
Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainName,
                                                    int64_t trainVersionCode, int options,
                                                    int32_t state,
                                                    const std::vector<int64_t>& experimentIds) {
                                                    const std::vector<int64_t>& experimentIdsIn) {
    uid_t uid = IPCThreadState::self()->getCallingUid();
    // For testing
    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
@@ -1201,7 +1199,7 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra

    bool readTrainInfoSuccess = false;
    InstallTrainInfo trainInfo;
    if (trainVersionCode == -1 || experimentIds.empty() || trainName.size() == 0) {
    if (trainVersionCode == -1 || experimentIdsIn.empty() || trainName.size() == 0) {
        readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfo);
    }

@@ -1209,27 +1207,19 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra
        trainVersionCode = trainInfo.trainVersionCode;
    }

    vector<uint8_t> experimentIdsProtoBuffer;
    if (readTrainInfoSuccess && experimentIds.empty()) {
        experimentIdsProtoBuffer = trainInfo.experimentIds;
    // Find the right experiment IDs
    std::vector<int64_t> experimentIds;
    if (readTrainInfoSuccess && experimentIdsIn.empty()) {
        experimentIds = trainInfo.experimentIds;
    } else {
        ProtoOutputStream proto;
        for (const auto& expId : experimentIds) {
            proto.write(FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_EXPERIMENT_ID,
                        (long long)expId);
        experimentIds = experimentIdsIn;
    }

        experimentIdsProtoBuffer.resize(proto.size());
        size_t pos = 0;
        sp<ProtoReader> reader = proto.data();
        while (reader->readBuffer() != NULL) {
            size_t toRead = reader->currentToRead();
            std::memcpy(&(experimentIdsProtoBuffer[pos]), reader->readBuffer(), toRead);
            pos += toRead;
            reader->move(toRead);
        }
    }
    // Flatten the experiment IDs to proto
    vector<uint8_t> experimentIdsProtoBuffer;
    writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer);

    // Find the right train name
    std::string trainNameUtf8;
    if (readTrainInfoSuccess && trainName.size() == 0) {
        trainNameUtf8 = trainInfo.trainName;
@@ -1244,7 +1234,34 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra
    LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled,
                   requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId);
    mProcessor->OnLogEvent(&event);
    StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIdsProtoBuffer);
    StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds);
    return Status::ok();
}

Status StatsService::getRegisteredExperimentIds(std::vector<int64_t>* experimentIdsOut) {
    uid_t uid = IPCThreadState::self()->getCallingUid();

    // Caller must be granted these permissions
    if (!checkCallingPermission(String16(kPermissionDump))) {
        return exception(binder::Status::EX_SECURITY,
                         StringPrintf("UID %d lacks permission %s", uid, kPermissionDump));
    }
    if (!checkCallingPermission(String16(kPermissionUsage))) {
        return exception(binder::Status::EX_SECURITY,
                         StringPrintf("UID %d lacks permission %s", uid, kPermissionUsage));
    }
    // TODO: add verifier permission

    // Read the latest train info
    InstallTrainInfo trainInfo;
    if (!StorageManager::readTrainInfo(trainInfo)) {
        // No train info means no experiment IDs, return an empty list
        experimentIdsOut->clear();
        return Status::ok();
    }

    // Copy the experiment IDs to the out vector
    experimentIdsOut->assign(trainInfo.experimentIds.begin(), trainInfo.experimentIds.end());
    return Status::ok();
}

+5 −0
Original line number Diff line number Diff line
@@ -193,6 +193,11 @@ public:
            const android::String16& trainName, int64_t trainVersionCode, int options,
            int32_t state, const std::vector<int64_t>& experimentIds) override;

    /**
     * Binder call to get registered experiment IDs.
     */
    virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);

    /**
     * Binder call to get SpeakerImpedance atom.
     */
+24 −1
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@ namespace android {
namespace os {
namespace statsd {

// for TrainInfo experiment id serialization
const int FIELD_ID_EXPERIMENT_ID = 1;

using namespace android::util;
using android::util::ProtoOutputStream;
using std::string;
@@ -241,7 +244,9 @@ LogEvent::LogEvent(int64_t wallClockTimestampNs, int64_t elapsedTimestampNs,

    mValues.push_back(
            FieldValue(Field(mTagId, getSimpleField(1)), Value(trainInfo.trainVersionCode)));
    mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainInfo.experimentIds)));
    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)));
}
@@ -671,6 +676,24 @@ void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
    writeFieldValueTreeToStream(mTagId, getValues(), &protoOutput);
}

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
+4 −1
Original line number Diff line number Diff line
@@ -60,8 +60,9 @@ struct InstallTrainInfo {
    int64_t trainVersionCode;
    std::string trainName;
    int32_t status;
    std::vector<uint8_t> experimentIds;
    std::vector<int64_t> experimentIds;
};

/**
 * Wrapper for the log_msg structure.
 */
@@ -239,6 +240,8 @@ private:
    uint32_t mLogUid;
};

void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);

}  // namespace statsd
}  // namespace os
}  // namespace android
+123 −88
Original line number Diff line number Diff line
@@ -36,9 +36,17 @@ using android::util::FIELD_COUNT_REPEATED;
using android::util::FIELD_TYPE_MESSAGE;
using std::map;

/**
 * NOTE: these directories are protected by SELinux, any changes here must also update
 * the SELinux policies.
 */
#define STATS_DATA_DIR "/data/misc/stats-data"
#define STATS_SERVICE_DIR "/data/misc/stats-service"
#define TRAIN_INFO_DIR "/data/misc/train-info"
#define TRAIN_INFO_PATH "/data/misc/train-info/train-info.bin"

// Magic word at the start of the train info file, change this if changing the file format
const uint32_t TRAIN_INFO_FILE_MAGIC = 0xff7447ff;

// for ConfigMetricsReportList
const int FIELD_ID_REPORTS = 2;
@@ -96,27 +104,42 @@ void StorageManager::writeFile(const char* file, const void* buffer, int numByte
}

bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string& trainName,
                                    int32_t status, const std::vector<uint8_t>& experimentIds) {
                                    int32_t status, const std::vector<int64_t>& experimentIds) {
    std::lock_guard<std::mutex> lock(sTrainInfoMutex);

    deleteAllFiles(TRAIN_INFO_DIR);

    string file_name = StringPrintf("%s/%lld", TRAIN_INFO_DIR, (long long)trainVersionCode);

    int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
    int fd = open(TRAIN_INFO_PATH, O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
    if (fd == -1) {
        VLOG("Attempt to access %s but failed", file_name.c_str());
        VLOG("Attempt to access %s but failed", TRAIN_INFO_PATH);
        return false;
    }

    size_t result;

    // Write the magic word
    result = write(fd, &TRAIN_INFO_FILE_MAGIC, sizeof(TRAIN_INFO_FILE_MAGIC));
    if (result != sizeof(TRAIN_INFO_FILE_MAGIC)) {
        VLOG("Failed to wrtie train info magic");
        close(fd);
        return false;
    }

    // Write the train version
    const size_t trainVersionCodeByteCount = sizeof(trainVersionCode);
    result = write(fd, &trainVersionCode, trainVersionCodeByteCount);
    if (result != trainVersionCodeByteCount) {
        VLOG("Failed to wrtie train version code");
        close(fd);
        return false;
    }

    // Write # of bytes in trainName to file
    const size_t trainNameSize = trainName.size();
    const size_t trainNameSizeByteCount = sizeof(trainNameSize);
    result = write(fd, (uint8_t*)&trainNameSize, trainNameSizeByteCount);
    if (result != trainNameSizeByteCount) {
        VLOG("Failed to write train name size for %s", file_name.c_str());
        VLOG("Failed to write train name size");
        close(fd);
        return false;
    }
@@ -124,7 +147,7 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
    // Write trainName to file
    result = write(fd, trainName.c_str(), trainNameSize);
    if (result != trainNameSize) {
        VLOG("Failed to write train name for%s", file_name.c_str());
        VLOG("Failed to write train name");
        close(fd);
        return false;
    }
@@ -133,34 +156,38 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
    const size_t statusByteCount = sizeof(status);
    result = write(fd, (uint8_t*)&status, statusByteCount);
    if (result != statusByteCount) {
        VLOG("Failed to write status for %s", file_name.c_str());
        VLOG("Failed to write status");
        close(fd);
        return false;
    }

    // Write experiment id size to file.
    const size_t experimentIdSize = experimentIds.size();
    const size_t experimentIdsSizeByteCount = sizeof(experimentIdSize);
    result = write(fd, (uint8_t*) &experimentIdSize, experimentIdsSizeByteCount);
    if (result != experimentIdsSizeByteCount) {
        VLOG("Failed to write experiment id size for %s", file_name.c_str());
    // Write experiment id count to file.
    const size_t experimentIdsCount = experimentIds.size();
    const size_t experimentIdsCountByteCount = sizeof(experimentIdsCount);
    result = write(fd, (uint8_t*) &experimentIdsCount, experimentIdsCountByteCount);
    if (result != experimentIdsCountByteCount) {
        VLOG("Failed to write experiment id count");
        close(fd);
        return false;
    }

    // Write experimentIds to file
    result = write(fd, experimentIds.data(), experimentIds.size());
    if (result == experimentIds.size()) {
        VLOG("Successfully wrote %s", file_name.c_str());
    for (size_t i = 0; i < experimentIdsCount; i++) {
        const int64_t experimentId = experimentIds[i];
        const size_t experimentIdByteCount = sizeof(experimentId);
        result = write(fd, &experimentId, experimentIdByteCount);
        if (result == experimentIdByteCount) {
            VLOG("Successfully wrote experiment IDs");
        } else {
        VLOG("Failed to write experiment ids for %s", file_name.c_str());
            VLOG("Failed to write experiment ids");
            close(fd);
            return false;
        }
    }

    result = fchown(fd, AID_STATSD, AID_STATSD);
    if (result) {
        VLOG("Failed to chown %s to statsd", file_name.c_str());
        VLOG("Failed to chown train info file to statsd");
        close(fd);
        return false;
    }
@@ -172,26 +199,33 @@ bool StorageManager::writeTrainInfo(int64_t trainVersionCode, const std::string&
bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
    std::lock_guard<std::mutex> lock(sTrainInfoMutex);

    unique_ptr<DIR, decltype(&closedir)> dir(opendir(TRAIN_INFO_DIR), closedir);

    if (dir == NULL) {
        VLOG("Directory does not exist: %s", TRAIN_INFO_DIR);
    int fd = open(TRAIN_INFO_PATH, O_RDONLY | O_CLOEXEC);
    if (fd == -1) {
        VLOG("Failed to open train-info.bin");
        return false;
    }

    dirent* de;
    while ((de = readdir(dir.get()))) {
        char* name = de->d_name;
        if (name[0] == '.') {
            continue;
    // Read the magic word
    uint32_t magic;
    size_t result = read(fd, &magic, sizeof(magic));
    if (result != sizeof(magic)) {
        VLOG("Failed to read train info magic");
        close(fd);
        return false;
    }

        size_t result;
    if (magic != TRAIN_INFO_FILE_MAGIC) {
        VLOG("Train info magic was 0x%08x, expected 0x%08x", magic, TRAIN_INFO_FILE_MAGIC);
        close(fd);
        return false;
    }

        trainInfo.trainVersionCode = StrToInt64(name);
        string fullPath = StringPrintf("%s/%s", TRAIN_INFO_DIR, name);
        int fd = open(fullPath.c_str(), O_RDONLY | O_CLOEXEC);
        if (fd == -1) {
    // Read the train version code
    const size_t trainVersionCodeByteCount(sizeof(trainInfo.trainVersionCode));
    result = read(fd, &trainInfo.trainVersionCode, trainVersionCodeByteCount);
    if (result != trainVersionCodeByteCount) {
        VLOG("Failed to read train version code from train info file");
        close(fd);
        return false;
    }

@@ -199,7 +233,7 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
    size_t trainNameSize;
    result = read(fd, &trainNameSize, sizeof(size_t));
    if (result != sizeof(size_t)) {
            VLOG("Failed to read train name size from file %s", fullPath.c_str());
        VLOG("Failed to read train name size from train info file");
        close(fd);
        return false;
    }
@@ -208,7 +242,7 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
    trainInfo.trainName.resize(trainNameSize);
    result = read(fd, trainInfo.trainName.data(), trainNameSize);
    if (result != trainNameSize) {
            VLOG("Failed to read train name from file %s", fullPath.c_str());
        VLOG("Failed to read train name from train info file");
        close(fd);
        return false;
    }
@@ -217,44 +251,45 @@ bool StorageManager::readTrainInfo(InstallTrainInfo& trainInfo) {
    const size_t statusByteCount = sizeof(trainInfo.status);
    result = read(fd, &trainInfo.status, statusByteCount);
    if (result != statusByteCount) {
            VLOG("Failed to read train status from file %s", fullPath.c_str());
        VLOG("Failed to read train status from train info file");
        close(fd);
        return false;
    }

        // Read experiment ids size.
        size_t experimentIdSize;
        result = read(fd, &experimentIdSize, sizeof(size_t));
    // Read experiment ids count.
    size_t experimentIdsCount;
    result = read(fd, &experimentIdsCount, sizeof(size_t));
    if (result != sizeof(size_t)) {
            VLOG("Failed to read train experiment id size from file %s", fullPath.c_str());
        VLOG("Failed to read train experiment id count from train info file");
        close(fd);
        return false;
    }

    // Read experimentIds
        trainInfo.experimentIds.resize(experimentIdSize);
        result = read(fd, trainInfo.experimentIds.data(), experimentIdSize);
        if (result != experimentIdSize) {
            VLOG("Failed to read train experiment ids from file %s", fullPath.c_str());
    for (size_t i = 0; i < experimentIdsCount; i++) {
        int64_t experimentId;
        result = read(fd, &experimentId, sizeof(experimentId));
        if (result != sizeof(experimentId)) {
            VLOG("Failed to read train experiment id from train info file");
            close(fd);
            return false;
        }
        trainInfo.experimentIds.push_back(experimentId);
    }

    // Expect to be at EOF.
    char c;
    result = read(fd, &c, 1);
    if (result != 0) {
            VLOG("Failed to read train info from file %s. Did not get expected EOF.", fullPath.c_str());
        VLOG("Failed to read train info from file. Did not get expected EOF.");
        close(fd);
        return false;
    }

        VLOG("Read train info file successful: %s", fullPath.c_str());
    VLOG("Read train info file successful");
    close(fd);
    return true;
}
    return false;
}

void StorageManager::deleteFile(const char* file) {
    if (remove(file) != 0) {
Loading