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