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