1 //
2 // Copyright (C) 2022 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 #include <sys/utsname.h>
17 #include <uuid.h>
18 
19 #include "common/libs/utils/files.h"
20 #include "common/libs/utils/flag_parser.h"
21 #include "host/commands/metrics/events.h"
22 #include "host/commands/metrics/metrics_defs.h"
23 #include "host/commands/metrics/proto/cf_metrics_protos.h"
24 #include "host/commands/metrics/utils.h"
25 #include "host/libs/config/cuttlefish_config.h"
26 #include "host/libs/vm_manager/crosvm_manager.h"
27 #include "host/libs/vm_manager/qemu_manager.h"
28 #include "shared/api_level.h"
29 
30 namespace cuttlefish {
31 
32 namespace {
33 
34 constexpr int kLogSourceId = 1753;
35 
36 constexpr char kLogSourceStr[] = "CUTTLEFISH_METRICS";
37 constexpr int kCppClientType =
38     19;  // C++ native client type (clientanalytics.proto)
39 
ConvertMillisToTime(uint64_t millis)40 std::pair<uint64_t, uint64_t> ConvertMillisToTime(uint64_t millis) {
41   uint64_t seconds = millis / 1000;
42   uint64_t nanos = (millis % 1000) * 1000000;
43   return {seconds, nanos};
44 }
45 
BuildCfLogEvent(uint64_t now_ms,CuttlefishLogEvent::DeviceType device_type)46 std::unique_ptr<CuttlefishLogEvent> BuildCfLogEvent(
47     uint64_t now_ms, CuttlefishLogEvent::DeviceType device_type) {
48   auto [now_s, now_ns] = ConvertMillisToTime(now_ms);
49 
50   // "cfEvent" is the top level CuttlefishLogEvent
51   auto cfEvent = std::make_unique<CuttlefishLogEvent>();
52   cfEvent->set_device_type(device_type);
53   cfEvent->set_session_id(metrics::GenerateSessionId(now_ms));
54 
55   if (!metrics::GetCfVersion().empty()) {
56     cfEvent->set_cuttlefish_version(metrics::GetCfVersion());
57   }
58 
59   Timestamp* timestamp = cfEvent->mutable_timestamp_ms();
60   timestamp->set_seconds(now_s);
61   timestamp->set_nanos(now_ns);
62 
63   return cfEvent;
64 }
65 
GetOsType()66 cuttlefish::MetricsEvent::OsType GetOsType() {
67   struct utsname buf;
68   if (uname(&buf) != 0) {
69     LOG(ERROR) << "failed to retrieve system information";
70     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
71   }
72   std::string sysname(buf.sysname);
73   std::string machine(buf.machine);
74 
75   if (sysname != "Linux") {
76     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
77   }
78   if (machine == "x86_64") {
79     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86_64;
80   }
81   if (machine == "x86") {
82     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_X86;
83   }
84   if (machine == "aarch64" || machine == "arm64") {
85     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH64;
86   }
87   if (machine[0] == 'a') {
88     return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_LINUX_AARCH32;
89   }
90   return cuttlefish::MetricsEvent::CUTTLEFISH_OS_TYPE_UNSPECIFIED;
91 }
92 
GetVmmManager()93 cuttlefish::MetricsEvent::VmmType GetVmmManager() {
94   auto config = cuttlefish::CuttlefishConfig::Get();
95   CHECK(config) << "Could not open cuttlefish config";
96   auto vmm = config->vm_manager();
97   if (vmm == cuttlefish::VmmMode::kCrosvm) {
98     return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_CROSVM;
99   }
100   if (vmm == cuttlefish::VmmMode::kQemu) {
101     return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_QEMU;
102   }
103   return cuttlefish::MetricsEvent::CUTTLEFISH_VMM_TYPE_UNSPECIFIED;
104 }
105 
106 // Builds the 2nd level MetricsEvent.
AddCfMetricsEventToLog(uint64_t now_ms,CuttlefishLogEvent * cfEvent,MetricsEvent::EventType event_type)107 void AddCfMetricsEventToLog(uint64_t now_ms, CuttlefishLogEvent* cfEvent,
108                             MetricsEvent::EventType event_type) {
109   auto [now_s, now_ns] = ConvertMillisToTime(now_ms);
110 
111   // "metrics_event" is the 2nd level MetricsEvent
112   cuttlefish::MetricsEvent* metrics_event = cfEvent->mutable_metrics_event();
113   metrics_event->set_event_type(event_type);
114   metrics_event->set_os_type(GetOsType());
115   metrics_event->set_os_version(metrics::GetOsVersion());
116   metrics_event->set_vmm_type(GetVmmManager());
117 
118   if (!metrics::GetVmmVersion().empty()) {
119     metrics_event->set_vmm_version(metrics::GetVmmVersion());
120   }
121 
122   metrics_event->set_company(metrics::GetCompany());
123   metrics_event->set_api_level(PRODUCT_SHIPPING_API_LEVEL);
124 
125   Timestamp* metrics_timestamp = metrics_event->mutable_event_time_ms();
126   metrics_timestamp->set_seconds(now_s);
127   metrics_timestamp->set_nanos(now_ns);
128 }
129 
BuildLogRequest(uint64_t now_ms,CuttlefishLogEvent * cfEvent)130 std::unique_ptr<LogRequest> BuildLogRequest(uint64_t now_ms,
131                                             CuttlefishLogEvent* cfEvent) {
132   // "log_request" is the top level LogRequest
133   auto log_request = std::make_unique<LogRequest>();
134   log_request->set_request_time_ms(now_ms);
135   log_request->set_log_source(kLogSourceId);
136   log_request->set_log_source_name(kLogSourceStr);
137 
138   ClientInfo* client_info = log_request->mutable_client_info();
139   client_info->set_client_type(kCppClientType);
140 
141   std::string cfLogStr;
142   if (!cfEvent->SerializeToString(&cfLogStr)) {
143     LOG(ERROR) << "Serialization failed for event";
144     return nullptr;
145   }
146 
147   LogEvent* logEvent = log_request->add_log_event();
148   logEvent->set_event_time_ms(now_ms);
149   logEvent->set_source_extension(cfLogStr);
150 
151   return log_request;
152 }
153 }  // namespace
154 
SendEvent(CuttlefishLogEvent::DeviceType device_type,MetricsEvent::EventType event_type)155 int Clearcut::SendEvent(CuttlefishLogEvent::DeviceType device_type,
156                         MetricsEvent::EventType event_type) {
157   uint64_t now_ms = metrics::GetEpochTimeMs();
158 
159   auto cfEvent = BuildCfLogEvent(now_ms, device_type);
160   AddCfMetricsEventToLog(now_ms, cfEvent.get(), event_type);
161 
162   auto logRequest = BuildLogRequest(now_ms, cfEvent.get());
163   if (!logRequest) {
164     LOG(ERROR) << "Failed to build LogRequest";
165     return MetricsExitCodes::kMetricsError;
166   }
167 
168   std::string logRequestStr;
169   if (!logRequest->SerializeToString(&logRequestStr)) {
170     LOG(ERROR) << "Serialization failed for LogRequest";
171     return MetricsExitCodes::kMetricsError;
172   }
173 
174   return metrics::PostRequest(logRequestStr, metrics::kProd);
175 }
176 
SendVMStart(CuttlefishLogEvent::DeviceType device)177 int Clearcut::SendVMStart(CuttlefishLogEvent::DeviceType device) {
178   return SendEvent(device,
179                    MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_INSTANTIATION);
180 }
181 
SendVMStop(CuttlefishLogEvent::DeviceType device)182 int Clearcut::SendVMStop(CuttlefishLogEvent::DeviceType device) {
183   return SendEvent(device, MetricsEvent::CUTTLEFISH_EVENT_TYPE_VM_STOP);
184 }
185 
SendDeviceBoot(CuttlefishLogEvent::DeviceType device)186 int Clearcut::SendDeviceBoot(CuttlefishLogEvent::DeviceType device) {
187   return SendEvent(device, MetricsEvent::CUTTLEFISH_EVENT_TYPE_DEVICE_BOOT);
188 }
189 
SendLockScreen(CuttlefishLogEvent::DeviceType device)190 int Clearcut::SendLockScreen(CuttlefishLogEvent::DeviceType device) {
191   return SendEvent(device,
192                    MetricsEvent::CUTTLEFISH_EVENT_TYPE_LOCK_SCREEN_AVAILABLE);
193 }
194 
195 // TODO (moelsherif@): remove this function in the future since it is not used
sampleEvent()196 cuttlefish::CuttlefishLogEvent* sampleEvent() {
197   cuttlefish::CuttlefishLogEvent* event = new cuttlefish::CuttlefishLogEvent();
198   event->set_device_type(
199       cuttlefish::CuttlefishLogEvent::CUTTLEFISH_DEVICE_TYPE_HOST);
200   return event;
201 }
202 
203 // TODO (moelsherif@): remove this function in the future since it is not used
ProtoToString(LogEvent * event)204 std::string ProtoToString(LogEvent* event) {
205   std::string output;
206   if (!event->SerializeToString(&output)) {
207     LOG(ERROR) << "failed to serialize proto LogEvent";
208   }
209   return output;
210 }
211 
212 }  // namespace cuttlefish