Add an API to read experiment IDs.
Change the file format to store the experiment IDs direclty so we don't need to parse the proto when reading the values out. Bug: 129099771 Test: bit statsd_test:* && adb shell cmd stats pull-source 10051 Change-Id: I0dc1fd118f4d9ba597c2f0959648136bbafb5aab
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
experimentIds = experimentIdsIn;
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
} else {
|
||||
VLOG("Failed to write experiment ids for %s", file_name.c_str());
|
||||
close(fd);
|
||||
return false;
|
||||
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");
|
||||
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,88 +199,96 @@ 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;
|
||||
}
|
||||
|
||||
size_t result;
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read # of bytes taken by trainName in the file.
|
||||
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());
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read trainName
|
||||
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());
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read status
|
||||
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());
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read experiment ids size.
|
||||
size_t experimentIdSize;
|
||||
result = read(fd, &experimentIdSize, sizeof(size_t));
|
||||
if (result != sizeof(size_t)) {
|
||||
VLOG("Failed to read train experiment id size from file %s", fullPath.c_str());
|
||||
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());
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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());
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG("Read train info file successful: %s", fullPath.c_str());
|
||||
// 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 true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Read # of bytes taken by trainName in the file.
|
||||
size_t trainNameSize;
|
||||
result = read(fd, &trainNameSize, sizeof(size_t));
|
||||
if (result != sizeof(size_t)) {
|
||||
VLOG("Failed to read train name size from train info file");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read trainName
|
||||
trainInfo.trainName.resize(trainNameSize);
|
||||
result = read(fd, trainInfo.trainName.data(), trainNameSize);
|
||||
if (result != trainNameSize) {
|
||||
VLOG("Failed to read train name from train info file");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read status
|
||||
const size_t statusByteCount = sizeof(trainInfo.status);
|
||||
result = read(fd, &trainInfo.status, statusByteCount);
|
||||
if (result != statusByteCount) {
|
||||
VLOG("Failed to read train status from train info file");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 count from train info file");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read experimentIds
|
||||
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. Did not get expected EOF.");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG("Read train info file successful");
|
||||
close(fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageManager::deleteFile(const char* file) {
|
||||
|
||||
@@ -29,11 +29,6 @@ namespace statsd {
|
||||
|
||||
using android::util::ProtoOutputStream;
|
||||
|
||||
struct TrainInfo {
|
||||
int64_t trainVersionCode;
|
||||
std::vector<uint8_t> experimentIds;
|
||||
};
|
||||
|
||||
class StorageManager : public virtual RefBase {
|
||||
public:
|
||||
/**
|
||||
@@ -45,7 +40,7 @@ public:
|
||||
* Writes train info.
|
||||
*/
|
||||
static bool 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);
|
||||
|
||||
/**
|
||||
* Reads train info.
|
||||
|
||||
@@ -645,6 +645,22 @@ TEST(LogEventTest, TestBinaryFieldAtom_empty) {
|
||||
EXPECT_EQ(orig_str, result_str);
|
||||
}
|
||||
|
||||
TEST(LogEventTest, TestWriteExperimentIdsToProto) {
|
||||
std::vector<int64_t> expIds;
|
||||
expIds.push_back(5038);
|
||||
std::vector<uint8_t> proto;
|
||||
|
||||
writeExperimentIdsToProto(expIds, &proto);
|
||||
|
||||
EXPECT_EQ(proto.size(), 3);
|
||||
// Proto wire format for field ID 1, varint
|
||||
EXPECT_EQ(proto[0], 0x08);
|
||||
// varint of 5038, 2 bytes long
|
||||
EXPECT_EQ(proto[1], 0xae);
|
||||
EXPECT_EQ(proto[2], 0x27);
|
||||
}
|
||||
|
||||
|
||||
} // namespace statsd
|
||||
} // namespace os
|
||||
} // namespace android
|
||||
|
||||
@@ -409,6 +409,36 @@ public final class StatsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the experiments IDs registered with statsd, or an empty array if there aren't any.
|
||||
*
|
||||
* @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
|
||||
public long[] getRegisteredExperimentIds()
|
||||
throws StatsUnavailableException {
|
||||
synchronized (this) {
|
||||
try {
|
||||
IStatsManager service = getIStatsManagerLocked();
|
||||
if (service == null) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Failed to find statsd when getting experiment IDs");
|
||||
}
|
||||
return new long[0];
|
||||
}
|
||||
return service.getRegisteredExperimentIds();
|
||||
} catch (RemoteException e) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG,
|
||||
"Failed to connect to StatsCompanionService when getting "
|
||||
+ "registered experiment IDs");
|
||||
}
|
||||
return new long[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback for an atom when that atom is to be pulled. The stats service will
|
||||
* invoke pullData in the callback when the stats service determines that this atom needs to be
|
||||
|
||||
@@ -217,4 +217,9 @@ interface IStatsManager {
|
||||
*/
|
||||
oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode,
|
||||
in int options, in int state, in long[] experimentId);
|
||||
|
||||
/**
|
||||
* Returns the most recently registered experiment IDs.
|
||||
*/
|
||||
long[] getRegisteredExperimentIds();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user