Home | History | Annotate | Download | only in metricsd
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "metrics/metrics_library.h"
     18 
     19 #include <base/logging.h>
     20 #include <base/strings/stringprintf.h>
     21 #include <binder/IServiceManager.h>
     22 #include <errno.h>
     23 #include <sys/file.h>
     24 #include <sys/stat.h>
     25 #include <utils/String16.h>
     26 
     27 #include <cstdio>
     28 #include <cstring>
     29 
     30 #include "android/brillo/metrics/IMetricsd.h"
     31 #include "constants.h"
     32 
     33 static const char kCrosEventHistogramName[] = "Platform.CrOSEvent";
     34 static const int kCrosEventHistogramMax = 100;
     35 static const char kMetricsServiceName[] = "android.brillo.metrics.IMetricsd";
     36 
     37 /* Add new cros events here.
     38  *
     39  * The index of the event is sent in the message, so please do not
     40  * reorder the names.
     41  */
     42 static const char *kCrosEventNames[] = {
     43   "ModemManagerCommandSendFailure",  // 0
     44   "HwWatchdogReboot",  // 1
     45   "Cras.NoCodecsFoundAtBoot",  // 2
     46   "Chaps.DatabaseCorrupted",  // 3
     47   "Chaps.DatabaseRepairFailure",  // 4
     48   "Chaps.DatabaseCreateFailure",  // 5
     49   "Attestation.OriginSpecificExhausted",  // 6
     50   "SpringPowerSupply.Original.High",  // 7
     51   "SpringPowerSupply.Other.High",  // 8
     52   "SpringPowerSupply.Original.Low",  // 9
     53   "SpringPowerSupply.ChargerIdle",  // 10
     54   "TPM.NonZeroDictionaryAttackCounter",  // 11
     55   "TPM.EarlyResetDuringCommand",  // 12
     56 };
     57 
     58 using android::binder::Status;
     59 using android::brillo::metrics::IMetricsd;
     60 using android::String16;
     61 
     62 MetricsLibrary::MetricsLibrary() {}
     63 MetricsLibrary::~MetricsLibrary() {}
     64 
     65 // We take buffer and buffer_size as parameters in order to simplify testing
     66 // of various alignments of the |device_name| with |buffer_size|.
     67 bool MetricsLibrary::IsDeviceMounted(const char* device_name,
     68                                      const char* mounts_file,
     69                                      char* buffer,
     70                                      int buffer_size,
     71                                      bool* result) {
     72   if (buffer == nullptr || buffer_size < 1)
     73     return false;
     74   int mounts_fd = open(mounts_file, O_RDONLY);
     75   if (mounts_fd < 0)
     76     return false;
     77   // match_offset describes:
     78   //   -1 -- not beginning of line
     79   //   0..strlen(device_name)-1 -- this offset in device_name is next to match
     80   //   strlen(device_name) -- matched full name, just need a space.
     81   int match_offset = 0;
     82   bool match = false;
     83   while (!match) {
     84     int read_size = read(mounts_fd, buffer, buffer_size);
     85     if (read_size <= 0) {
     86       if (errno == -EINTR)
     87         continue;
     88       break;
     89     }
     90     for (int i = 0; i < read_size; ++i) {
     91       if (buffer[i] == '\n') {
     92         match_offset = 0;
     93         continue;
     94       }
     95       if (match_offset < 0) {
     96         continue;
     97       }
     98       if (device_name[match_offset] == '\0') {
     99         if (buffer[i] == ' ') {
    100           match = true;
    101           break;
    102         }
    103         match_offset = -1;
    104         continue;
    105       }
    106 
    107       if (buffer[i] == device_name[match_offset]) {
    108         ++match_offset;
    109       } else {
    110         match_offset = -1;
    111       }
    112     }
    113   }
    114   close(mounts_fd);
    115   *result = match;
    116   return true;
    117 }
    118 
    119 bool MetricsLibrary::IsGuestMode() {
    120   char buffer[256];
    121   bool result = false;
    122   if (!IsDeviceMounted("guestfs",
    123                        "/proc/mounts",
    124                        buffer,
    125                        sizeof(buffer),
    126                        &result)) {
    127     return false;
    128   }
    129   return result && (access("/var/run/state/logged-in", F_OK) == 0);
    130 }
    131 
    132 bool MetricsLibrary::CheckService() {
    133   if (metricsd_proxy_.get() &&
    134       android::IInterface::asBinder(metricsd_proxy_)->isBinderAlive())
    135     return true;
    136 
    137   const String16 name(kMetricsServiceName);
    138   metricsd_proxy_ = android::interface_cast<IMetricsd>(
    139       android::defaultServiceManager()->checkService(name));
    140   return metricsd_proxy_.get();
    141 }
    142 
    143 bool MetricsLibrary::AreMetricsEnabled() {
    144   static struct stat stat_buffer;
    145   time_t this_check_time = time(nullptr);
    146   if (!use_caching_ || this_check_time != cached_enabled_time_) {
    147     cached_enabled_time_ = this_check_time;
    148     cached_enabled_ = stat(consent_file_.value().data(), &stat_buffer) >= 0;
    149   }
    150   return cached_enabled_;
    151 }
    152 
    153 void MetricsLibrary::Init() {
    154   base::FilePath dir = base::FilePath(metrics::kSharedMetricsDirectory);
    155   consent_file_ = dir.Append(metrics::kConsentFileName);
    156   cached_enabled_ = false;
    157   cached_enabled_time_ = 0;
    158   use_caching_ = true;
    159 }
    160 
    161 void MetricsLibrary::InitWithNoCaching() {
    162   Init();
    163   use_caching_ = false;
    164 }
    165 
    166 void MetricsLibrary::InitForTest(const base::FilePath& metrics_directory) {
    167   consent_file_ = metrics_directory.Append(metrics::kConsentFileName);
    168   cached_enabled_ = false;
    169   cached_enabled_time_ = 0;
    170   use_caching_ = true;
    171 }
    172 
    173 bool MetricsLibrary::SendToUMA(
    174     const std::string& name, int sample, int min, int max, int nbuckets) {
    175   return CheckService() &&
    176          metricsd_proxy_->recordHistogram(String16(name.c_str()), sample, min,
    177                                           max, nbuckets)
    178              .isOk();
    179 }
    180 
    181 bool MetricsLibrary::SendEnumToUMA(const std::string& name,
    182                                    int sample,
    183                                    int max) {
    184   return CheckService() &&
    185          metricsd_proxy_->recordLinearHistogram(String16(name.c_str()), sample,
    186                                                 max)
    187              .isOk();
    188 }
    189 
    190 bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
    191   return CheckService() &&
    192          metricsd_proxy_->recordLinearHistogram(String16(name.c_str()),
    193                                                 sample ? 1 : 0, 2)
    194              .isOk();
    195 }
    196 
    197 bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
    198   return CheckService() &&
    199          metricsd_proxy_->recordSparseHistogram(String16(name.c_str()), sample)
    200              .isOk();
    201 }
    202 
    203 bool MetricsLibrary::SendCrashToUMA(const char* crash_kind) {
    204   return CheckService() &&
    205          metricsd_proxy_->recordCrash(String16(crash_kind)).isOk();
    206 }
    207 
    208 bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) {
    209   for (size_t i = 0; i < arraysize(kCrosEventNames); i++) {
    210     if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) {
    211       return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax);
    212     }
    213   }
    214   return false;
    215 }
    216 
    217 bool MetricsLibrary::GetHistogramsDump(std::string* dump) {
    218   android::String16 temp_dump;
    219   if (!CheckService() ||
    220       !metricsd_proxy_->getHistogramsDump(&temp_dump).isOk()) {
    221     return false;
    222   }
    223 
    224   *dump = android::String8(temp_dump).string();
    225   return true;
    226 }
    227