/* * Copyright (C) 2015 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. */ #include "metrics/metrics_library.h" #include #include #include #include #include #include #include #include #include #include "android/brillo/metrics/IMetricsd.h" #include "constants.h" static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; static const int kCrosEventHistogramMax = 100; static const char kMetricsServiceName[] = "android.brillo.metrics.IMetricsd"; /* Add new cros events here. * * The index of the event is sent in the message, so please do not * reorder the names. */ static const char *kCrosEventNames[] = { "ModemManagerCommandSendFailure", // 0 "HwWatchdogReboot", // 1 "Cras.NoCodecsFoundAtBoot", // 2 "Chaps.DatabaseCorrupted", // 3 "Chaps.DatabaseRepairFailure", // 4 "Chaps.DatabaseCreateFailure", // 5 "Attestation.OriginSpecificExhausted", // 6 "SpringPowerSupply.Original.High", // 7 "SpringPowerSupply.Other.High", // 8 "SpringPowerSupply.Original.Low", // 9 "SpringPowerSupply.ChargerIdle", // 10 "TPM.NonZeroDictionaryAttackCounter", // 11 "TPM.EarlyResetDuringCommand", // 12 }; using android::binder::Status; using android::brillo::metrics::IMetricsd; using android::String16; MetricsLibrary::MetricsLibrary() {} MetricsLibrary::~MetricsLibrary() {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. bool MetricsLibrary::IsDeviceMounted(const char* device_name, const char* mounts_file, char* buffer, int buffer_size, bool* result) { if (buffer == nullptr || buffer_size < 1) return false; int mounts_fd = open(mounts_file, O_RDONLY); if (mounts_fd < 0) return false; // match_offset describes: // -1 -- not beginning of line // 0..strlen(device_name)-1 -- this offset in device_name is next to match // strlen(device_name) -- matched full name, just need a space. int match_offset = 0; bool match = false; while (!match) { int read_size = read(mounts_fd, buffer, buffer_size); if (read_size <= 0) { if (errno == -EINTR) continue; break; } for (int i = 0; i < read_size; ++i) { if (buffer[i] == '\n') { match_offset = 0; continue; } if (match_offset < 0) { continue; } if (device_name[match_offset] == '\0') { if (buffer[i] == ' ') { match = true; break; } match_offset = -1; continue; } if (buffer[i] == device_name[match_offset]) { ++match_offset; } else { match_offset = -1; } } } close(mounts_fd); *result = match; return true; } bool MetricsLibrary::IsGuestMode() { char buffer[256]; bool result = false; if (!IsDeviceMounted("guestfs", "/proc/mounts", buffer, sizeof(buffer), &result)) { return false; } return result && (access("/var/run/state/logged-in", F_OK) == 0); } bool MetricsLibrary::CheckService() { if (metricsd_proxy_.get() && android::IInterface::asBinder(metricsd_proxy_)->isBinderAlive()) return true; const String16 name(kMetricsServiceName); metricsd_proxy_ = android::interface_cast( android::defaultServiceManager()->checkService(name)); return metricsd_proxy_.get(); } bool MetricsLibrary::AreMetricsEnabled() { static struct stat stat_buffer; time_t this_check_time = time(nullptr); if (!use_caching_ || this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; cached_enabled_ = stat(consent_file_.value().data(), &stat_buffer) >= 0; } return cached_enabled_; } void MetricsLibrary::Init() { base::FilePath dir = base::FilePath(metrics::kSharedMetricsDirectory); consent_file_ = dir.Append(metrics::kConsentFileName); cached_enabled_ = false; cached_enabled_time_ = 0; use_caching_ = true; } void MetricsLibrary::InitWithNoCaching() { Init(); use_caching_ = false; } void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) { consent_file_ = metrics_directory.Append(metrics::kConsentFileName); cached_enabled_ = false; cached_enabled_time_ = 0; use_caching_ = true; } bool MetricsLibrary::SendToUMA( const std::string& name, int sample, int min, int max, int nbuckets) { return CheckService() && metricsd_proxy_->recordHistogram(String16(name.c_str()), sample, min, max, nbuckets) .isOk(); } bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, int max) { return CheckService() && metricsd_proxy_->recordLinearHistogram(String16(name.c_str()), sample, max) .isOk(); } bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) { return CheckService() && metricsd_proxy_->recordLinearHistogram(String16(name.c_str()), sample ? 1 : 0, 2) .isOk(); } bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) { return CheckService() && metricsd_proxy_->recordSparseHistogram(String16(name.c_str()), sample) .isOk(); } bool MetricsLibrary::SendCrashToUMA(const char* crash_kind) { return CheckService() && metricsd_proxy_->recordCrash(String16(crash_kind)).isOk(); } bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) { for (size_t i = 0; i < arraysize(kCrosEventNames); i++) { if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) { return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax); } } return false; } bool MetricsLibrary::GetHistogramsDump(std::string* dump) { android::String16 temp_dump; if (!CheckService() || !metricsd_proxy_->getHistogramsDump(&temp_dump).isOk()) { return false; } *dump = android::String8(temp_dump).string(); return true; }