/*
* 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;
}