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 
MetricsLibrary()62 MetricsLibrary::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|.
IsDeviceMounted(const char * device_name,const char * mounts_file,char * buffer,int buffer_size,bool * result)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 
IsGuestMode()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 
CheckService()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 
AreMetricsEnabled()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 
Init()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 
InitWithNoCaching()161 void MetricsLibrary::InitWithNoCaching() {
162   Init();
163   use_caching_ = false;
164 }
165 
InitForTest(const base::FilePath & metrics_directory)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 
SendToUMA(const std::string & name,int sample,int min,int max,int nbuckets)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 
SendEnumToUMA(const std::string & name,int sample,int max)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 
SendBoolToUMA(const std::string & name,bool sample)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 
SendSparseToUMA(const std::string & name,int sample)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 
SendCrashToUMA(const char * crash_kind)203 bool MetricsLibrary::SendCrashToUMA(const char* crash_kind) {
204   return CheckService() &&
205          metricsd_proxy_->recordCrash(String16(crash_kind)).isOk();
206 }
207 
SendCrosEventToUMA(const std::string & event)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 
GetHistogramsDump(std::string * dump)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