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