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