From a0e6de15df58b4162f9e5d72556f759c9ef5ad9d Mon Sep 17 00:00:00 2001 From: Jonathan Nguyen Date: Tue, 28 Jan 2020 18:33:55 -0800 Subject: [PATCH] Update StatsLogProcessor to handle BinaryPushStateChanged Also changed StatsLog to call write() instead of the hard coded function in StatsService. Test: gts-tradefed run gts-dev --module GtsStatsdHostTestCases Change-Id: I26171fa4cfc877e1e179b74ec8076d964aff8548 --- apex/statsd/aidl/android/os/IStatsd.aidl | 6 -- cmds/statsd/Android.bp | 1 + cmds/statsd/src/StatsLogProcessor.cpp | 123 ++++++++++++++++++++++ cmds/statsd/src/StatsLogProcessor.h | 8 ++ cmds/statsd/src/StatsService.cpp | 127 ++--------------------- cmds/statsd/src/StatsService.h | 10 -- cmds/statsd/src/atoms.proto | 1 + cmds/statsd/src/experiment_ids.proto | 29 ++++++ cmds/statsd/src/logd/LogEvent.cpp | 20 ++++ cmds/statsd/src/logd/LogEvent.h | 17 +++ cmds/statsd/src/stats_log_util.cpp | 17 +++ cmds/statsd/src/stats_log_util.h | 3 + core/java/android/util/StatsLog.java | 41 ++++---- 13 files changed, 245 insertions(+), 158 deletions(-) create mode 100644 cmds/statsd/src/experiment_ids.proto diff --git a/apex/statsd/aidl/android/os/IStatsd.aidl b/apex/statsd/aidl/android/os/IStatsd.aidl index a2564212366f0..253b2c17423d4 100644 --- a/apex/statsd/aidl/android/os/IStatsd.aidl +++ b/apex/statsd/aidl/android/os/IStatsd.aidl @@ -221,12 +221,6 @@ interface IStatsd { */ const int FLAG_REQUIRE_LOW_LATENCY_MONITOR = 0x04; - /** - * Logs an event for binary push for module updates. - */ - oneway void sendBinaryPushStateChangedAtom(in String trainName, in long trainVersionCode, - in int options, in int state, in long[] experimentId); - /** * Logs an event for watchdog rollbacks. */ diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index ebed4ee2b82db..286fc699f9de6 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -64,6 +64,7 @@ cc_defaults { "src/config/ConfigKey.cpp", "src/config/ConfigListener.cpp", "src/config/ConfigManager.cpp", + "src/experiment_ids.proto", "src/external/GpuStatsPuller.cpp", "src/external/Perfetto.cpp", "src/external/PullResultReceiver.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 879b3c3e52aa1..bde15a5cdaae0 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -20,13 +20,17 @@ #include "StatsLogProcessor.h" #include +#include #include +#include #include "android-base/stringprintf.h" #include "atoms_info.h" #include "external/StatsPullerManager.h" #include "guardrail/StatsdStats.h" +#include "logd/LogEvent.h" #include "metrics/CountMetricProducer.h" +#include "StatsService.h" #include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" @@ -68,6 +72,10 @@ const int FIELD_ID_STRINGS = 9; // for ActiveConfigList const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1; +// for permissions checks +constexpr const char* kPermissionDump = "android.permission.DUMP"; +constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS"; + #define NS_PER_HOUR 3600 * NS_PER_SEC #define STATS_ACTIVE_METRIC_DIR "/data/misc/stats-active-metric" @@ -181,6 +189,115 @@ void StatsLogProcessor::onIsolatedUidChangedEventLocked(const LogEvent& event) { } } +void StatsLogProcessor::onBinaryPushStateChangedEventLocked(LogEvent* event) { + pid_t pid = event->GetPid(); + uid_t uid = event->GetUid(); + if (!checkPermissionForIds(kPermissionDump, pid, uid) || + !checkPermissionForIds(kPermissionUsage, pid, uid)) { + return; + } + status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR, err4 = NO_ERROR; + string trainName = string(event->GetString(1 /*train name field id*/, &err)); + int64_t trainVersionCode = event->GetLong(2 /*train version field id*/, &err2); + int32_t state = int32_t(event->GetLong(6 /*state field id*/, &err3)); +#ifdef NEW_ENCODING_SCHEME + std::vector trainExperimentIdBytes = + event->GetStorage(7 /*experiment ids field id*/, &err4); +#else + string trainExperimentIdString = event->GetString(7 /*experiment ids field id*/, &err4); +#endif + if (err != NO_ERROR || err2 != NO_ERROR || err3 != NO_ERROR || err4 != NO_ERROR) { + ALOGE("Failed to parse fields in binary push state changed log event"); + return; + } + ExperimentIds trainExperimentIds; +#ifdef NEW_ENCODING_SCHEME + if (!trainExperimentIds.ParseFromArray(trainExperimentIdBytes.data(), + trainExperimentIdBytes.size())) { +#else + if (!trainExperimentIds.ParseFromString(trainExperimentIdString)) { +#endif + ALOGE("Failed to parse experimentids in binary push state changed."); + return; + } + vector experimentIdVector = {trainExperimentIds.experiment_id().begin(), + trainExperimentIds.experiment_id().end()}; + // Update the train info on disk and get any data the logevent is missing. + getAndUpdateTrainInfoOnDisk( + state, &trainVersionCode, &trainName, &experimentIdVector); + + std::vector trainExperimentIdProto; + writeExperimentIdsToProto(experimentIdVector, &trainExperimentIdProto); + int32_t userId = multiuser_get_user_id(uid); + + event->updateValue(1 /*train name field id*/, trainName, STRING); + event->updateValue(2 /*train version field id*/, trainVersionCode, LONG); +#ifdef NEW_ENCODING_SCHEME + event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STORAGE); +#else + event->updateValue(7 /*experiment ids field id*/, trainExperimentIdProto, STRING); +#endif + event->updateValue(8 /*user id field id*/, userId, INT); +} + +void StatsLogProcessor::getAndUpdateTrainInfoOnDisk(int32_t state, + int64_t* trainVersionCode, + string* trainName, + std::vector* experimentIds) { + bool readTrainInfoSuccess = false; + InstallTrainInfo trainInfoOnDisk; + readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk); + + bool resetExperimentIds = false; + if (readTrainInfoSuccess) { + // Keep the old train version if we received an empty version. + if (*trainVersionCode == -1) { + *trainVersionCode = trainInfoOnDisk.trainVersionCode; + } else if (*trainVersionCode != trainInfoOnDisk.trainVersionCode) { + // Reset experiment ids if we receive a new non-empty train version. + resetExperimentIds = true; + } + + // Keep the old train name if we received an empty train name. + if (trainName->size() == 0) { + *trainName = trainInfoOnDisk.trainName; + } else if (*trainName != trainInfoOnDisk.trainName) { + // Reset experiment ids if we received a new valid train name. + resetExperimentIds = true; + } + + // Reset if we received a different experiment id. + if (!experimentIds->empty() && + (trainInfoOnDisk.experimentIds.empty() || + experimentIds->at(0) != trainInfoOnDisk.experimentIds[0])) { + resetExperimentIds = true; + } + } + + // Find the right experiment IDs + if (!resetExperimentIds && readTrainInfoSuccess) { + *experimentIds = trainInfoOnDisk.experimentIds; + } + + if (!experimentIds->empty()) { + int64_t firstId = experimentIds->at(0); + switch (state) { + case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: + experimentIds->push_back(firstId + 1); + break; + case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: + experimentIds->push_back(firstId + 2); + break; + case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: + experimentIds->push_back(firstId + 3); + break; + } + } + + StorageManager::writeTrainInfo(*trainVersionCode, *trainName, state, *experimentIds); +} + + void StatsLogProcessor::resetConfigs() { std::lock_guard lock(mMetricsMutex); resetConfigsLocked(getElapsedRealtimeNs()); @@ -201,6 +318,12 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { void StatsLogProcessor::OnLogEvent(LogEvent* event, int64_t elapsedRealtimeNs) { std::lock_guard lock(mMetricsMutex); + // Hard-coded logic to update train info on disk and fill in any information + // this log event may be missing. + if (event->GetTagId() == android::util::BINARY_PUSH_STATE_CHANGED) { + onBinaryPushStateChangedEventLocked(event); + } + #ifdef VERY_VERBOSE_PRINTING if (mPrintAllLogs) { ALOGI("%s", event->ToString().c_str()); diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index c569bc1e33f76..c49f2e0ec68db 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -196,6 +196,14 @@ private: // Handler over the isolated uid change event. void onIsolatedUidChangedEventLocked(const LogEvent& event); + // Handler over the binary push state changed event. + void onBinaryPushStateChangedEventLocked(LogEvent* event); + + // Updates train info on disk based on binary push state changed info and + // write disk info into parameters. + void getAndUpdateTrainInfoOnDisk(int32_t state, int64_t* trainVersionCode, + string* trainName, std::vector* experimentIds); + // Reset all configs. void resetConfigsLocked(const int64_t timestampNs); // Reset the specified configs. diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 8a8c1e6ff0ac0..0256e3617dd7b 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -70,25 +70,12 @@ static binder::Status exception(uint32_t code, const std::string& msg) { return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } - static bool checkPermission(const char* permission) { - sp scs = getStatsCompanionService(); - if (scs == nullptr) { - return false; - } - - bool success; pid_t pid = IPCThreadState::self()->getCallingPid(); uid_t uid = IPCThreadState::self()->getCallingUid(); - - binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success); - if (!status.isOk()) { - return false; - } - return success; + return checkPermissionForIds(permission, pid, uid); } - binder::Status checkUid(uid_t expectedUid) { uid_t uid = IPCThreadState::self()->getCallingUid(); if (uid == expectedUid || uid == AID_ROOT) { @@ -870,18 +857,8 @@ status_t StatsService::cmd_log_binary_push(int out, const Vector& args) dprintf(out, "Incorrect number of argument supplied\n"); return UNKNOWN_ERROR; } - android::String16 trainName = android::String16(args[1].c_str()); + string trainName = string(args[1].c_str()); int64_t trainVersion = strtoll(args[2].c_str(), nullptr, 10); - int options = 0; - if (args[3] == "1") { - options = options | IStatsd::FLAG_REQUIRE_STAGING; - } - if (args[4] == "1") { - options = options | IStatsd::FLAG_ROLLBACK_ENABLED; - } - if (args[5] == "1") { - options = options | IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR; - } int32_t state = atoi(args[6].c_str()); vector experimentIds; if (argCount == 8) { @@ -892,7 +869,10 @@ status_t StatsService::cmd_log_binary_push(int out, const Vector& args) } } dprintf(out, "Logging BinaryPushStateChanged\n"); - sendBinaryPushStateChangedAtom(trainName, trainVersion, options, state, experimentIds); + vector experimentIdBytes; + writeExperimentIdsToProto(experimentIds, &experimentIdBytes); + LogEvent event(trainName, trainVersion, args[3], args[4], args[5], state, experimentIdBytes, 0); + mProcessor->OnLogEvent(&event); return NO_ERROR; } @@ -1313,101 +1293,6 @@ Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) { return Status::ok(); } -Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& trainNameIn, - const int64_t trainVersionCodeIn, - const int options, - const int32_t state, - const std::vector& experimentIdsIn) { - // Note: We skip the usage stats op check here since we do not have a package name. - // This is ok since we are overloading the usage_stats permission. - // This method only sends data, it does not receive it. - pid_t pid = IPCThreadState::self()->getCallingPid(); - uid_t uid = IPCThreadState::self()->getCallingUid(); - // Root, system, and shell always have access - if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) { - // Caller must be granted these permissions - if (!checkPermission(kPermissionDump)) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, - kPermissionDump)); - } - if (!checkPermission(kPermissionUsage)) { - return exception(binder::Status::EX_SECURITY, - StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, - kPermissionUsage)); - } - } - - bool readTrainInfoSuccess = false; - InstallTrainInfo trainInfoOnDisk; - readTrainInfoSuccess = StorageManager::readTrainInfo(trainInfoOnDisk); - - bool resetExperimentIds = false; - int64_t trainVersionCode = trainVersionCodeIn; - std::string trainNameUtf8 = std::string(String8(trainNameIn).string()); - if (readTrainInfoSuccess) { - // Keep the old train version if we received an empty version. - if (trainVersionCodeIn == -1) { - trainVersionCode = trainInfoOnDisk.trainVersionCode; - } else if (trainVersionCodeIn != trainInfoOnDisk.trainVersionCode) { - // Reset experiment ids if we receive a new non-empty train version. - resetExperimentIds = true; - } - - // Keep the old train name if we received an empty train name. - if (trainNameUtf8.size() == 0) { - trainNameUtf8 = trainInfoOnDisk.trainName; - } else if (trainNameUtf8 != trainInfoOnDisk.trainName) { - // Reset experiment ids if we received a new valid train name. - resetExperimentIds = true; - } - - // Reset if we received a different experiment id. - if (!experimentIdsIn.empty() && - (trainInfoOnDisk.experimentIds.empty() || - experimentIdsIn[0] != trainInfoOnDisk.experimentIds[0])) { - resetExperimentIds = true; - } - } - - // Find the right experiment IDs - std::vector experimentIds; - if (resetExperimentIds || !readTrainInfoSuccess) { - experimentIds = experimentIdsIn; - } else { - experimentIds = trainInfoOnDisk.experimentIds; - } - - if (!experimentIds.empty()) { - int64_t firstId = experimentIds[0]; - switch (state) { - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALL_SUCCESS: - experimentIds.push_back(firstId + 1); - break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_INITIATED: - experimentIds.push_back(firstId + 2); - break; - case android::util::BINARY_PUSH_STATE_CHANGED__STATE__INSTALLER_ROLLBACK_SUCCESS: - experimentIds.push_back(firstId + 3); - break; - } - } - - // Flatten the experiment IDs to proto - vector experimentIdsProtoBuffer; - writeExperimentIdsToProto(experimentIds, &experimentIdsProtoBuffer); - StorageManager::writeTrainInfo(trainVersionCode, trainNameUtf8, state, experimentIds); - - userid_t userId = multiuser_get_user_id(uid); - bool requiresStaging = options & IStatsd::FLAG_REQUIRE_STAGING; - bool rollbackEnabled = options & IStatsd::FLAG_ROLLBACK_ENABLED; - bool requiresLowLatencyMonitor = options & IStatsd::FLAG_REQUIRE_LOW_LATENCY_MONITOR; - LogEvent event(trainNameUtf8, trainVersionCode, requiresStaging, rollbackEnabled, - requiresLowLatencyMonitor, state, experimentIdsProtoBuffer, userId); - mProcessor->OnLogEvent(&event); - return Status::ok(); -} - Status StatsService::sendWatchdogRollbackOccurredAtom(const int32_t rollbackTypeIn, const android::String16& packageNameIn, const int64_t packageVersionCodeIn, diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index 3bfaa9842950f..af3016f867731 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -192,16 +192,6 @@ public: */ virtual Status unregisterNativePullAtomCallback(int32_t atomTag) override; - /** - * Binder call to log BinaryPushStateChanged atom. - */ - virtual Status sendBinaryPushStateChangedAtom( - const android::String16& trainNameIn, - const int64_t trainVersionCodeIn, - const int options, - const int32_t state, - const std::vector& experimentIdsIn) override; - /** * Binder call to log WatchdogRollbackOccurred atom. */ diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index fccefdc073631..3be1ef87b477c 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -3706,6 +3706,7 @@ message FlagFlipUpdateOccurred { /** * Potential experiment ids that goes with a train install. + * Should be kept in sync with experiment_ids.proto. */ message TrainExperimentIds { repeated int64 experiment_id = 1; diff --git a/cmds/statsd/src/experiment_ids.proto b/cmds/statsd/src/experiment_ids.proto new file mode 100644 index 0000000000000..c2036314cf587 --- /dev/null +++ b/cmds/statsd/src/experiment_ids.proto @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2020 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. + */ + +syntax = "proto2"; + +package android.os.statsd; + +option java_package = "com.android.internal.os"; +option java_outer_classname = "ExperimentIdsProto"; + +// StatsLogProcessor uses the proto to parse experiment ids from +// BinaryPushStateChanged atoms. This needs to be in sync with +// TrainExperimentIds in atoms.proto. +message ExperimentIds { + repeated int64 experiment_id = 1; +} diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 9a0693a84e659..656743be6a93a 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -809,6 +809,26 @@ float LogEvent::GetFloat(size_t key, status_t* err) const { 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, diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 3db26765326d3..e4b784e069ca7 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -144,6 +144,7 @@ public: const char* GetString(size_t key, status_t* err) const; bool GetBool(size_t key, status_t* err) const; float GetFloat(size_t key, status_t* err) const; + std::vector GetStorage(size_t key, status_t* err) const; /** * Write test data to the LogEvent. This can only be used when the LogEvent is constructed @@ -214,6 +215,22 @@ public: return LogEvent(*this); } + template + status_t updateValue(size_t key, T& value, Type type) { + int field = getSimpleField(key); + for (auto& fieldValue : mValues) { + if (fieldValue.mField.getField() == field) { + if (fieldValue.mValue.getType() == type) { + fieldValue.mValue = Value(value); + return OK; + } else { + return BAD_TYPE; + } + } + } + return BAD_INDEX; + } + private: /** * Only use this if copy is absolutely needed. diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 8e0c628699322..73f640e130a84 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -21,6 +21,8 @@ #include #include +#include "statscompanion_util.h" + using android::util::AtomsInfo; using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; @@ -584,6 +586,21 @@ int64_t MillisToNano(const int64_t millis) { return millis * 1000000; } +bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) { + sp scs = getStatsCompanionService(); + if (scs == nullptr) { + return false; + } + + bool success; + binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success); + if (!status.isOk()) { + return false; + } + + return success; +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h index 5fdf6e2602470..aec09561b7c6f 100644 --- a/cmds/statsd/src/stats_log_util.h +++ b/cmds/statsd/src/stats_log_util.h @@ -95,6 +95,9 @@ bool parseProtoOutputStream(util::ProtoOutputStream& protoOutput, T* message) { // Returns the truncated timestamp to the nearest 5 minutes if needed. int64_t truncateTimestampIfNecessary(int atomId, int64_t timestampNs); +// Checks permission for given pid and uid. +bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid); + inline bool isVendorPulledAtom(int atomId) { return atomId >= StatsdStats::kVendorPulledAtomStartTag && atomId < StatsdStats::kMaxAtomTag; } diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java index feeff6c479720..791073794b0ed 100644 --- a/core/java/android/util/StatsLog.java +++ b/core/java/android/util/StatsLog.java @@ -27,6 +27,7 @@ import android.content.Context; import android.os.IStatsd; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.proto.ProtoOutputStream; import com.android.internal.util.FrameworkStatsLog; @@ -37,6 +38,7 @@ import com.android.internal.util.FrameworkStatsLog; public final class StatsLog { private static final String TAG = "StatsLog"; private static final boolean DEBUG = false; + private static final int EXPERIMENT_IDS_FIELD_ID = 1; private static IStatsd sService; @@ -152,28 +154,25 @@ public final class StatsLog { public static boolean logBinaryPushStateChanged(@NonNull String trainName, long trainVersionCode, int options, int state, @NonNull long[] experimentIds) { - synchronized (sLogLock) { - try { - IStatsd service = getIStatsdLocked(); - if (service == null) { - if (DEBUG) { - Slog.d(TAG, "Failed to find statsd when logging event"); - } - return false; - } - service.sendBinaryPushStateChangedAtom( - trainName, trainVersionCode, options, state, experimentIds); - return true; - } catch (RemoteException e) { - sService = null; - if (DEBUG) { - Slog.d(TAG, - "Failed to connect to StatsCompanionService when logging " - + "BinaryPushStateChanged"); - } - return false; - } + ProtoOutputStream proto = new ProtoOutputStream(); + for (long id : experimentIds) { + proto.write( + ProtoOutputStream.FIELD_TYPE_INT64 + | ProtoOutputStream.FIELD_COUNT_REPEATED + | EXPERIMENT_IDS_FIELD_ID, + id); } + FrameworkStatsLog.write(FrameworkStatsLog.BINARY_PUSH_STATE_CHANGED, + trainName, + trainVersionCode, + (options & IStatsd.FLAG_REQUIRE_STAGING) > 0, + (options & IStatsd.FLAG_ROLLBACK_ENABLED) > 0, + (options & IStatsd.FLAG_REQUIRE_LOW_LATENCY_MONITOR) > 0, + state, + proto.getBytes(), + 0, + 0); + return true; } /**