1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
|
// 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.
#ifndef ASH_CAPTURE_MODE_CAPTURE_MODE_CONTROLLER_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_CONTROLLER_H_
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_education_controller.h"
#include "ash/capture_mode/capture_mode_metrics.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/capture_mode/video_recording_watcher.h"
#include "ash/public/cpp/capture_mode/capture_mode_delegate.h"
#include "ash/public/cpp/session/session_observer.h"
#include "ash/scanner/scanner_session.h"
#include "ash/shell.h"
#include "ash/shell_observer.h"
#include "base/files/file_path.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted_memory.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/services/recording/public/mojom/recording_service.mojom.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom-forward.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
class PrefRegistrySimple;
namespace base {
class FilePath;
class Time;
class SequencedTaskRunner;
} // namespace base
namespace network {
class SimpleURLLoader;
class SharedURLLoaderFactory;
} // namespace network
namespace ash {
class CaptureModeBehavior;
class CaptureModeCameraController;
class CaptureModeObserver;
class BaseCaptureModeSession;
class SearchResultsPanel;
// Defines a callback type that will be invoked when an attempt to delete the
// given `path` is completed with the given status `delete_successful`.
using OnFileDeletedCallback =
base::OnceCallback<void(const base::FilePath& path,
bool delete_successful)>;
// Defines a callback type that will be invoked when the status of the capture
// mode session initialization process is determined with the given status of
// `success`.
using OnSessionStartAttemptCallback = base::OnceCallback<void(bool success)>;
// Controls starting and ending a Capture Mode session and its behavior. There
// are various checks that are run when a capture session start is attempted,
// and when a capture operation is performed, to make sure they're allowed. For
// example, checking that policy allows screen capture, and there are no content
// on the screen restricted by DLP (Data Leak Prevention). In the case of video
// recording, HDCP is also checked to ensure no protected content is being
// recorded.
class ASH_EXPORT CaptureModeController
: public recording::mojom::RecordingServiceClient,
public recording::mojom::DriveFsQuotaDelegate,
public SessionObserver,
public chromeos::PowerManagerClient::Observer,
public crosapi::mojom::VideoConferenceManagerClient,
public ShellObserver {
public:
// Contains info about the folder used for saving the captured images and
// videos.
struct CaptureFolder {
// The absolute path of the folder used for saving the captures.
base::FilePath path;
// True if the above `path` is the default "Downloads" folder on the device.
bool is_default_downloads_folder = false;
};
explicit CaptureModeController(std::unique_ptr<CaptureModeDelegate> delegate);
CaptureModeController(const CaptureModeController&) = delete;
CaptureModeController& operator=(const CaptureModeController&) = delete;
~CaptureModeController() override;
// Convenience function to get the controller instance, which is created and
// owned by Shell.
static CaptureModeController* Get();
static void RegisterProfilePrefs(PrefRegistrySimple* registry);
CaptureModeCameraController* camera_controller() {
return camera_controller_.get();
}
CaptureModeType type() const { return type_; }
CaptureModeSource source() const { return source_; }
RecordingType recording_type() const { return recording_type_; }
// Returns the raw audio recording mode, without taking into account the
// `AudioCaptureAllowed` policy.
AudioRecordingMode audio_recording_mode() const {
return audio_recording_mode_;
}
BaseCaptureModeSession* capture_mode_session() const {
return capture_mode_session_.get();
}
gfx::Rect user_capture_region() const { return user_capture_region_; }
bool is_recording_in_progress() const {
return is_initializing_recording_ ||
(video_recording_watcher_ &&
!video_recording_watcher_->is_shutting_down());
}
bool can_start_new_recording() const {
// Even if recording stops, it takes a while for the video/gif file to be
// fully finalized. Until that happens, a new recording is not allowed to
// start.
return current_video_file_path_.empty() && !is_recording_in_progress();
}
bool enable_demo_tools() const { return enable_demo_tools_; }
CaptureModeEducationController* education_controller() {
return education_controller_.get();
}
views::Widget* search_results_panel_widget() {
return search_results_panel_widget_.get();
}
// Returns the search results panel, or nullptr if none exists.
SearchResultsPanel* GetSearchResultsPanel() const;
// Shows the results panel.
void ShowSearchResultsPanel();
// Navigates the Sunfish search results panel to the given URL, if the panel
// is available.
void NavigateSearchResultsPanel(const GURL& url);
// Closes the search results panel, or does nothing if it doesn't exist.
void CloseSearchResultsPanel();
// Called explicitly by `CaptureModeSession` on a mouse drag, to hide the
// panel.
void OnLocatedEventDragged();
// Updates the search results panel bounds if the widget exists.
void MaybeUpdateSearchResultsPanelBounds();
// Returns true if a capture mode session is currently active. If you only
// need to call this method, but don't need the rest of the controller, use
// capture_mode_util::IsCaptureModeActive(). Note that null sessions can still
// return `true`.
bool IsActive() const;
// Returns the effective audio recording mode, taking into account the
// `AudioCaptureAllowed` policy.
AudioRecordingMode GetEffectiveAudioRecordingMode() const;
// Returns true if audio recording is forced disabled by the
// `AudioCaptureAllowed` policy.
bool IsAudioCaptureDisabledByPolicy() const;
// Returns true if the folder to save captures is enforced by
// `ScreenCaptureLocation` policy and can't be changed.
bool IsCustomFolderManagedByPolicy() const;
// Returns true if Search is allowed.
bool IsSearchAllowedByPolicy() const;
// Returns true if there's an active video recording that is recording audio.
bool IsAudioRecordingInProgress() const;
// Returns true if the camera preview is visible, false otherwise.
bool IsShowingCameraPreview() const;
// Returns true if the panel should handle the event.
bool IsEventOnSearchResultsPanel(const ui::LocatedEvent& event,
const gfx::Point& screen_location);
// Returns true if the panel is visible.
bool IsSearchResultsPanelVisible() const;
// Returns true if the network is currently in an offline or unknown state.
bool IsNetworkConnectionOffline() const;
// Returns true if this supports the new behavior provided by
// `new_entry_type`.
bool SupportsBehaviorChange(CaptureModeEntryType new_entry_type) const;
// Sets the capture source/type, and recording type, which will be applied to
// an ongoing capture session (if any), or to a future capture session when
// Start() is called.
void SetSource(CaptureModeSource source);
void SetType(CaptureModeType type);
void SetRecordingType(RecordingType recording_type);
// Sets the audio recording mode, which will be applied to any future
// recordings (cannot be set mid recording), or to a future capture mode
// session when Start() is called. The effective enabled state takes into
// account the `AudioCaptureAllowed` policy.
void SetAudioRecordingMode(AudioRecordingMode mode);
// Sets the flag to enable the demo tools feature, which will be applied to
// any future recordings (cannot be set mid recording), or to a future capture
// mode session when Start() is called. Currently the demo tools feature is
// behind the feature flag.
void EnableDemoTools(bool enable);
// Starts a new capture session with the most-recently used `type_` and
// `source_`. Also records what `entry_type` that started capture mode. The
// `callback` will be invoked when the status of the capture mode session
// initialization process is determined.
void Start(CaptureModeEntryType entry_type,
OnSessionStartAttemptCallback callback = base::DoNothing());
// Starts a new capture session with a pre-selected window which will be
// observed throughout the session and can't be altered.
void StartForGameDashboard(aura::Window* game_window);
// Starts recording a pre-selected game window as soon as possible without
// starting a countdown by using a null session. Currently unused.
void StartRecordingInstantlyForGameDashboard(aura::Window* game_window);
// Starts a new sunfish session. Currently invoked when clicking the Sunfish
// button in the launcher, holding down the home button in the shelf, or a
// debug command.
void StartSunfishSession();
// Stops an existing capture session.
void Stop();
// Called by the `capture_mode_session_` to notify the `observers_` when the
// capture mode session ends without starting any recording
void NotifyRecordingStartAborted();
// Sets the user capture region. If it's non-empty and changed by the user,
// update |last_capture_region_update_time_|.
void SetUserCaptureRegion(const gfx::Rect& region, bool by_user);
// Returns true if we can show a user nudge animation and a toast message to
// alert users any available new features.
bool CanShowSunfishRegionNudge() const;
// Disables showing the user nudge from now on. Calling the above
// CanShowSunfishRegionNudge() will return false for the current active user
// going forward.
void DisableSunfishRegionNudgeForever();
// Sets whether the currently logged in user selected to use the default
// "Downloads" folder as the current save location, even while they already
// have a currently set custom folder. When this setting is true, any
// currently set custom folder is ignored but not removed.
// This can only be called when user is logged in.
void SetUsesDefaultCaptureFolder(bool value);
// Sets the given |path| as the custom save location of captured images and
// videos for the currently logged in user. Setting an empty |path| clears any
// custom selected folder resulting in using the default downloads folder.
// Calling this function will reset the value of "UsesDefaultCaptureFolder" to
// false, since it means the user wants to switch to a custom folder when it's
// set.
// This can only be called when user is logged in.
void SetCustomCaptureFolder(const base::FilePath& path);
// Returns the currently saved custom folder even if it's not being currently
// used.
base::FilePath GetCustomCaptureFolder() const;
// Returns the folder in which all taken screenshots and videos will be saved.
// It can be the temp directory if the user is not logged in, the default
// "Downloads" folder, or a user-selected custom location.
CaptureFolder GetCurrentCaptureFolder() const;
// Performs the instant full screen capture for each available display if no
// restricted content exists on that display, each capture is saved as an
// individual file. Note: this won't start a capture mode session.
void CaptureScreenshotsOfAllDisplays();
// Performs the instant screen capture for the `given_window` which bypasses
// the capture mode session.
void CaptureScreenshotOfGivenWindow(aura::Window* given_window);
// Called only while a capture session is in progress to perform the actual
// capture depending on the current selected `source_` and `type_`, and ends
// the capture session. `capture_type` indicates what type of capture to do.
void PerformCapture(
PerformCaptureType capture_type = PerformCaptureType::kCapture);
// Called by a capture session behavior to perform an image capture search.
// `capture_type` indicates the type of search to perform on the captured
// image.
void PerformImageSearch(PerformCaptureType capture_type);
void EndVideoRecording(EndRecordingReason reason);
// Posts a task to the blocking pool to check the availability of the given
// `folder` and replies back asynchronously by calling the given `callback`
// with `available` set either to true or false.
void CheckFolderAvailability(
const base::FilePath& folder,
base::OnceCallback<void(bool available)> callback);
// Sets the |protection_mask| that is currently set on the given |window|. If
// the |protection_mask| is |display::CONTENT_PROTECTION_METHOD_NONE|, then
// the window will no longer be tracked.
// Note that content protection (a.k.a. HDCP (High-bandwidth Digital Content
// Protection)) is different from DLP (Data Leak Prevention). The latter is
// enforced by admins and applies to both image and video capture, whereas
// the former is enforced by apps and content providers and is applied only to
// video capture.
void SetWindowProtectionMask(aura::Window* window, uint32_t protection_mask);
// If a video recording is in progress, it will end if so required by content
// protection.
void RefreshContentProtection();
// Returns true if the given `path` is the root folder of DriveFS, false
// otherwise.
bool IsRootDriveFsPath(const base::FilePath& path) const;
// Returns true if the given `path` is the same as the Android Play files
// path, false otherwise.
bool IsAndroidFilesPath(const base::FilePath& path) const;
// Returns true if the given `path` is the same as the Linux Files path, false
// otherwise.
bool IsLinuxFilesPath(const base::FilePath& path) const;
// Returns true if the given `path` is the root folder of OneDrive, false
// otherwise.
bool IsRootOneDriveFilesPath(const base::FilePath& path) const;
// Calls the delegate to instantiate `SearchResultsView`.
std::unique_ptr<AshWebView> CreateSearchResultsView() const;
// Returns the current parent window for the on-capture-surface widgets such
// as `CaptureModeCameraController::camera_preview_widget_` and
// `CaptureModeDemoToolsController::key_combo_widget_`.
aura::Window* GetOnCaptureSurfaceWidgetParentWindow() const;
// Returns the bounds, within which the on-capture-surface widgets (such as
// the camera preview widget and the key combo widget) will be confined. The
// bounds is in screen coordinate when capture source is `kFullscreen` or
// 'kRegion', but in window's coordinate when it is 'kWindow' type.
gfx::Rect GetCaptureSurfaceConfineBounds() const;
// Returns the windows that to be avoided for collision with other system
// windows such as the PIP window and the automatic click bubble menu.
std::vector<aura::Window*> GetWindowsForCollisionAvoidance() const;
// Updates the video conference manager and panel with the current state of
// screen recording and whether camera or audio are being recorded.
void MaybeUpdateVcPanel();
// Checks if there are any content currently on the screen that are restricted
// by DLP. `shutting_down` is true if the lock state controller has received a
// request to shut down, and false otherwise. `callback` will be triggered by
// the DLP manager with `proceed` set to true if screen capture is allowed to
// continue, or set to false if it should not continue.
void CheckScreenCaptureDlpRestrictions(
bool shutting_down,
OnCaptureModeDlpRestrictionChecked callback);
// Returns true if the video recording is in progress and annotating is
// supported.
bool ShouldAllowAnnotating() const;
// Returns true is annotating should be supported for the current capture mode
// behavior.
bool IsAnnotatingSupported() const;
// TODO(hewer): Remove the `image` param when cleaning up the native
// implementation.
// Called by `CaptureModeDelegate` when the search result is fetched. Opens
// `url` if the captured region on the screen has not changed since the
// request was made. `image` is used as the thumbnail image for the native
// search box; if the Lens Web implementation is enabled, then `image` can be
// empty as we use the Lens search box instead.
void OnSearchUrlFetched(const gfx::Rect& captured_region,
const gfx::ImageSkia& image,
GURL url);
// Called by `CaptureModeDelegate` if an error occurs while trying to perform
// and image search or text detection. Shows a generic error message in the
// action container if the session is active.
void OnLensWebError(base::WeakPtr<BaseCaptureModeSession> image_search_token);
// Called by `SearchResultsView` when a search result is opened.
void OnSearchResultClicked();
// Returns true if Google is the default search engine for the active user,
// and false otherwise.
bool ActiveUserDefaultSearchProviderIsGoogle() const;
// recording::mojom::RecordingServiceClient:
void OnRecordingEnded(recording::mojom::RecordingStatus status,
const gfx::ImageSkia& thumbnail) override;
// recording::mojom::DriveFsQuotaDelegate:
void GetDriveFsFreeSpaceBytes(
GetDriveFsFreeSpaceBytesCallback callback) override;
// SessionObserver:
void OnFirstSessionStarted() override;
void OnActiveUserSessionChanged(const AccountId& account_id) override;
void OnSessionStateChanged(session_manager::SessionState state) override;
void OnChromeTerminating() override;
// chromeos::PowerManagerClient::Observer:
void SuspendImminent(power_manager::SuspendImminent::Reason reason) override;
// crosapi::mojom::VideoConferenceManagerClient:
void GetMediaApps(GetMediaAppsCallback callback) override;
void ReturnToApp(const base::UnguessableToken& token,
ReturnToAppCallback callback) override;
void SetSystemMediaDeviceStatus(
crosapi::mojom::VideoConferenceMediaDevice device,
bool disabled,
SetSystemMediaDeviceStatusCallback callback) override;
void StopAllScreenShare() override;
// ShellObserver:
void OnPinnedStateChanged(aura::Window* pinned_window) override;
// Skips the 3-second count down, and IsCaptureAllowed() checks, and starts
// video recording right away for testing purposes.
void StartVideoRecordingImmediatelyForTesting();
void AddObserver(CaptureModeObserver* observer);
void RemoveObserver(CaptureModeObserver* observer);
CaptureModeDelegate* delegate_for_testing() const { return delegate_.get(); }
VideoRecordingWatcher* video_recording_watcher_for_testing() const {
return video_recording_watcher_.get();
}
private:
friend class CaptureModeTestApi;
friend class VideoRecordingWatcher;
// Performs the necessary setup common to all start methods (excluding
// testing-only methods).
void StartInternal(
SessionType session_type,
CaptureModeEntryType entry_type,
OnSessionStartAttemptCallback callback = base::DoNothing());
// Called by |video_recording_watcher_| when the display on which recording is
// happening changes its bounds such as on display rotation or device scale
// factor changes. In this case we push the new |root_size| in DIPs, and the
// |device_scale_factor| to the recording service so that it can update the
// video size. Note that we do this only when recording a window or a partial
// region. When recording a fullscreen, the capturer can handle these changes
// and would center and letter-box the video frames within the requested size.
void PushNewRootSizeToRecordingService(const gfx::Size& root_size,
float device_scale_factor);
// Called by |video_recording_watcher_| to inform us that the |window| being
// recorded (i.e. |is_recording_in_progress()| is true) is about to move to a
// |new_root|. This is needed so we can inform the recording service of this
// change so that it can switch its capture target to the new root's frame
// sink.
void OnRecordedWindowChangingRoot(aura::Window* window,
aura::Window* new_root);
// Called by |video_recording_watcher_| to inform us that the size of the
// |window| being recorded was changed to |new_size| in DIPs. This is pushed
// to the recording service in order to update the video dimensions.
void OnRecordedWindowSizeChanged(const gfx::Size& new_size);
// Returns true if screen recording needs to be blocked due to protected
// content. |window| is the window being recorded or desired to be recorded.
bool ShouldBlockRecordingForContentProtection(aura::Window* window) const;
// Used by user session change, and suspend events to end the capture mode
// session if it's active, or stop the video recording if one is in progress.
void EndSessionOrRecording(EndRecordingReason reason);
// The capture parameters for the capture operation that is about to be
// performed (i.e. the window to be captured, and the capture bounds). If
// nothing is to be captured (e.g. when there's no window selected in a
// kWindow source, or no region is selected in a kRegion source), then a
// std::nullopt is returned.
struct CaptureParams {
raw_ptr<aura::Window> window = nullptr;
// The capture bounds, either in root coordinates (in kFullscreen or kRegion
// capture sources), or window-local coordinates (in a kWindow capture
// source).
gfx::Rect bounds;
};
std::optional<CaptureParams> GetCaptureParams() const;
// Launches the mojo service that handles audio and video recording, and
// begins recording according to the given `capture_params`. It creates an
// overlay on the video capturer so that it can be used to record the mouse
// cursor. It gives the pending receiver end to that overlay on Viz, and the
// other end should be owned by the `video_recording_watcher_`. If the given
// `effective_audio_mode` (which takes into account the audio capture admin
// policy and the recording format type) is not `kOff`, it will setup the
// needed mojo connections to the Audio Service so that audio recording can be
// done by the recording service.
void LaunchRecordingServiceAndStartRecording(
const CaptureParams& capture_params,
mojo::PendingReceiver<viz::mojom::FrameSinkVideoCaptureOverlay>
cursor_overlay,
AudioRecordingMode effective_audio_mode);
// Called back when the mojo pipe to the recording service gets disconnected.
void OnRecordingServiceDisconnected();
// Terminates the recording service process, closes any recording-related UI
// elements (only if |success| is false as this indicates that recording was
// not ended normally by calling EndVideoRecording()), and shows the video
// file notification with the given |thumbnail|.
void FinalizeRecording(bool success, const gfx::ImageSkia& thumbnail);
// Called to terminate the stop-recording shelf pod button, and the
// |video_recording_watcher_| when recording ends.
void TerminateRecordingUiElements();
// The below functions start the actual image/video capture. They expect that
// the capture session is still active when called, so they can retrieve the
// capture parameters they need. They will end the sessions themselves.
// They should never be called if IsCaptureAllowed() returns false.
void CaptureImage(const CaptureParams& capture_params,
const base::FilePath& path,
const CaptureModeBehavior* behavior);
void CaptureVideo(const CaptureParams& capture_params);
// Called back when an image has been captured to trigger an attempt to save
// the image as a file. `was_cursor_originally_blocked` is whether the cursor
// was blocked at the time the screenshot capture request was made.
// `png_bytes` is the buffer containing the captured image in a PNG format.
void OnImageCaptured(const base::FilePath& path,
bool was_cursor_originally_blocked,
const CaptureModeBehavior* behavior,
scoped_refptr<base::RefCountedMemory> png_bytes);
// Called back when an image has been captured to trigger a search request.
// `jpeg_bytes` is the buffer containing the captured image in a JPEG format.
// `capture_type` indicates the type of search to perform on the captured
// image.
void OnImageCapturedForSearch(
PerformCaptureType capture_type,
bool was_cursor_originally_blocked,
base::WeakPtr<BaseCaptureModeSession> image_search_token,
scoped_refptr<base::RefCountedMemory> jpeg_bytes);
// Called back when on-device text detection is complete to show copy text and
// smart actions buttons if needed. `image_search_token` is a weak pointer
// which is invalidated every time the selected region or session changes. If
// the selected region or session has changed since the request was made, then
// the detected text result is discarded and no buttons are shown.
// `ocr_attempt_start_time` is used to record the metric for the the latency
// of the on device text detection. Currently only used for regular capture
// mode sessions when a region is selected.
void OnTextDetectionComplete(
base::WeakPtr<BaseCaptureModeSession> image_search_token,
base::TimeTicks ocr_attempt_start_time,
std::optional<std::string> detected_text);
// Called back when Lens-based text detection is complete to show the copy
// text button if needed. `image_search_token` is a weak pointer which is
// invalidated every time the selected region or session changes. If the
// selected region or session has changed since the request was made, then the
// detected text result is discarded and no button is shown. Currently only
// used in Sunfish capture mode sessions when a region is selected.
void OnLensTextDetectionComplete(
base::WeakPtr<BaseCaptureModeSession> image_search_token,
std::optional<std::string> detected_text);
// Helper function that adds a Copy Text action button. Called when both
// Lens-based and on-device text detection are completed with non-empty
// `detected_text`.
void AddCopyTextButton(std::string_view detected_text);
// Called back when the copy text button is clicked. This will copy `text` to
// clipboard, show a notification, and close the capture session.
void OnCopyTextButtonClicked(const std::u16string& text);
// Shows scanner discliamer if necessary, which has an option to accept or
// decline consent for scanner.
// If only scanner is enabled, then stops the session if declined since there
// is nothing you can do in the session.
void MaybeShowScannerDisclaimerOnSunfishStartup(bool startup_success);
// Called back when the Scanner feature has processed a captured image to
// suggest available Scanner actions.
void OnScannerActionsFetched(
base::WeakPtr<BaseCaptureModeSession> image_search_token,
ScannerSession::FetchActionsResponse actions_response);
// Called back when an attempt to save the image file has been completed, with
// `file_saved_path` indicating whether the attempt succeeded or failed. If
// `file_saved_path` is empty, the attempt failed. `png_bytes` is the buffer
// containing the captured image in a PNG format, which will be used to show a
// preview of the image in a notification, and save it as a bitmap in the
// clipboard. If saving was successful, then the image was saved in
// `file_saved_path`.
void OnImageFileSaved(scoped_refptr<base::RefCountedMemory> png_bytes,
const CaptureModeBehavior* behavior,
const base::FilePath& file_saved_path);
// Called back after the image file was saved to the final location.
// Parameters are same as for `OnImageFileSaved()` with `success` indicating
// the result of finalizing the file.
void OnImageFileFinalized(const gfx::Image& image,
const CaptureModeBehavior* behavior,
bool success,
const base::FilePath& file_saved_path);
// Called back when the check for custom folder's availability is done in
// `CheckFolderAvailability`, with `available` indicating whether the custom
// folder is available or not.
void OnCustomFolderAvailabilityChecked(bool available);
// Called back when the `video_file_handler_` flushes the remaining cached
// video chunks in its buffer. Called on the UI thread. `video_thumbnail` is
// an RGB image provided by the recording service that can be used as a
// thumbnail of the video in the notification. `behavior` will decide whether
// the recording will not be shown in tote or notification.
// `saved_video_file_path` indicates whether the attempt succeeded or failed.
// If `saved_video_file_path` is empty, the attempt failed.
void OnVideoFileSaved(const gfx::ImageSkia& video_thumbnail,
const CaptureModeBehavior* behavior,
bool success,
const base::FilePath& saved_video_file_path);
// Shows a preview notification of the newly taken screenshot or screen
// recording. Customized notification view will be decided based on the
// `behavior`.
void ShowPreviewNotification(const base::FilePath& screen_capture_path,
const gfx::Image& preview_image,
const CaptureModeType type,
const CaptureModeBehavior* behavior);
void HandleNotificationClicked(const base::FilePath& screen_capture_path,
const CaptureModeType type,
const BehaviorType behavior_type,
std::optional<int> button_index);
// Builds a path for a file of an image screenshot, or a video screen
// recording, builds with display index if there are
// multiple displays.
base::FilePath BuildImagePath() const;
base::FilePath BuildVideoPath() const;
base::FilePath BuildImagePathForDisplay(int display_index) const;
// Used by the above three functions by providing the corresponding file name
// |base_name| to a capture type (image or video). The returned file path
// excludes the file extension. The above functions are responsible for adding
// it.
base::FilePath BuildPathNoExtension(std::string_view base_name,
base::Time timestamp) const;
// Returns a fallback path concatenating the default `Downloads` folder and
// the base name of `path`.
base::FilePath GetFallbackFilePathFromFile(const base::FilePath& path);
// Records the number of screenshots taken.
void RecordAndResetScreenshotsTakenInLastDay();
void RecordAndResetScreenshotsTakenInLastWeek();
// Records the number of consecutive screenshots taken within 5s of each
// other.
void RecordAndResetConsecutiveScreenshots();
// Called when the video record 3-seconds count down finishes.
void OnVideoRecordCountDownFinished();
// Called when the client of capture mode requires creating its own folder to
// host the video file, and that folder has been created, and the desired full
// path of the video file (including the extension) is provided. Note that
// this path will be empty if the folder creation operation failed.
void OnCaptureFolderCreated(const CaptureParams& capture_params,
const base::FilePath& capture_file_full_path);
// Ends the capture session and starts the video recording for the given
// `capture_params`. The video will be saved to a file to the given
// `video_file_path`. This can only be called while the session is still
// active.
void BeginVideoRecording(const CaptureParams& capture_params,
const base::FilePath& video_file_path);
// Called to interrupt the ongoing video recording because it's not anymore
// allowed to be captured.
void InterruptVideoRecording();
// Called by the DLP manager when it's checked for any on-screen content
// restriction at the time when the capture operation is attempted. `proceed`
// will be set to true if the capture operation should continue, false if it
// should be aborted. `capture_type` indicates what type of capture to do.
void OnDlpRestrictionCheckedAtPerformingCapture(
PerformCaptureType capture_type,
bool proceed);
// Called after the video recording was written to the final file location.
// `should_delete_file` indicates if the file was deleted due to an error.
void OnVideoFileFinalized(bool should_delete_file,
const gfx::ImageSkia& video_thumbnail);
// Called by the DLP manager when it's checked again for any on-screen content
// restriction at the time when the video capture 3-second countdown ends.
// `proceed` will be set to true if video recording should begin, or false if
// it should be aborted.
void OnDlpRestrictionCheckedAtCountDownFinished(bool proceed);
// Bound to a callback that will be called by the DLP manager to let us know
// whether a pending session initialization should `proceed` or abort due to
// some restricted contents on the screen. `at_exit_closure` is passed from
// `Start()` and will be called on the exit of the function.
void OnDlpRestrictionCheckedAtSessionInit(SessionType session_type,
CaptureModeEntryType entry_type,
base::OnceClosure at_exit_closure,
bool proceed);
// At the end of a video recording, the DLP manager is checked to see if there
// were any restricted content of a warning level type during the recording
// (warning-level restrictions do not result in interrupting the video
// recording), if so, the DLP manager shows a dialog asking the user whether
// to continue with saving the video file. When the dialog closes, this
// function is called to provide the user choice; save the video file when
// `proceed` is true, or to delete it when `proceed` is false.
void OnDlpRestrictionCheckedAtVideoEnd(const gfx::ImageSkia& video_thumbnail,
bool success,
const CaptureModeBehavior* behavior,
bool proceed);
// Encapsulates the policy check and calls into DLP manager to do DLP check.
// `instant_screenshot_callback` will be moved and invoked in
// `OnDlpRestrictionCheckedAtCaptureScreenshot()` to perform the instant
// screenshot. This is invoked via the screenshot accelerator commands and
// will end capture mode session if it is active (only if the session was
// started by the same behavior).
void CaptureInstantScreenshot(CaptureModeEntryType entry_type,
CaptureModeSource source,
base::OnceClosure instant_screenshot_callback,
BehaviorType behavior_type);
// Bound to a callback that will be called by DLP manager to let the user know
// whether screen capture should `proceed` or abort due to some restricted
// contents on the screen. Invokes the `instant_screenshot_callback` and
// records the metrics for the given `behavior_type` if `proceed` is true and
// screen capture is allowed by the enterprise policy.
void OnDlpRestrictionCheckedAtCaptureScreenshot(
CaptureModeEntryType entry_type,
CaptureModeSource source,
base::OnceClosure instant_screenshot_callback,
BehaviorType behavior_type,
bool proceed);
// Takes screenshots of all the available displays and saves them to disk.
void PerformScreenshotsOfAllDisplays(BehaviorType behavior_type);
// Takes a screenshot of the `given_window` and save it to disk.
void PerformScreenshotOfGivenWindow(aura::Window* given_window,
BehaviorType behavior_type);
bool ShouldClearCaptureRegion(BehaviorType behavior_type) const;
// Gets the corresponding `SaveLocation` enum value on the given `path`.
CaptureModeSaveToLocation GetSaveToOption(const base::FilePath& path);
// Retrieves the instance of the `CaptureModeBehavior` by the given
// `behavior_type` from the `behaviors_map_` if exits, or creates an instance
// otherwise.
CaptureModeBehavior* GetBehavior(BehaviorType behavior_type);
// Deletes the `path` on `blocking_task_runner_` if local or calls delegate to
// remove it if remote.
void DeleteFileAsync(const base::FilePath& path);
// Refreshes the search results panel stacking order if it exists.
// `current_root` is the current capture mode session root if it is active,
// else nullptr.
void RefreshSearchResultsPanel(aura::Window* current_root);
// The ID of this object as a client of the video conference manager.
const base::UnguessableToken vc_client_id_ = base::UnguessableToken::Create();
// The ID of an ongoing recording as a media app tracked by the video
// conference manager.
const base::UnguessableToken capture_mode_media_app_id_ =
base::UnguessableToken::Create();
std::unique_ptr<CaptureModeDelegate> delegate_;
// Controls the selfie camera feature of capture mode.
std::unique_ptr<CaptureModeCameraController> camera_controller_;
CaptureModeType type_ = CaptureModeType::kImage;
CaptureModeSource source_ = CaptureModeSource::kRegion;
RecordingType recording_type_ = RecordingType::kWebM;
// Contains `SearchResultsPanel` as its contents view.
views::UniqueWidgetPtr search_results_panel_widget_;
// A blocking task runner for file IO operations.
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
mojo::Remote<recording::mojom::RecordingService> recording_service_remote_;
mojo::Receiver<recording::mojom::RecordingServiceClient>
recording_service_client_receiver_{this};
mojo::Receiver<recording::mojom::DriveFsQuotaDelegate>
drive_fs_quota_delegate_receiver_{this};
// This is the file path of the video file currently being recorded. It is
// empty when no video recording is in progress or when no video is being
// saved.
base::FilePath current_video_file_path_;
// We remember the user selected capture region when the source is |kRegion|
// between sessions. Initially, this value is empty at which point we display
// a message to the user instructing them to start selecting a region.
gfx::Rect user_capture_region_;
std::unique_ptr<BaseCaptureModeSession> capture_mode_session_;
// Remember the user selected preference for audio recording between sessions.
// Initially, this value is set to `kOff`, ensuring that this is an opt-in
// feature. Note that this value will always be overwritten by the
// `AudioCaptureAllowed` policy, when `GetEffectiveAudioRecordingMode()` is
// called.
AudioRecordingMode audio_recording_mode_ = AudioRecordingMode::kOff;
// If true, the 3-second countdown UI will be skipped, and video recording
// will start immediately.
bool skip_count_down_ui_ = false;
// True only if the recording service detects a |kLowDiskSpace| condition
// while writing the video file to the file system. This value is used only to
// determine the message shown to the user in the video preview notification
// to explain why the recording was ended, and is then reset back to false.
bool low_disk_space_threshold_reached_ = false;
// Set to true when we're waiting for a callback from the DLP manager to check
// content restrictions that may block capture mode at any of its stages
// (initialization or performing the capture).
bool pending_dlp_check_ = false;
// These track the state of the camera and microphone (e.g. the camera can be
// disabled using a privacy switch, and the microphone can be muted from the
// settings).
bool is_camera_muted_ = false;
bool is_microphone_muted_ = false;
// Tracks the windows that currently have content protection enabled, so that
// we prevent them from being video recorded. Each window is mapped to its
// currently-set protection_mask. Windows in this map are only the ones that
// have protection masks other than |display::CONTENT_PROTECTION_METHOD_NONE|.
base::flat_map<aura::Window*, /*protection_mask*/ uint32_t>
protected_windows_;
// If set, it will be called when either an image or video file is saved and
// the file size metric has been recorded.
base::OnceCallback<void(const base::FilePath&)>
on_file_saved_callback_for_test_;
OnFileDeletedCallback on_file_deleted_callback_for_test_;
base::OnceClosure on_countdown_finished_callback_for_test_;
base::OnceClosure on_video_recording_started_callback_for_test_;
base::OnceCallback<void(PerformCaptureType capture_type)>
on_image_captured_for_search_callback_for_test_;
// Timers used to schedule recording of the number of screenshots taken.
base::RepeatingTimer num_screenshots_taken_in_last_day_scheduler_;
base::RepeatingTimer num_screenshots_taken_in_last_week_scheduler_;
// Counters used to track the number of screenshots taken. These values are
// not persisted across crashes, restarts or sessions so they only provide a
// rough approximation.
int num_screenshots_taken_in_last_day_ = 0;
int num_screenshots_taken_in_last_week_ = 0;
// Counter used to track the number of consecutive screenshots taken.
int num_consecutive_screenshots_ = 0;
base::DelayTimer num_consecutive_screenshots_scheduler_;
// The time when OnVideoRecordCountDownFinished is called and video has
// started recording. It is used when video has finished recording for metrics
// collection.
base::TimeTicks recording_start_time_;
// The last time the user sets a non-empty capture region. It will be used to
// clear the user capture region from previous capture sessions if 8+ minutes
// has passed since the last time the user changes the capture region when the
// new capture session starts .
base::TimeTicks last_capture_region_update_time_;
// True in the scope of BeginVideoRecording().
bool is_initializing_recording_ = false;
// Remember the user preference of whether to enable demo tools feature or
// not in video recording mode, between sessions. Initially, this value is set
// to false, ensuring that this is an opt-in feature.
bool enable_demo_tools_ = false;
// Maps an instance of the `CaptureModeBehavior` by `BehaviorType`. This is a
// map because a user session may start multiple different behaviors, during
// which we don't need to instantiate the behavior again. We also want to keep
// the behavior alive for the rest of the session until the user signs out or
// shuts down. This is because the behaviors are the ones that cache the
// settings.
base::flat_map<BehaviorType, std::unique_ptr<CaptureModeBehavior>>
behaviors_map_;
// Watches events that lead to ending video recording.
// Must be destroyed before `behaviors_map_` to avoid dangling raw pointers
// to `CaptureModeBehavior`s.
std::unique_ptr<VideoRecordingWatcher> video_recording_watcher_;
base::ObserverList<CaptureModeObserver> observers_;
std::unique_ptr<CaptureModeEducationController> education_controller_;
base::ScopedObservation<Shell, ShellObserver> shell_observation_{this};
std::list<std::unique_ptr<const network::SimpleURLLoader>>
uploads_in_progress_;
// URLLoaderFactory used for network requests. May be null initially if the
// creation is delayed.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
base::WeakPtrFactory<CaptureModeController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_CONTROLLER_H_
|