| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/capture_mode/capture_mode_metrics.h" |
| |
| #include "ash/capture_mode/capture_mode_behavior.h" |
| #include "ash/capture_mode/capture_mode_types.h" |
| #include "ash/shell.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/time/time.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kCaptureModeMetricCommonPrefix[] = "Ash.CaptureModeController."; |
| |
| constexpr char kEndRecordingReasonHistogramRootWord[] = "EndRecordingReason"; |
| constexpr char kBarButtonHistogramRootWord[] = "BarButtons"; |
| constexpr char kCaptureAudioRecordingModeHistogramRootWord[] = |
| "AudioRecordingMode"; |
| constexpr char kCaptureConfigurationHistogramRootWord[] = |
| "CaptureConfiguration"; |
| constexpr char kCaptureRegionAdjustmentHistogramRootWord[] = |
| "CaptureRegionAdjusted"; |
| constexpr char kDemoToolsEnabledOnRecordingStartRootWord[] = |
| "DemoToolsEnabledOnRecordingStart"; |
| constexpr char kEntryPointHistogramRootWord[] = "EntryPoint"; |
| constexpr char kRecordingDurationHistogramRootWord[] = "ScreenRecordingLength"; |
| constexpr char kGifRecordingDurationHistogramRootWord[] = "GIFRecordingLength"; |
| constexpr char kGifRecordingRegionToScreenRatioHistogramRootWord[] = |
| "GIFRecordingRegionToScreenRatio"; |
| constexpr char kSaveToLocationHistogramRootWord[] = "SaveLocation"; |
| constexpr char kSwitchToDefaultFolderReasonHistogramRootWord[] = |
| "SwitchToDefaultReason"; |
| constexpr char kRecordingStartsWithCameraRootWord[] = |
| "RecordingStartsWithCamera"; |
| constexpr char kCameraDisconnectionsDuringRecordingsRootWord[] = |
| "CameraDisconnectionsDuringRecordings"; |
| constexpr char kCameraReconnectDurationRootWord[] = "CameraReconnectDuration"; |
| constexpr char kRecordingCameraSizeOnStartRootWord[] = |
| "RecordingCameraSizeOnStart"; |
| constexpr char kRecordingCameraPositionOnStartRootWord[] = |
| "RecordingCameraPositionOnStart"; |
| constexpr char kGifRecordingFileSizeRootWord[] = "GIFRecordingFileSize"; |
| constexpr char kScreenRecordingFileSizeRootWord[] = "ScreenRecordingFileSize"; |
| constexpr char kNumberOfConnectedCamerasRootWord[] = "NumberOfConnectedCameras"; |
| constexpr char kConsecutiveScreenshotRootWord[] = "ConsecutiveScreenshots"; |
| constexpr char kQuickActionRootWord[] = "QuickAction"; |
| constexpr char kScreenshotsPerDayRootWord[] = "ScreenshotsPerDay"; |
| constexpr char kScreenshotsPerWeekRootWord[] = "ScreenshotsPerWeek"; |
| constexpr char kSwitchesFromInitialModeRootWord[] = |
| "SwitchesFromInitialCaptureMode"; |
| |
| void RecordCaptureModeRecordingDurationInternal( |
| const std::string& histogram_name, |
| base::TimeDelta recording_duration) { |
| // Use the custom counts function instead of custom times so we can record in |
| // seconds instead of milliseconds. The max bucket is 3 hours. |
| base::UmaHistogramCustomCounts(histogram_name, recording_duration.InSeconds(), |
| /*min=*/1, |
| /*exclusive_max=*/base::Hours(3).InSeconds(), |
| /*buckets=*/50); |
| } |
| |
| } // namespace |
| |
| void RecordEndRecordingReason(EndRecordingReason reason) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kEndRecordingReasonHistogramRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| reason); |
| } |
| |
| void RecordCaptureModeBarButtonType(CaptureModeBarButtonType button_type) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kBarButtonHistogramRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| button_type); |
| } |
| |
| void RecordCaptureModeConfiguration(CaptureModeType type, |
| CaptureModeSource source, |
| RecordingType recording_type, |
| AudioRecordingMode audio_mode, |
| const CaptureModeBehavior* behavior) { |
| std::string configuration_histogram_name = |
| BuildHistogramName(kCaptureConfigurationHistogramRootWord, behavior, |
| /*append_ui_mode_suffix=*/true); |
| base::UmaHistogramEnumeration(configuration_histogram_name, |
| GetConfiguration(type, source, recording_type)); |
| if (type == CaptureModeType::kVideo && |
| recording_type != RecordingType::kGif) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kCaptureAudioRecordingModeHistogramRootWord, |
| behavior, |
| /*append_ui_mode_suffix=*/true), |
| audio_mode); |
| } |
| } |
| |
| void RecordGifRegionToScreenRatio(float ratio_percent) { |
| base::UmaHistogramPercentage( |
| BuildHistogramName(kGifRecordingRegionToScreenRatioHistogramRootWord, |
| /*behavior=*/nullptr, /*append_ui_mode_suffix=*/true), |
| ratio_percent); |
| } |
| |
| void RecordCaptureModeEntryType(CaptureModeEntryType entry_type) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kEntryPointHistogramRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| entry_type); |
| } |
| |
| void RecordCaptureModeRecordingDuration(base::TimeDelta recording_duration, |
| const CaptureModeBehavior* behavior, |
| bool is_gif) { |
| RecordCaptureModeRecordingDurationInternal( |
| BuildHistogramName(!behavior->ShouldGifBeSupported() || !is_gif |
| ? kRecordingDurationHistogramRootWord |
| : kGifRecordingDurationHistogramRootWord, |
| behavior, |
| /*append_ui_mode_suffix=*/true), |
| recording_duration); |
| } |
| |
| void RecordVideoFileSizeKB(bool is_gif, |
| const CaptureModeBehavior* behavior, |
| int size_in_kb) { |
| if (!Shell::HasInstance()) { |
| // This function can be called asynchronously after the `Shell` instance had |
| // already been destroyed. |
| return; |
| } |
| |
| if (size_in_kb < 0) { |
| LOG(ERROR) << "Failed to calculate the video file size. Is GIF: " << is_gif; |
| return; |
| } |
| |
| base::UmaHistogramMemoryKB( |
| BuildHistogramName(is_gif ? kGifRecordingFileSizeRootWord |
| : kScreenRecordingFileSizeRootWord, |
| behavior, /*append_ui_mode_suffix=*/true), |
| size_in_kb); |
| } |
| |
| void RecordCaptureModeSwitchesFromInitialMode(bool switched) { |
| base::UmaHistogramBoolean( |
| BuildHistogramName(kSwitchesFromInitialModeRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| switched); |
| } |
| |
| void RecordNumberOfCaptureRegionAdjustments( |
| int num_adjustments, |
| const CaptureModeBehavior* behavior) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kCaptureRegionAdjustmentHistogramRootWord, behavior, |
| /*append_ui_mode_suffix=*/true), |
| num_adjustments); |
| } |
| |
| void RecordNumberOfConsecutiveScreenshots(int num_consecutive_screenshots) { |
| if (num_consecutive_screenshots > 1) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kConsecutiveScreenshotRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| num_consecutive_screenshots); |
| } |
| } |
| |
| void RecordNumberOfScreenshotsTakenInLastDay( |
| int num_screenshots_taken_in_last_day) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kScreenshotsPerDayRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| num_screenshots_taken_in_last_day); |
| } |
| |
| void RecordNumberOfScreenshotsTakenInLastWeek( |
| int num_screenshots_taken_in_last_week) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kScreenshotsPerWeekRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| num_screenshots_taken_in_last_week); |
| } |
| |
| void RecordScreenshotNotificationQuickAction(CaptureQuickAction action) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kQuickActionRootWord, /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| action); |
| } |
| |
| void RecordSaveToLocation(CaptureModeSaveToLocation save_location, |
| const CaptureModeBehavior* behavior) { |
| // Save-to location metrics should not be recorded for the |
| // projector-inititated capture mode session. |
| const CaptureModeBehavior* modified_behavior = |
| behavior->behavior_type() == BehaviorType::kProjector ? nullptr |
| : behavior; |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kSaveToLocationHistogramRootWord, modified_behavior, |
| /*append_ui_mode_suffix=*/true), |
| save_location); |
| } |
| |
| void RecordSwitchToDefaultFolderReason( |
| CaptureModeSwitchToDefaultReason reason) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kSwitchToDefaultFolderReasonHistogramRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| reason); |
| } |
| |
| CaptureModeConfiguration GetConfiguration(CaptureModeType type, |
| CaptureModeSource source, |
| RecordingType recording_type) { |
| switch (source) { |
| case CaptureModeSource::kFullscreen: |
| return type == CaptureModeType::kImage |
| ? CaptureModeConfiguration::kFullscreenScreenshot |
| : CaptureModeConfiguration::kFullscreenRecording; |
| case CaptureModeSource::kRegion: |
| if (type == CaptureModeType::kImage) { |
| return CaptureModeConfiguration::kRegionScreenshot; |
| } |
| |
| return recording_type == RecordingType::kGif |
| ? CaptureModeConfiguration::kRegionGifRecording |
| : CaptureModeConfiguration::kRegionRecording; |
| case CaptureModeSource::kWindow: |
| return type == CaptureModeType::kImage |
| ? CaptureModeConfiguration::kWindowScreenshot |
| : CaptureModeConfiguration::kWindowRecording; |
| } |
| } |
| |
| void RecordRecordingStartsWithCamera(bool starts_with_camera, |
| const CaptureModeBehavior* behavior) { |
| base::UmaHistogramBoolean( |
| BuildHistogramName(kRecordingStartsWithCameraRootWord, behavior, |
| /*append_ui_mode_suffix=*/true), |
| starts_with_camera); |
| } |
| |
| void RecordCameraDisconnectionsDuringRecordings(int num_camera_disconnections) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kCameraDisconnectionsDuringRecordingsRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| num_camera_disconnections); |
| } |
| |
| void RecordNumberOfConnectedCameras(int num_camera_connected) { |
| base::UmaHistogramCounts100( |
| BuildHistogramName(kNumberOfConnectedCamerasRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/false), |
| num_camera_connected); |
| } |
| |
| void RecordCameraReconnectDuration(int length_in_seconds, |
| int grace_period_in_seconds) { |
| base::UmaHistogramCustomCounts( |
| BuildHistogramName(kCameraReconnectDurationRootWord, nullptr, |
| /*append_ui_mode_suffix=*/true), |
| length_in_seconds, 0, grace_period_in_seconds, grace_period_in_seconds); |
| } |
| |
| void RecordCameraSizeOnStart(CaptureModeCameraSize camera_size) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kRecordingCameraSizeOnStartRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| camera_size); |
| } |
| |
| void RecordCameraPositionOnStart(CameraPreviewSnapPosition camera_position) { |
| base::UmaHistogramEnumeration( |
| BuildHistogramName(kRecordingCameraPositionOnStartRootWord, |
| /*behavior=*/nullptr, |
| /*append_ui_mode_suffix=*/true), |
| camera_position); |
| } |
| |
| void RecordRecordingStartsWithDemoTools(bool demo_tools_enabled, |
| const CaptureModeBehavior* behavior) { |
| base::UmaHistogramBoolean( |
| BuildHistogramName(kDemoToolsEnabledOnRecordingStartRootWord, behavior, |
| /*append_ui_mode_suffix=*/true), |
| demo_tools_enabled); |
| } |
| |
| std::string BuildHistogramName(const char* const root_word, |
| const CaptureModeBehavior* behavior, |
| bool append_ui_mode_suffix) { |
| std::string histogram_name(kCaptureModeMetricCommonPrefix); |
| if (behavior) { |
| histogram_name.append(behavior->GetClientMetricComponent()); |
| } |
| histogram_name.append(root_word); |
| if (append_ui_mode_suffix) { |
| histogram_name.append(Shell::Get()->IsInTabletMode() ? ".TabletMode" |
| : ".ClamshellMode"); |
| } |
| return histogram_name; |
| } |
| |
| } // namespace ash |