1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sharing/sharing_metrics.h"
6
7 #include "base/metrics/histogram_functions.h"
8 #include "base/strings/strcat.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "chrome/browser/sharing/sharing_device_registration_result.h"
11 #include "components/version_info/version_info.h"
12 #include "content/public/browser/browser_thread.h"
13
14 namespace {
GetEnumStringValue(SharingFeatureName feature)15 const char* GetEnumStringValue(SharingFeatureName feature) {
16 DCHECK(feature != SharingFeatureName::kUnknown)
17 << "Feature needs to be specified for metrics logging.";
18
19 switch (feature) {
20 case SharingFeatureName::kUnknown:
21 return "Unknown";
22 case SharingFeatureName::kClickToCall:
23 return "ClickToCall";
24 case SharingFeatureName::kSharedClipboard:
25 return "SharedClipboard";
26 }
27 }
28
29 // Maps SharingChannelType enum values to strings used as histogram
30 // suffixes. Keep in sync with "SharingChannelType" in histograms.xml.
SharingChannelTypeToString(SharingChannelType channel_type)31 std::string SharingChannelTypeToString(SharingChannelType channel_type) {
32 switch (channel_type) {
33 case SharingChannelType::kUnknown:
34 return "Unknown";
35 case SharingChannelType::kFcmVapid:
36 return "FcmVapid";
37 case SharingChannelType::kFcmSenderId:
38 return "FcmSenderId";
39 case SharingChannelType::kServer:
40 return "Server";
41 case SharingChannelType::kWebRtc:
42 return "WebRTC";
43 }
44 }
45
46 // Maps SharingDevicePlatform enum values to strings used as histogram
47 // suffixes. Keep in sync with "SharingDevicePlatform" in histograms.xml.
DevicePlatformToString(SharingDevicePlatform device_platform)48 std::string DevicePlatformToString(SharingDevicePlatform device_platform) {
49 switch (device_platform) {
50 case SharingDevicePlatform::kAndroid:
51 return "Android";
52 case SharingDevicePlatform::kChromeOS:
53 return "ChromeOS";
54 case SharingDevicePlatform::kIOS:
55 return "iOS";
56 case SharingDevicePlatform::kLinux:
57 return "Linux";
58 case SharingDevicePlatform::kMac:
59 return "Mac";
60 case SharingDevicePlatform::kWindows:
61 return "Windows";
62 case SharingDevicePlatform::kServer:
63 return "Server";
64 case SharingDevicePlatform::kUnknown:
65 return "Unknown";
66 }
67 }
68
69 // Maps pulse intervals to strings used as histogram suffixes. Keep in sync with
70 // "SharingPulseInterval" in histograms.xml.
PulseIntervalToString(base::TimeDelta pulse_interval)71 std::string PulseIntervalToString(base::TimeDelta pulse_interval) {
72 if (pulse_interval < base::TimeDelta::FromHours(4))
73 return "PulseIntervalShort";
74 if (pulse_interval > base::TimeDelta::FromHours(12))
75 return "PulseIntervalLong";
76 return "PulseIntervalMedium";
77 }
78
79 // Major Chrome version comparison with the receiver device.
80 // These values are logged to UMA. Entries should not be renumbered and numeric
81 // values should never be reused. Please keep in sync with
82 // "SharingMajorVersionComparison" in enums.xml.
83 enum class SharingMajorVersionComparison {
84 kUnknown = 0,
85 kSenderIsLower = 1,
86 kSame = 2,
87 kSenderIsHigher = 3,
88 kMaxValue = kSenderIsHigher,
89 };
90 } // namespace
91
SharingSendMessageResultToString(SharingSendMessageResult result)92 std::string SharingSendMessageResultToString(SharingSendMessageResult result) {
93 switch (result) {
94 case SharingSendMessageResult::kSuccessful:
95 return "Successful";
96 case SharingSendMessageResult::kDeviceNotFound:
97 return "DeviceNotFound";
98 case SharingSendMessageResult::kNetworkError:
99 return "NetworkError";
100 case SharingSendMessageResult::kPayloadTooLarge:
101 return "PayloadTooLarge";
102 case SharingSendMessageResult::kAckTimeout:
103 return "AckTimeout";
104 case SharingSendMessageResult::kInternalError:
105 return "InternalError";
106 case SharingSendMessageResult::kEncryptionError:
107 return "EncryptionError";
108 case SharingSendMessageResult::kCommitTimeout:
109 return "CommitTimeout";
110 }
111 }
112
SharingPayloadCaseToMessageType(chrome_browser_sharing::SharingMessage::PayloadCase payload_case)113 chrome_browser_sharing::MessageType SharingPayloadCaseToMessageType(
114 chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
115 switch (payload_case) {
116 case chrome_browser_sharing::SharingMessage::PAYLOAD_NOT_SET:
117 return chrome_browser_sharing::UNKNOWN_MESSAGE;
118 case chrome_browser_sharing::SharingMessage::kPingMessage:
119 return chrome_browser_sharing::PING_MESSAGE;
120 case chrome_browser_sharing::SharingMessage::kAckMessage:
121 return chrome_browser_sharing::ACK_MESSAGE;
122 case chrome_browser_sharing::SharingMessage::kClickToCallMessage:
123 return chrome_browser_sharing::CLICK_TO_CALL_MESSAGE;
124 case chrome_browser_sharing::SharingMessage::kSharedClipboardMessage:
125 return chrome_browser_sharing::SHARED_CLIPBOARD_MESSAGE;
126 case chrome_browser_sharing::SharingMessage::kSmsFetchRequest:
127 return chrome_browser_sharing::SMS_FETCH_REQUEST;
128 case chrome_browser_sharing::SharingMessage::kRemoteCopyMessage:
129 return chrome_browser_sharing::REMOTE_COPY_MESSAGE;
130 case chrome_browser_sharing::SharingMessage::kPeerConnectionOfferMessage:
131 return chrome_browser_sharing::PEER_CONNECTION_OFFER_MESSAGE;
132 case chrome_browser_sharing::SharingMessage::
133 kPeerConnectionIceCandidatesMessage:
134 return chrome_browser_sharing::PEER_CONNECTION_ICE_CANDIDATES_MESSAGE;
135 case chrome_browser_sharing::SharingMessage::kDiscoveryRequest:
136 return chrome_browser_sharing::DISCOVERY_REQUEST;
137 case chrome_browser_sharing::SharingMessage::kWebRtcSignalingFrame:
138 return chrome_browser_sharing::WEB_RTC_SIGNALING_FRAME;
139 }
140 // For proto3 enums unrecognized enum values are kept when parsing, and a new
141 // payload case received over the network would not default to
142 // PAYLOAD_NOT_SET. Explicitly return UNKNOWN_MESSAGE here to handle this
143 // case.
144 return chrome_browser_sharing::UNKNOWN_MESSAGE;
145 }
146
SharingMessageTypeToString(chrome_browser_sharing::MessageType message_type)147 const std::string& SharingMessageTypeToString(
148 chrome_browser_sharing::MessageType message_type) {
149 // For proto3 enums unrecognized enum values are kept when parsing and their
150 // name is an empty string. We don't want to use that as a histogram suffix.
151 if (!chrome_browser_sharing::MessageType_IsValid(message_type)) {
152 return chrome_browser_sharing::MessageType_Name(
153 chrome_browser_sharing::UNKNOWN_MESSAGE);
154 }
155 return chrome_browser_sharing::MessageType_Name(message_type);
156 }
157
GenerateSharingTraceId()158 int GenerateSharingTraceId() {
159 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
160 static int next_id = 0;
161 return next_id++;
162 }
163
LogSharingMessageReceived(chrome_browser_sharing::SharingMessage::PayloadCase payload_case)164 void LogSharingMessageReceived(
165 chrome_browser_sharing::SharingMessage::PayloadCase payload_case) {
166 base::UmaHistogramExactLinear("Sharing.MessageReceivedType",
167 SharingPayloadCaseToMessageType(payload_case),
168 chrome_browser_sharing::MessageType_ARRAYSIZE);
169 }
170
LogSharingRegistrationResult(SharingDeviceRegistrationResult result)171 void LogSharingRegistrationResult(SharingDeviceRegistrationResult result) {
172 base::UmaHistogramEnumeration("Sharing.DeviceRegistrationResult", result);
173 }
174
LogSharingUnegistrationResult(SharingDeviceRegistrationResult result)175 void LogSharingUnegistrationResult(SharingDeviceRegistrationResult result) {
176 base::UmaHistogramEnumeration("Sharing.DeviceUnregistrationResult", result);
177 }
178
LogSharingDevicesToShow(SharingFeatureName feature,const char * histogram_suffix,int count)179 void LogSharingDevicesToShow(SharingFeatureName feature,
180 const char* histogram_suffix,
181 int count) {
182 auto* feature_str = GetEnumStringValue(feature);
183 // Explicitly log both the base and the suffixed histogram because the base
184 // aggregation is not automatically generated.
185 base::UmaHistogramExactLinear(
186 base::StrCat({"Sharing.", feature_str, "DevicesToShow"}), count,
187 /*value_max=*/20);
188 if (!histogram_suffix)
189 return;
190 base::UmaHistogramExactLinear(
191 base::StrCat(
192 {"Sharing.", feature_str, "DevicesToShow.", histogram_suffix}),
193 count,
194 /*value_max=*/20);
195 }
196
LogSharingAppsToShow(SharingFeatureName feature,const char * histogram_suffix,int count)197 void LogSharingAppsToShow(SharingFeatureName feature,
198 const char* histogram_suffix,
199 int count) {
200 auto* feature_str = GetEnumStringValue(feature);
201 // Explicitly log both the base and the suffixed histogram because the base
202 // aggregation is not automatically generated.
203 base::UmaHistogramExactLinear(
204 base::StrCat({"Sharing.", feature_str, "AppsToShow"}), count,
205 /*value_max=*/20);
206 if (!histogram_suffix)
207 return;
208 base::UmaHistogramExactLinear(
209 base::StrCat({"Sharing.", feature_str, "AppsToShow.", histogram_suffix}),
210 count,
211 /*value_max=*/20);
212 }
213
LogSharingSelectedIndex(SharingFeatureName feature,const char * histogram_suffix,int index,SharingIndexType index_type)214 void LogSharingSelectedIndex(SharingFeatureName feature,
215 const char* histogram_suffix,
216 int index,
217 SharingIndexType index_type) {
218 auto* feature_str = GetEnumStringValue(feature);
219 // Explicitly log both the base and the suffixed histogram because the base
220 // aggregation is not automatically generated.
221 std::string name = base::StrCat(
222 {"Sharing.", feature_str, "Selected",
223 (index_type == SharingIndexType::kDevice) ? "Device" : "App", "Index"});
224 base::UmaHistogramExactLinear(name, index, /*value_max=*/20);
225 if (!histogram_suffix)
226 return;
227 base::UmaHistogramExactLinear(base::StrCat({name, ".", histogram_suffix}),
228 index,
229 /*value_max=*/20);
230 }
231
LogSharingMessageAckTime(chrome_browser_sharing::MessageType message_type,SharingDevicePlatform receiver_device_platform,SharingChannelType channel_type,base::TimeDelta time)232 void LogSharingMessageAckTime(chrome_browser_sharing::MessageType message_type,
233 SharingDevicePlatform receiver_device_platform,
234 SharingChannelType channel_type,
235 base::TimeDelta time) {
236 std::string type_suffixed_name = base::StrCat(
237 {"Sharing.MessageAckTime.", SharingMessageTypeToString(message_type)});
238 std::string platform_suffixed_name =
239 base::StrCat({"Sharing.MessageAckTime.",
240 DevicePlatformToString(receiver_device_platform), ".",
241 SharingMessageTypeToString(message_type)});
242 std::string channel_suffixed_name = base::StrCat(
243 {"Sharing.MessageAckTime.", SharingChannelTypeToString(channel_type)});
244 switch (message_type) {
245 case chrome_browser_sharing::MessageType::UNKNOWN_MESSAGE:
246 case chrome_browser_sharing::MessageType::PING_MESSAGE:
247 case chrome_browser_sharing::MessageType::CLICK_TO_CALL_MESSAGE:
248 case chrome_browser_sharing::MessageType::SHARED_CLIPBOARD_MESSAGE:
249 case chrome_browser_sharing::MessageType::PEER_CONNECTION_OFFER_MESSAGE:
250 case chrome_browser_sharing::MessageType::
251 PEER_CONNECTION_ICE_CANDIDATES_MESSAGE:
252 base::UmaHistogramMediumTimes(type_suffixed_name, time);
253 base::UmaHistogramMediumTimes(platform_suffixed_name, time);
254 base::UmaHistogramMediumTimes(channel_suffixed_name, time);
255 break;
256 case chrome_browser_sharing::MessageType::SMS_FETCH_REQUEST:
257 case chrome_browser_sharing::MessageType::DISCOVERY_REQUEST:
258 case chrome_browser_sharing::MessageType::WEB_RTC_SIGNALING_FRAME:
259 base::UmaHistogramCustomTimes(
260 type_suffixed_name, time,
261 /*min=*/base::TimeDelta::FromMilliseconds(1),
262 /*max=*/base::TimeDelta::FromMinutes(10), /*buckets=*/50);
263 base::UmaHistogramCustomTimes(
264 platform_suffixed_name, time,
265 /*min=*/base::TimeDelta::FromMilliseconds(1),
266 /*max=*/base::TimeDelta::FromMinutes(10), /*buckets=*/50);
267 base::UmaHistogramCustomTimes(
268 channel_suffixed_name, time,
269 /*min=*/base::TimeDelta::FromMilliseconds(1),
270 /*max=*/base::TimeDelta::FromMinutes(10), /*buckets=*/50);
271 break;
272 case chrome_browser_sharing::MessageType::ACK_MESSAGE:
273 default:
274 // For proto3 enums unrecognized enum values are kept, so message_type may
275 // not fall into any switch case. However, as an ack message, original
276 // message type should always be known.
277 NOTREACHED();
278 }
279 }
280
LogSharingMessageHandlerTime(chrome_browser_sharing::MessageType message_type,base::TimeDelta time_taken)281 void LogSharingMessageHandlerTime(
282 chrome_browser_sharing::MessageType message_type,
283 base::TimeDelta time_taken) {
284 base::UmaHistogramMediumTimes(
285 base::StrCat({"Sharing.MessageHandlerTime.",
286 SharingMessageTypeToString(message_type)}),
287 time_taken);
288 }
289
LogSharingDeviceLastUpdatedAge(chrome_browser_sharing::MessageType message_type,base::TimeDelta age)290 void LogSharingDeviceLastUpdatedAge(
291 chrome_browser_sharing::MessageType message_type,
292 base::TimeDelta age) {
293 constexpr char kBase[] = "Sharing.DeviceLastUpdatedAge";
294 int hours = age.InHours();
295 base::UmaHistogramCounts1000(kBase, hours);
296 base::UmaHistogramCounts1000(
297 base::StrCat({kBase, ".", SharingMessageTypeToString(message_type)}),
298 hours);
299 }
300
LogSharingDeviceLastUpdatedAgeWithResult(SharingSendMessageResult result,base::TimeDelta age)301 void LogSharingDeviceLastUpdatedAgeWithResult(SharingSendMessageResult result,
302 base::TimeDelta age) {
303 base::UmaHistogramCounts1000(
304 base::StrCat({"Sharing.DeviceLastUpdatedAgeWithResult.",
305 SharingSendMessageResultToString(result)}),
306 age.InHours());
307 }
308
LogSharingVersionComparison(chrome_browser_sharing::MessageType message_type,const std::string & receiver_version)309 void LogSharingVersionComparison(
310 chrome_browser_sharing::MessageType message_type,
311 const std::string& receiver_version) {
312 int sender_major = 0;
313 base::StringToInt(version_info::GetMajorVersionNumber(), &sender_major);
314
315 // The |receiver_version| has optional modifiers e.g. "1.2.3.4 canary" so we
316 // do not parse it with base::Version.
317 int receiver_major = 0;
318 base::StringToInt(receiver_version, &receiver_major);
319
320 SharingMajorVersionComparison result;
321 if (sender_major == 0 || sender_major == INT_MIN || sender_major == INT_MAX ||
322 receiver_major == 0 || receiver_major == INT_MIN ||
323 receiver_major == INT_MAX) {
324 result = SharingMajorVersionComparison::kUnknown;
325 } else if (sender_major < receiver_major) {
326 result = SharingMajorVersionComparison::kSenderIsLower;
327 } else if (sender_major == receiver_major) {
328 result = SharingMajorVersionComparison::kSame;
329 } else {
330 result = SharingMajorVersionComparison::kSenderIsHigher;
331 }
332 constexpr char kBase[] = "Sharing.MajorVersionComparison";
333 base::UmaHistogramEnumeration(kBase, result);
334 base::UmaHistogramEnumeration(
335 base::StrCat({kBase, ".", SharingMessageTypeToString(message_type)}),
336 result);
337 }
338
LogSharingDialogShown(SharingFeatureName feature,SharingDialogType type)339 void LogSharingDialogShown(SharingFeatureName feature, SharingDialogType type) {
340 base::UmaHistogramEnumeration(
341 base::StrCat({"Sharing.", GetEnumStringValue(feature), "DialogShown"}),
342 type);
343 }
344
LogSendSharingMessageResult(chrome_browser_sharing::MessageType message_type,SharingDevicePlatform receiving_device_platform,SharingChannelType channel_type,base::TimeDelta pulse_interval,SharingSendMessageResult result)345 void LogSendSharingMessageResult(
346 chrome_browser_sharing::MessageType message_type,
347 SharingDevicePlatform receiving_device_platform,
348 SharingChannelType channel_type,
349 base::TimeDelta pulse_interval,
350 SharingSendMessageResult result) {
351 const std::string metric_prefix = "Sharing.SendMessageResult";
352
353 base::UmaHistogramEnumeration(metric_prefix, result);
354
355 base::UmaHistogramEnumeration(
356 base::StrCat(
357 {metric_prefix, ".", SharingMessageTypeToString(message_type)}),
358 result);
359
360 base::UmaHistogramEnumeration(
361 base::StrCat({metric_prefix, ".",
362 DevicePlatformToString(receiving_device_platform)}),
363 result);
364
365 base::UmaHistogramEnumeration(
366 base::StrCat({metric_prefix, ".",
367 DevicePlatformToString(receiving_device_platform), ".",
368 SharingMessageTypeToString(message_type)}),
369 result);
370
371 base::UmaHistogramEnumeration(
372 base::StrCat(
373 {metric_prefix, ".", SharingChannelTypeToString(channel_type)}),
374 result);
375
376 // There is no "invalid" bucket so only log valid pulse intervals.
377 if (!pulse_interval.is_zero()) {
378 base::UmaHistogramEnumeration(
379 base::StrCat(
380 {metric_prefix, ".", PulseIntervalToString(pulse_interval)}),
381 result);
382 base::UmaHistogramEnumeration(
383 base::StrCat({metric_prefix, ".",
384 DevicePlatformToString(receiving_device_platform), ".",
385 PulseIntervalToString(pulse_interval)}),
386 result);
387 }
388 }
389
LogSendSharingAckMessageResult(chrome_browser_sharing::MessageType message_type,SharingDevicePlatform ack_receiver_device_type,SharingChannelType channel_type,SharingSendMessageResult result)390 void LogSendSharingAckMessageResult(
391 chrome_browser_sharing::MessageType message_type,
392 SharingDevicePlatform ack_receiver_device_type,
393 SharingChannelType channel_type,
394 SharingSendMessageResult result) {
395 const std::string metric_prefix = "Sharing.SendAckMessageResult";
396
397 base::UmaHistogramEnumeration(metric_prefix, result);
398
399 base::UmaHistogramEnumeration(
400 base::StrCat(
401 {metric_prefix, ".", SharingMessageTypeToString(message_type)}),
402 result);
403
404 base::UmaHistogramEnumeration(
405 base::StrCat({metric_prefix, ".",
406 DevicePlatformToString(ack_receiver_device_type)}),
407 result);
408
409 base::UmaHistogramEnumeration(
410 base::StrCat({metric_prefix, ".",
411 DevicePlatformToString(ack_receiver_device_type), ".",
412 SharingMessageTypeToString(message_type)}),
413 result);
414
415 base::UmaHistogramEnumeration(
416 base::StrCat(
417 {metric_prefix, ".", SharingChannelTypeToString(channel_type)}),
418 result);
419 }
420
LogSharedClipboardSelectedTextSize(size_t size)421 void LogSharedClipboardSelectedTextSize(size_t size) {
422 base::UmaHistogramCounts100000("Sharing.SharedClipboardSelectedTextSize",
423 size);
424 }
425
LogSharedClipboardRetries(int retries,SharingSendMessageResult result)426 void LogSharedClipboardRetries(int retries, SharingSendMessageResult result) {
427 constexpr char kBase[] = "Sharing.SharedClipboardRetries";
428 base::UmaHistogramExactLinear(kBase, retries, /*value_max=*/20);
429 base::UmaHistogramExactLinear(
430 base::StrCat({kBase, ".", SharingSendMessageResultToString(result)}),
431 retries,
432 /*value_max=*/20);
433 }
434
LogRemoteCopyHandleMessageResult(RemoteCopyHandleMessageResult result)435 void LogRemoteCopyHandleMessageResult(RemoteCopyHandleMessageResult result) {
436 base::UmaHistogramEnumeration("Sharing.RemoteCopyHandleMessageResult",
437 result);
438 }
439
LogRemoteCopyReceivedTextSize(size_t size)440 void LogRemoteCopyReceivedTextSize(size_t size) {
441 base::UmaHistogramCounts100000("Sharing.RemoteCopyReceivedTextSize", size);
442 }
443
LogRemoteCopyReceivedImageSizeBeforeDecode(size_t size)444 void LogRemoteCopyReceivedImageSizeBeforeDecode(size_t size) {
445 base::UmaHistogramCounts10M("Sharing.RemoteCopyReceivedImageSizeBeforeDecode",
446 size);
447 }
448
LogRemoteCopyReceivedImageSizeAfterDecode(size_t size)449 void LogRemoteCopyReceivedImageSizeAfterDecode(size_t size) {
450 base::UmaHistogramCustomCounts(
451 "Sharing.RemoteCopyReceivedImageSizeAfterDecode", size, 1, 100000000, 50);
452 }
453
LogRemoteCopyLoadImageStatusCode(int code)454 void LogRemoteCopyLoadImageStatusCode(int code) {
455 base::UmaHistogramSparse("Sharing.RemoteCopyLoadImageStatusCode", code);
456 }
457
LogRemoteCopyLoadImageTime(base::TimeDelta time)458 void LogRemoteCopyLoadImageTime(base::TimeDelta time) {
459 base::UmaHistogramMediumTimes("Sharing.RemoteCopyLoadImageTime", time);
460 }
461
LogRemoteCopyDecodeImageTime(base::TimeDelta time)462 void LogRemoteCopyDecodeImageTime(base::TimeDelta time) {
463 base::UmaHistogramMediumTimes("Sharing.RemoteCopyDecodeImageTime", time);
464 }
465
LogRemoteCopyResizeImageTime(base::TimeDelta time)466 void LogRemoteCopyResizeImageTime(base::TimeDelta time) {
467 base::UmaHistogramMediumTimes("Sharing.RemoteCopyResizeImageTime", time);
468 }
469
LogRemoteCopyWriteTime(base::TimeDelta time,bool is_image)470 void LogRemoteCopyWriteTime(base::TimeDelta time, bool is_image) {
471 if (is_image)
472 base::UmaHistogramMediumTimes("Sharing.RemoteCopyWriteImageTime", time);
473 else
474 base::UmaHistogramMediumTimes("Sharing.RemoteCopyWriteTextTime", time);
475 }
476
LogRemoteCopyWriteDetectionTime(base::TimeDelta time,bool is_image)477 void LogRemoteCopyWriteDetectionTime(base::TimeDelta time, bool is_image) {
478 if (is_image)
479 base::UmaHistogramTimes("Sharing.RemoteCopyWriteImageDetectionTime", time);
480 else
481 base::UmaHistogramTimes("Sharing.RemoteCopyWriteTextDetectionTime", time);
482 }
483
LogSharingDeviceInfoAvailable(bool available)484 void LogSharingDeviceInfoAvailable(bool available) {
485 base::UmaHistogramBoolean("Sharing.DeviceInfoAvailable", available);
486 }
487