capture_mode_controller.cc 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719
  1. // Copyright 2020 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. #include "ash/capture_mode/capture_mode_controller.h"
  5. #include <utility>
  6. #include "ash/capture_mode/capture_mode_ash_notification_view.h"
  7. #include "ash/capture_mode/capture_mode_camera_controller.h"
  8. #include "ash/capture_mode/capture_mode_metrics.h"
  9. #include "ash/capture_mode/capture_mode_notification_view.h"
  10. #include "ash/capture_mode/capture_mode_session.h"
  11. #include "ash/capture_mode/capture_mode_util.h"
  12. #include "ash/constants/ash_features.h"
  13. #include "ash/constants/notifier_catalogs.h"
  14. #include "ash/projector/projector_controller_impl.h"
  15. #include "ash/public/cpp/capture_mode/recording_overlay_view.h"
  16. #include "ash/public/cpp/holding_space/holding_space_client.h"
  17. #include "ash/public/cpp/holding_space/holding_space_controller.h"
  18. #include "ash/public/cpp/notification_utils.h"
  19. #include "ash/resources/vector_icons/vector_icons.h"
  20. #include "ash/root_window_controller.h"
  21. #include "ash/session/session_controller_impl.h"
  22. #include "ash/shell.h"
  23. #include "ash/strings/grit/ash_strings.h"
  24. #include "ash/system/message_center/message_view_factory.h"
  25. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  26. #include "base/auto_reset.h"
  27. #include "base/bind.h"
  28. #include "base/callback_helpers.h"
  29. #include "base/check.h"
  30. #include "base/check_op.h"
  31. #include "base/files/file_path.h"
  32. #include "base/files/file_util.h"
  33. #include "base/location.h"
  34. #include "base/memory/ref_counted_memory.h"
  35. #include "base/memory/scoped_refptr.h"
  36. #include "base/notreached.h"
  37. #include "base/strings/stringprintf.h"
  38. #include "base/task/current_thread.h"
  39. #include "base/task/task_traits.h"
  40. #include "base/task/thread_pool.h"
  41. #include "base/time/time.h"
  42. #include "components/prefs/pref_registry_simple.h"
  43. #include "components/prefs/pref_service.h"
  44. #include "components/user_manager/user_type.h"
  45. #include "components/vector_icons/vector_icons.h"
  46. #include "components/viz/host/host_frame_sink_manager.h"
  47. #include "mojo/public/cpp/bindings/pending_remote.h"
  48. #include "ui/aura/env.h"
  49. #include "ui/base/clipboard/clipboard_buffer.h"
  50. #include "ui/base/clipboard/scoped_clipboard_writer.h"
  51. #include "ui/base/l10n/l10n_util.h"
  52. #include "ui/compositor/compositor.h"
  53. #include "ui/compositor/layer.h"
  54. #include "ui/display/types/display_constants.h"
  55. #include "ui/message_center/message_center.h"
  56. #include "ui/message_center/public/cpp/notification.h"
  57. #include "ui/message_center/public/cpp/notification_delegate.h"
  58. #include "ui/snapshot/snapshot.h"
  59. namespace ash {
  60. namespace {
  61. CaptureModeController* g_instance = nullptr;
  62. // The amount of time that can elapse from the prior screenshot to be considered
  63. // consecutive.
  64. constexpr base::TimeDelta kConsecutiveScreenshotThreshold = base::Seconds(5);
  65. constexpr char kScreenCaptureNotificationId[] = "capture_mode_notification";
  66. constexpr char kScreenCaptureStoppedNotificationId[] =
  67. "capture_mode_stopped_notification";
  68. constexpr char kScreenCaptureNotifierId[] = "ash.capture_mode_controller";
  69. constexpr char kScreenShotNotificationType[] = "screen_shot_notification_type";
  70. constexpr char kScreenRecordingNotificationType[] =
  71. "screen_recording_notification_type";
  72. // The format strings of the file names of captured images.
  73. // TODO(afakhry): Discuss with UX localizing "Screenshot" and "Screen
  74. // recording".
  75. constexpr char kScreenshotFileNameFmtStr[] = "Screenshot %s %s";
  76. constexpr char kVideoFileNameFmtStr[] = "Screen recording %s %s";
  77. constexpr char kDateFmtStr[] = "%d-%02d-%02d";
  78. constexpr char k24HourTimeFmtStr[] = "%02d.%02d.%02d";
  79. constexpr char kAmPmTimeFmtStr[] = "%d.%02d.%02d";
  80. // Duration to clear the capture region selection from the previous session.
  81. constexpr base::TimeDelta kResetCaptureRegionDuration = base::Minutes(8);
  82. // The name of a file path pref for the user-selected custom path to which
  83. // captured images and videos should be saved.
  84. constexpr char kCustomCapturePathPrefName[] =
  85. "ash.capture_mode.custom_save_path";
  86. // The name of a boolean pref that indicates whether the default downloads path
  87. // is currently selected even if a custom capture path is set.
  88. constexpr char kUsesDefaultCapturePathPrefName[] =
  89. "ash.capture_mode.uses_default_capture_path";
  90. // The name of a boolean pref that determines whether we can show the selfie
  91. // camera user nudge. When this pref is false, it means that we showed the
  92. // nudge at some point and the user interacted with the capture mode session UI
  93. // in such a way that the nudge no longer needs to be displayed again.
  94. constexpr char kCanShowCameraNudge[] = "ash.capture_mode.can_show_camera_nudge";
  95. // The screenshot notification button index.
  96. enum ScreenshotNotificationButtonIndex {
  97. BUTTON_EDIT = 0,
  98. BUTTON_DELETE,
  99. };
  100. // The video notification button index.
  101. enum VideoNotificationButtonIndex {
  102. BUTTON_DELETE_VIDEO = 0,
  103. };
  104. // Returns the date extracted from |timestamp| as a string to be part of
  105. // captured file names. Note that naturally formatted dates includes slashes
  106. // (e.g. 2020/09/02), which will cause problems when used in file names since
  107. // slash is a path separator.
  108. std::string GetDateStr(const base::Time::Exploded& timestamp) {
  109. return base::StringPrintf(kDateFmtStr, timestamp.year, timestamp.month,
  110. timestamp.day_of_month);
  111. }
  112. // Returns the time extracted from |timestamp| as a string to be part of
  113. // captured file names. Also note that naturally formatted times include colons
  114. // (e.g. 11:20 AM), which is restricted in file names in most file systems.
  115. // https://en.wikipedia.org/wiki/Filename#Comparison_of_filename_limitations.
  116. std::string GetTimeStr(const base::Time::Exploded& timestamp,
  117. bool use_24_hour) {
  118. if (use_24_hour) {
  119. return base::StringPrintf(k24HourTimeFmtStr, timestamp.hour,
  120. timestamp.minute, timestamp.second);
  121. }
  122. int hour = timestamp.hour % 12;
  123. if (hour <= 0)
  124. hour += 12;
  125. std::string time = base::StringPrintf(kAmPmTimeFmtStr, hour, timestamp.minute,
  126. timestamp.second);
  127. return time.append(timestamp.hour >= 12 ? " PM" : " AM");
  128. }
  129. // Selects a file path for captured files (image/video) from `current_path` and
  130. // `fallback_path`. If `current_path` is valid, use `current_path`, otherwise
  131. // use `fallback_path`.
  132. base::FilePath SelectFilePathForCapturedFile(
  133. const base::FilePath& current_path,
  134. const base::FilePath& fallback_path) {
  135. if (base::PathExists(current_path.DirName()))
  136. return current_path;
  137. DCHECK(base::PathExists(fallback_path.DirName()));
  138. return fallback_path;
  139. }
  140. // Writes the given `data` in a file with `path`. Returns true if saving
  141. // succeeded, or false otherwise.
  142. base::FilePath DoSaveFile(scoped_refptr<base::RefCountedMemory> data,
  143. const base::FilePath& path) {
  144. DCHECK(data);
  145. const int size = static_cast<int>(data->size());
  146. DCHECK(size);
  147. if (size != base::WriteFile(
  148. path, reinterpret_cast<const char*>(data->front()), size)) {
  149. LOG(ERROR) << "Failed to save file: " << path;
  150. return base::FilePath();
  151. }
  152. return path;
  153. }
  154. // Attempts to write the given `data` with the file path returned from
  155. // `SelectAFilePathForCapturedFile`.
  156. base::FilePath SaveFile(scoped_refptr<base::RefCountedMemory> data,
  157. const base::FilePath& current_path,
  158. const base::FilePath& fallback_path) {
  159. DCHECK(!base::CurrentUIThread::IsSet());
  160. DCHECK(!current_path.empty());
  161. DCHECK(!fallback_path.empty());
  162. return DoSaveFile(data,
  163. SelectFilePathForCapturedFile(current_path, fallback_path));
  164. }
  165. void DeleteFileAsync(scoped_refptr<base::SequencedTaskRunner> task_runner,
  166. const base::FilePath& path,
  167. OnFileDeletedCallback callback) {
  168. task_runner->PostTaskAndReplyWithResult(
  169. FROM_HERE, base::BindOnce(&base::DeleteFile, path),
  170. callback ? base::BindOnce(std::move(callback), path)
  171. : base::BindOnce(
  172. [](const base::FilePath& path, bool success) {
  173. // TODO(afakhry): Show toast?
  174. if (!success)
  175. LOG(ERROR) << "Failed to delete the file: " << path;
  176. },
  177. path));
  178. }
  179. // Shows a Capture Mode related notification with the given parameters.
  180. // |for_video_thumbnail| will be considered only if |optional_fields| contain
  181. // an image to show in the notification as a thumbnail for what was captured.
  182. void ShowNotification(
  183. const std::string& notification_id,
  184. int title_id,
  185. int message_id,
  186. const message_center::RichNotificationData& optional_fields,
  187. scoped_refptr<message_center::NotificationDelegate> delegate,
  188. message_center::SystemNotificationWarningLevel warning_level =
  189. message_center::SystemNotificationWarningLevel::NORMAL,
  190. const gfx::VectorIcon& notification_icon = kCaptureModeIcon,
  191. bool for_video_thumbnail = false) {
  192. const auto type = optional_fields.image.IsEmpty()
  193. ? message_center::NOTIFICATION_TYPE_SIMPLE
  194. : message_center::NOTIFICATION_TYPE_CUSTOM;
  195. std::unique_ptr<message_center::Notification> notification =
  196. CreateSystemNotification(
  197. type, notification_id, l10n_util::GetStringUTF16(title_id),
  198. l10n_util::GetStringUTF16(message_id),
  199. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_DISPLAY_SOURCE),
  200. GURL(),
  201. message_center::NotifierId(
  202. message_center::NotifierType::SYSTEM_COMPONENT,
  203. kScreenCaptureNotifierId,
  204. NotificationCatalogName::kScreenCapture),
  205. optional_fields, delegate, notification_icon, warning_level);
  206. if (type == message_center::NOTIFICATION_TYPE_CUSTOM) {
  207. notification->set_custom_view_type(for_video_thumbnail
  208. ? kScreenRecordingNotificationType
  209. : kScreenShotNotificationType);
  210. }
  211. // Remove the previous notification before showing the new one if there is
  212. // any.
  213. auto* message_center = message_center::MessageCenter::Get();
  214. message_center->RemoveNotification(notification_id,
  215. /*by_user=*/false);
  216. message_center->AddNotification(std::move(notification));
  217. }
  218. // Shows a notification informing the user that a Capture Mode operation has
  219. // failed.
  220. void ShowFailureNotification() {
  221. ShowNotification(kScreenCaptureStoppedNotificationId,
  222. IDS_ASH_SCREEN_CAPTURE_FAILURE_TITLE,
  223. IDS_ASH_SCREEN_CAPTURE_FAILURE_MESSAGE,
  224. /*optional_fields=*/{}, /*delegate=*/nullptr);
  225. }
  226. // Returns the ID of the message or the title for the notification based on
  227. // |allowance| and |for_title|.
  228. int GetDisabledNotificationMessageId(CaptureAllowance allowance,
  229. bool for_title) {
  230. switch (allowance) {
  231. case CaptureAllowance::kDisallowedByPolicy:
  232. return for_title ? IDS_ASH_SCREEN_CAPTURE_POLICY_DISABLED_TITLE
  233. : IDS_ASH_SCREEN_CAPTURE_POLICY_DISABLED_MESSAGE;
  234. case CaptureAllowance::kDisallowedByHdcp:
  235. return for_title ? IDS_ASH_SCREEN_CAPTURE_HDCP_STOPPED_TITLE
  236. : IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE;
  237. case CaptureAllowance::kAllowed:
  238. NOTREACHED();
  239. return IDS_ASH_SCREEN_CAPTURE_POLICY_DISABLED_MESSAGE;
  240. }
  241. }
  242. // Shows a notification informing the user that Capture Mode operations are
  243. // currently disabled. |allowance| identifies the reason why the operation is
  244. // currently disabled.
  245. void ShowDisabledNotification(CaptureAllowance allowance) {
  246. DCHECK(allowance != CaptureAllowance::kAllowed);
  247. ShowNotification(
  248. kScreenCaptureNotificationId,
  249. GetDisabledNotificationMessageId(allowance, /*for_title=*/true),
  250. GetDisabledNotificationMessageId(allowance, /*for_title=*/false),
  251. /*optional_fields=*/{}, /*delegate=*/nullptr,
  252. message_center::SystemNotificationWarningLevel::CRITICAL_WARNING,
  253. allowance == CaptureAllowance::kDisallowedByHdcp
  254. ? kCaptureModeIcon
  255. : vector_icons::kBusinessIcon);
  256. }
  257. // Shows a notification informing the user that video recording was stopped due
  258. // to a content-enforced protection.
  259. void ShowVideoRecordingStoppedByHdcpNotification() {
  260. ShowNotification(
  261. kScreenCaptureStoppedNotificationId,
  262. IDS_ASH_SCREEN_CAPTURE_HDCP_STOPPED_TITLE,
  263. IDS_ASH_SCREEN_CAPTURE_HDCP_BLOCKED_MESSAGE,
  264. /*optional_fields=*/{}, /*delegate=*/nullptr,
  265. message_center::SystemNotificationWarningLevel::CRITICAL_WARNING,
  266. kCaptureModeIcon);
  267. }
  268. // Copies the bitmap representation of the given |image| to the clipboard.
  269. void CopyImageToClipboard(const gfx::Image& image) {
  270. ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste)
  271. .WriteImage(image.AsBitmap());
  272. }
  273. // Emits UMA samples for the |status| of the recording as reported by the
  274. // recording service.
  275. void EmitServiceRecordingStatus(recording::mojom::RecordingStatus status) {
  276. using recording::mojom::RecordingStatus;
  277. switch (status) {
  278. case RecordingStatus::kSuccess:
  279. // We emit no samples for success status, as in this case the recording
  280. // was ended normally by the client, and the end reason for that is
  281. // emitted else where.
  282. break;
  283. case RecordingStatus::kServiceClosing:
  284. RecordEndRecordingReason(EndRecordingReason::kServiceClosing);
  285. break;
  286. case RecordingStatus::kVizVideoCapturerDisconnected:
  287. RecordEndRecordingReason(
  288. EndRecordingReason::kVizVideoCaptureDisconnected);
  289. break;
  290. case RecordingStatus::kAudioEncoderInitializationFailure:
  291. RecordEndRecordingReason(
  292. EndRecordingReason::kAudioEncoderInitializationFailure);
  293. break;
  294. case RecordingStatus::kVideoEncoderInitializationFailure:
  295. RecordEndRecordingReason(
  296. EndRecordingReason::kVideoEncoderInitializationFailure);
  297. break;
  298. case RecordingStatus::kAudioEncodingError:
  299. RecordEndRecordingReason(EndRecordingReason::kAudioEncodingError);
  300. break;
  301. case RecordingStatus::kVideoEncodingError:
  302. RecordEndRecordingReason(EndRecordingReason::kVideoEncodingError);
  303. break;
  304. case RecordingStatus::kIoError:
  305. RecordEndRecordingReason(EndRecordingReason::kFileIoError);
  306. break;
  307. case RecordingStatus::kLowDiskSpace:
  308. RecordEndRecordingReason(EndRecordingReason::kLowDiskSpace);
  309. break;
  310. case RecordingStatus::kLowDriveFsQuota:
  311. RecordEndRecordingReason(EndRecordingReason::kLowDriveFsQuota);
  312. break;
  313. }
  314. }
  315. PrefService* GetActiveUserPrefService() {
  316. DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
  317. auto* pref_service =
  318. Shell::Get()->session_controller()->GetActivePrefService();
  319. DCHECK(pref_service);
  320. return pref_service;
  321. }
  322. base::FilePath GetTempDir() {
  323. base::FilePath temp_dir;
  324. if (!base::GetTempDir(&temp_dir))
  325. LOG(ERROR) << "Failed to find the temporary directory.";
  326. return temp_dir;
  327. }
  328. } // namespace
  329. CaptureModeController::CaptureModeController(
  330. std::unique_ptr<CaptureModeDelegate> delegate)
  331. : delegate_(std::move(delegate)),
  332. camera_controller_(
  333. std::make_unique<CaptureModeCameraController>(delegate_.get())),
  334. blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
  335. // A task priority of BEST_EFFORT is good enough for this runner,
  336. // since it's used for blocking file IO such as saving the screenshots
  337. // or the successive webm video chunks received from the recording
  338. // service.
  339. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  340. base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})),
  341. num_consecutive_screenshots_scheduler_(
  342. FROM_HERE,
  343. kConsecutiveScreenshotThreshold,
  344. this,
  345. &CaptureModeController::RecordAndResetConsecutiveScreenshots) {
  346. DCHECK_EQ(g_instance, nullptr);
  347. g_instance = this;
  348. // Schedule recording of the number of screenshots taken per day.
  349. num_screenshots_taken_in_last_day_scheduler_.Start(
  350. FROM_HERE, base::Days(1),
  351. base::BindRepeating(
  352. &CaptureModeController::RecordAndResetScreenshotsTakenInLastDay,
  353. weak_ptr_factory_.GetWeakPtr()));
  354. // Schedule recording of the number of screenshots taken per week.
  355. num_screenshots_taken_in_last_week_scheduler_.Start(
  356. FROM_HERE, base::Days(7),
  357. base::BindRepeating(
  358. &CaptureModeController::RecordAndResetScreenshotsTakenInLastWeek,
  359. weak_ptr_factory_.GetWeakPtr()));
  360. DCHECK(!MessageViewFactory::HasCustomNotificationViewFactory(
  361. kScreenShotNotificationType));
  362. DCHECK(!MessageViewFactory::HasCustomNotificationViewFactory(
  363. kScreenRecordingNotificationType));
  364. if (features::IsNotificationsRefreshEnabled()) {
  365. MessageViewFactory::SetCustomNotificationViewFactory(
  366. kScreenShotNotificationType,
  367. base::BindRepeating(&CaptureModeAshNotificationView::CreateForImage));
  368. MessageViewFactory::SetCustomNotificationViewFactory(
  369. kScreenRecordingNotificationType,
  370. base::BindRepeating(&CaptureModeAshNotificationView::CreateForVideo));
  371. } else {
  372. MessageViewFactory::SetCustomNotificationViewFactory(
  373. kScreenShotNotificationType,
  374. base::BindRepeating(&CaptureModeNotificationView::CreateForImage));
  375. MessageViewFactory::SetCustomNotificationViewFactory(
  376. kScreenRecordingNotificationType,
  377. base::BindRepeating(&CaptureModeNotificationView::CreateForVideo));
  378. }
  379. Shell::Get()->session_controller()->AddObserver(this);
  380. chromeos::PowerManagerClient::Get()->AddObserver(this);
  381. }
  382. CaptureModeController::~CaptureModeController() {
  383. if (IsActive()) {
  384. // If for some reason a session was started after `OnChromeTerminating()`
  385. // was called (see https://crbug.com/1350711), we must explicitly shut it
  386. // down, so that it can stop observing the things it observes.
  387. Stop();
  388. }
  389. chromeos::PowerManagerClient::Get()->RemoveObserver(this);
  390. Shell::Get()->session_controller()->RemoveObserver(this);
  391. // Remove the custom notification view factories.
  392. MessageViewFactory::ClearCustomNotificationViewFactory(
  393. kScreenShotNotificationType);
  394. MessageViewFactory::ClearCustomNotificationViewFactory(
  395. kScreenRecordingNotificationType);
  396. DCHECK_EQ(g_instance, this);
  397. g_instance = nullptr;
  398. }
  399. // static
  400. CaptureModeController* CaptureModeController::Get() {
  401. DCHECK(g_instance);
  402. return g_instance;
  403. }
  404. // static
  405. void CaptureModeController::RegisterProfilePrefs(PrefRegistrySimple* registry) {
  406. registry->RegisterFilePathPref(kCustomCapturePathPrefName,
  407. /*default_value=*/base::FilePath());
  408. registry->RegisterBooleanPref(kUsesDefaultCapturePathPrefName,
  409. /*default_value=*/false);
  410. registry->RegisterBooleanPref(kCanShowCameraNudge, /*default_value=*/true);
  411. }
  412. bool CaptureModeController::IsActive() const {
  413. return capture_mode_session_ && !capture_mode_session_->is_shutting_down();
  414. }
  415. void CaptureModeController::SetSource(CaptureModeSource source) {
  416. if (source == source_)
  417. return;
  418. source_ = source;
  419. if (capture_mode_session_)
  420. capture_mode_session_->OnCaptureSourceChanged(source_);
  421. }
  422. void CaptureModeController::SetType(CaptureModeType type) {
  423. if (is_recording_in_progress() && type == CaptureModeType::kVideo) {
  424. // Overwrite video capture types to image, as we can't have more than one
  425. // recording at a time.
  426. type = CaptureModeType::kImage;
  427. }
  428. if (type == type_)
  429. return;
  430. type_ = type;
  431. if (capture_mode_session_)
  432. capture_mode_session_->OnCaptureTypeChanged(type_);
  433. }
  434. void CaptureModeController::EnableAudioRecording(bool enable_audio_recording) {
  435. if (enable_audio_recording_ == enable_audio_recording)
  436. return;
  437. enable_audio_recording_ = enable_audio_recording;
  438. }
  439. void CaptureModeController::Start(CaptureModeEntryType entry_type) {
  440. if (capture_mode_session_ || pending_dlp_check_)
  441. return;
  442. if (!delegate_->IsCaptureAllowedByPolicy()) {
  443. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  444. return;
  445. }
  446. pending_dlp_check_ = true;
  447. delegate_->CheckCaptureModeInitRestrictionByDlp(base::BindOnce(
  448. &CaptureModeController::OnDlpRestrictionCheckedAtSessionInit,
  449. weak_ptr_factory_.GetWeakPtr(), entry_type));
  450. }
  451. void CaptureModeController::Stop() {
  452. DCHECK(IsActive());
  453. capture_mode_session_->ReportSessionHistograms();
  454. capture_mode_session_->Shutdown();
  455. capture_mode_session_.reset();
  456. delegate_->OnSessionStateChanged(/*started=*/false);
  457. }
  458. void CaptureModeController::SetUserCaptureRegion(const gfx::Rect& region,
  459. bool by_user) {
  460. user_capture_region_ = region;
  461. if (!user_capture_region_.IsEmpty() && by_user)
  462. last_capture_region_update_time_ = base::TimeTicks::Now();
  463. if (!is_recording_in_progress() && source_ == CaptureModeSource::kRegion)
  464. camera_controller_->MaybeReparentPreviewWidget();
  465. }
  466. bool CaptureModeController::CanShowUserNudge() const {
  467. auto* session_controller = Shell::Get()->session_controller();
  468. DCHECK(session_controller->IsActiveUserSessionStarted());
  469. absl::optional<user_manager::UserType> user_type =
  470. session_controller->GetUserType();
  471. // This can only be called while a user is logged in, so `user_type` should
  472. // never be empty.
  473. DCHECK(user_type);
  474. switch (*user_type) {
  475. case user_manager::USER_TYPE_REGULAR:
  476. case user_manager::USER_TYPE_CHILD:
  477. // We only allow regular and child accounts to see the nudge.
  478. break;
  479. case user_manager::USER_TYPE_GUEST:
  480. case user_manager::USER_TYPE_PUBLIC_ACCOUNT:
  481. case user_manager::USER_TYPE_KIOSK_APP:
  482. case user_manager::USER_TYPE_ARC_KIOSK_APP:
  483. case user_manager::USER_TYPE_WEB_KIOSK_APP:
  484. case user_manager::USER_TYPE_ACTIVE_DIRECTORY:
  485. case user_manager::NUM_USER_TYPES:
  486. return false;
  487. }
  488. auto* pref_service = session_controller->GetActivePrefService();
  489. DCHECK(pref_service);
  490. return pref_service->GetBoolean(kCanShowCameraNudge);
  491. }
  492. void CaptureModeController::DisableUserNudgeForever() {
  493. GetActiveUserPrefService()->SetBoolean(kCanShowCameraNudge, false);
  494. }
  495. void CaptureModeController::SetUsesDefaultCaptureFolder(bool value) {
  496. GetActiveUserPrefService()->SetBoolean(kUsesDefaultCapturePathPrefName,
  497. value);
  498. if (IsActive())
  499. capture_mode_session_->OnDefaultCaptureFolderSelectionChanged();
  500. }
  501. void CaptureModeController::SetCustomCaptureFolder(const base::FilePath& path) {
  502. auto* pref_service = GetActiveUserPrefService();
  503. pref_service->SetFilePath(kCustomCapturePathPrefName, path);
  504. // When this function is called, it means the user is switching back to the
  505. // custom capture folder, and we need to reset the setting to force using the
  506. // default downloads folder.
  507. pref_service->SetBoolean(kUsesDefaultCapturePathPrefName, false);
  508. if (IsActive())
  509. capture_mode_session_->OnCaptureFolderMayHaveChanged();
  510. }
  511. base::FilePath CaptureModeController::GetCustomCaptureFolder() const {
  512. const auto custom_path =
  513. GetActiveUserPrefService()->GetFilePath(kCustomCapturePathPrefName);
  514. return custom_path != delegate_->GetUserDefaultDownloadsFolder()
  515. ? custom_path
  516. : base::FilePath();
  517. }
  518. CaptureModeController::CaptureFolder
  519. CaptureModeController::GetCurrentCaptureFolder() const {
  520. auto* session_controller = Shell::Get()->session_controller();
  521. if (!session_controller->IsActiveUserSessionStarted())
  522. return {GetTempDir(), /*is_default_downloads_folder=*/false};
  523. auto* pref_service = session_controller->GetActivePrefService();
  524. const auto default_downloads_folder =
  525. delegate_->GetUserDefaultDownloadsFolder();
  526. if (pref_service &&
  527. !pref_service->GetBoolean(kUsesDefaultCapturePathPrefName)) {
  528. const auto custom_path =
  529. pref_service->GetFilePath(kCustomCapturePathPrefName);
  530. if (!custom_path.empty()) {
  531. return {custom_path,
  532. /*is_default_downloads_folder=*/custom_path ==
  533. default_downloads_folder};
  534. }
  535. }
  536. return {default_downloads_folder,
  537. /*is_default_downloads_folder=*/true};
  538. }
  539. void CaptureModeController::CaptureScreenshotsOfAllDisplays() {
  540. if (pending_dlp_check_)
  541. return;
  542. if (!delegate_->IsCaptureAllowedByPolicy()) {
  543. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  544. return;
  545. }
  546. pending_dlp_check_ = true;
  547. delegate_->CheckCaptureModeInitRestrictionByDlp(base::BindOnce(
  548. &CaptureModeController::
  549. OnDlpRestrictionCheckedAtCaptureScreenshotsOfAllDisplays,
  550. weak_ptr_factory_.GetWeakPtr()));
  551. }
  552. void CaptureModeController::PerformCapture() {
  553. DCHECK(IsActive());
  554. if (pending_dlp_check_)
  555. return;
  556. const absl::optional<CaptureParams> capture_params = GetCaptureParams();
  557. if (!capture_params)
  558. return;
  559. DCHECK(!pending_dlp_check_);
  560. pending_dlp_check_ = true;
  561. capture_mode_session_->OnWaitingForDlpConfirmationStarted();
  562. delegate_->CheckCaptureOperationRestrictionByDlp(
  563. capture_params->window, capture_params->bounds,
  564. base::BindOnce(
  565. &CaptureModeController::OnDlpRestrictionCheckedAtPerformingCapture,
  566. weak_ptr_factory_.GetWeakPtr()));
  567. }
  568. void CaptureModeController::EndVideoRecording(EndRecordingReason reason) {
  569. RecordEndRecordingReason(reason);
  570. recording_service_remote_->StopRecording();
  571. TerminateRecordingUiElements();
  572. }
  573. void CaptureModeController::CheckFolderAvailability(
  574. const base::FilePath& folder,
  575. base::OnceCallback<void(bool available)> callback) {
  576. blocking_task_runner_->PostTaskAndReplyWithResult(
  577. FROM_HERE, base::BindOnce(&base::PathExists, folder),
  578. std::move(callback));
  579. }
  580. void CaptureModeController::SetWindowProtectionMask(aura::Window* window,
  581. uint32_t protection_mask) {
  582. if (protection_mask == display::CONTENT_PROTECTION_METHOD_NONE)
  583. protected_windows_.erase(window);
  584. else
  585. protected_windows_[window] = protection_mask;
  586. RefreshContentProtection();
  587. }
  588. void CaptureModeController::RefreshContentProtection() {
  589. if (!is_recording_in_progress())
  590. return;
  591. DCHECK(video_recording_watcher_);
  592. if (ShouldBlockRecordingForContentProtection(
  593. video_recording_watcher_->window_being_recorded())) {
  594. // HDCP violation is also considered a failure, and we're going to terminate
  595. // the service immediately so as not to record any further frames.
  596. RecordEndRecordingReason(EndRecordingReason::kHdcpInterruption);
  597. FinalizeRecording(/*success=*/false, gfx::ImageSkia());
  598. ShowVideoRecordingStoppedByHdcpNotification();
  599. }
  600. }
  601. void CaptureModeController::ToggleRecordingOverlayEnabled() {
  602. DCHECK(is_recording_in_progress());
  603. DCHECK(video_recording_watcher_->is_in_projector_mode());
  604. video_recording_watcher_->ToggleRecordingOverlayEnabled();
  605. }
  606. std::unique_ptr<RecordingOverlayView>
  607. CaptureModeController::CreateRecordingOverlayView() {
  608. return delegate_->CreateRecordingOverlayView();
  609. }
  610. bool CaptureModeController::IsRootDriveFsPath(
  611. const base::FilePath& path) const {
  612. base::FilePath mounted_path;
  613. if (delegate_->GetDriveFsMountPointPath(&mounted_path)) {
  614. if (path == mounted_path.Append("root"))
  615. return true;
  616. }
  617. return false;
  618. }
  619. bool CaptureModeController::IsAndroidFilesPath(
  620. const base::FilePath& path) const {
  621. return path == delegate_->GetAndroidFilesPath();
  622. }
  623. bool CaptureModeController::IsLinuxFilesPath(const base::FilePath& path) const {
  624. return path == delegate_->GetLinuxFilesPath();
  625. }
  626. aura::Window* CaptureModeController::GetCameraPreviewParentWindow() const {
  627. // Trying to get camera preview's parent from `video_recording_watcher_` first
  628. // if a video recording is in progress. As a capture session can be started
  629. // with `kImage` type while recording, and we should get the parent of the
  630. // camera preview with the settings inside VideoRecordingWatcher in this case,
  631. // e.g, CaptureModeSource for taking the video.
  632. if (is_recording_in_progress())
  633. return video_recording_watcher_->GetCameraPreviewParentWindow();
  634. if (IsActive())
  635. return capture_mode_session_->GetCameraPreviewParentWindow();
  636. return nullptr;
  637. }
  638. gfx::Rect CaptureModeController::GetCameraPreviewConfineBounds() const {
  639. // Getting the bounds from `video_recording_watcher_` first if a video
  640. // recording is in progress. As a capture session can be started with `kImage`
  641. // type while recording, and we should get the bounds with the settings inside
  642. // VideoRecordingWatcher in this case, e.g, user-selected region.
  643. if (is_recording_in_progress())
  644. return video_recording_watcher_->GetCameraPreviewConfineBounds();
  645. if (IsActive())
  646. return capture_mode_session_->GetCameraPreviewConfineBounds();
  647. return gfx::Rect();
  648. }
  649. void CaptureModeController::OnRecordingEnded(
  650. recording::mojom::RecordingStatus status,
  651. const gfx::ImageSkia& thumbnail) {
  652. low_disk_space_threshold_reached_ =
  653. status == recording::mojom::RecordingStatus::kLowDiskSpace ||
  654. status == recording::mojom::RecordingStatus::kLowDriveFsQuota;
  655. EmitServiceRecordingStatus(status);
  656. FinalizeRecording(status == recording::mojom::RecordingStatus::kSuccess,
  657. thumbnail);
  658. }
  659. void CaptureModeController::GetDriveFsFreeSpaceBytes(
  660. GetDriveFsFreeSpaceBytesCallback callback) {
  661. delegate_->GetDriveFsFreeSpaceBytes(std::move(callback));
  662. }
  663. void CaptureModeController::OnActiveUserSessionChanged(
  664. const AccountId& account_id) {
  665. EndSessionOrRecording(EndRecordingReason::kActiveUserChange);
  666. camera_controller_->OnActiveUserSessionChanged();
  667. // Remove the previous notification when switching to another user.
  668. auto* message_center = message_center::MessageCenter::Get();
  669. message_center->RemoveNotification(kScreenCaptureNotificationId,
  670. /*by_user=*/false);
  671. }
  672. void CaptureModeController::OnSessionStateChanged(
  673. session_manager::SessionState state) {
  674. if (Shell::Get()->session_controller()->IsUserSessionBlocked())
  675. EndSessionOrRecording(EndRecordingReason::kSessionBlocked);
  676. }
  677. void CaptureModeController::OnChromeTerminating() {
  678. // Order here matters. We may shutdown while a session with a camera is active
  679. // before recording starts, we need to inform the camera controller first to
  680. // destroy the camera preview first.
  681. camera_controller_->OnShuttingDown();
  682. EndSessionOrRecording(EndRecordingReason::kShuttingDown);
  683. }
  684. void CaptureModeController::SuspendImminent(
  685. power_manager::SuspendImminent::Reason reason) {
  686. EndSessionOrRecording(EndRecordingReason::kImminentSuspend);
  687. }
  688. void CaptureModeController::StartVideoRecordingImmediatelyForTesting() {
  689. DCHECK(IsActive());
  690. DCHECK_EQ(type_, CaptureModeType::kVideo);
  691. OnVideoRecordCountDownFinished();
  692. }
  693. void CaptureModeController::PushNewRootSizeToRecordingService(
  694. const gfx::Size& root_size,
  695. float device_scale_factor) {
  696. DCHECK(is_recording_in_progress());
  697. DCHECK(video_recording_watcher_);
  698. DCHECK(recording_service_remote_);
  699. recording_service_remote_->OnFrameSinkSizeChanged(root_size,
  700. device_scale_factor);
  701. }
  702. void CaptureModeController::OnRecordedWindowChangingRoot(
  703. aura::Window* window,
  704. aura::Window* new_root) {
  705. DCHECK(is_recording_in_progress());
  706. DCHECK(video_recording_watcher_);
  707. DCHECK_EQ(window, video_recording_watcher_->window_being_recorded());
  708. DCHECK(recording_service_remote_);
  709. DCHECK(new_root);
  710. // When a window being recorded changes displays either due to a display
  711. // getting disconnected, or moved by the user, the stop-recording button
  712. // should follow that window to that display.
  713. capture_mode_util::SetStopRecordingButtonVisibility(window->GetRootWindow(),
  714. false);
  715. capture_mode_util::SetStopRecordingButtonVisibility(new_root, true);
  716. recording_service_remote_->OnRecordedWindowChangingRoot(
  717. new_root->GetFrameSinkId(), new_root->GetBoundsInRootWindow().size(),
  718. new_root->GetHost()->device_scale_factor());
  719. }
  720. void CaptureModeController::OnRecordedWindowSizeChanged(
  721. const gfx::Size& new_size) {
  722. DCHECK(is_recording_in_progress());
  723. DCHECK(video_recording_watcher_);
  724. DCHECK(recording_service_remote_);
  725. recording_service_remote_->OnRecordedWindowSizeChanged(new_size);
  726. }
  727. bool CaptureModeController::ShouldBlockRecordingForContentProtection(
  728. aura::Window* window_being_recorded) const {
  729. DCHECK(window_being_recorded);
  730. // The protected window can be a descendant of the window being recorded, for
  731. // examples:
  732. // - When recording a fullscreen or partial region of it, the
  733. // |window_being_recorded| in this case is the root window, and a
  734. // protected window on this root will be a descendant.
  735. // - When recording a browser window showing a page with protected content,
  736. // the |window_being_recorded| in this case is the BrowserFrame, while the
  737. // protected window will be the RenderWidgetHostViewAura, which is also a
  738. // descendant.
  739. for (const auto& iter : protected_windows_) {
  740. if (window_being_recorded->Contains(iter.first))
  741. return true;
  742. }
  743. return false;
  744. }
  745. void CaptureModeController::EndSessionOrRecording(EndRecordingReason reason) {
  746. if (IsActive()) {
  747. // Suspend or user session changes can happen while the capture mode session
  748. // is active or after the three-second countdown had started but not
  749. // finished yet.
  750. Stop();
  751. }
  752. if (!is_recording_in_progress())
  753. return;
  754. if (reason == EndRecordingReason::kImminentSuspend ||
  755. reason == EndRecordingReason::kShuttingDown) {
  756. // If suspend or shutdown happen while recording is in progress, we consider
  757. // this a failure, and cut the recording immediately. The recording service
  758. // will flush any remaining buffered chunks in the muxer before it
  759. // terminates.
  760. RecordEndRecordingReason(reason);
  761. FinalizeRecording(/*success=*/false, gfx::ImageSkia());
  762. return;
  763. }
  764. EndVideoRecording(reason);
  765. }
  766. absl::optional<CaptureModeController::CaptureParams>
  767. CaptureModeController::GetCaptureParams() const {
  768. DCHECK(IsActive());
  769. aura::Window* window = nullptr;
  770. gfx::Rect bounds;
  771. switch (source_) {
  772. case CaptureModeSource::kFullscreen:
  773. window = capture_mode_session_->current_root();
  774. DCHECK(window);
  775. DCHECK(window->IsRootWindow());
  776. bounds = window->bounds();
  777. break;
  778. case CaptureModeSource::kWindow:
  779. window = capture_mode_session_->GetSelectedWindow();
  780. if (!window) {
  781. // TODO(afakhry): Consider showing a toast or a notification that no
  782. // window was selected.
  783. return absl::nullopt;
  784. }
  785. // window->bounds() are in root coordinates, but we want to get the
  786. // capture area in |window|'s coordinates.
  787. bounds = gfx::Rect(window->bounds().size());
  788. break;
  789. case CaptureModeSource::kRegion:
  790. window = capture_mode_session_->current_root();
  791. DCHECK(window);
  792. DCHECK(window->IsRootWindow());
  793. if (user_capture_region_.IsEmpty()) {
  794. // TODO(afakhry): Consider showing a toast or a notification that no
  795. // region was selected.
  796. return absl::nullopt;
  797. }
  798. // TODO(afakhry): Consider any special handling of display scale changes
  799. // while video recording is in progress.
  800. bounds = user_capture_region_;
  801. break;
  802. }
  803. DCHECK(window);
  804. return CaptureParams{window, bounds};
  805. }
  806. void CaptureModeController::LaunchRecordingServiceAndStartRecording(
  807. const CaptureParams& capture_params,
  808. mojo::PendingReceiver<viz::mojom::FrameSinkVideoCaptureOverlay>
  809. cursor_overlay) {
  810. DCHECK(!recording_service_remote_.is_bound())
  811. << "Should not launch a new recording service while one is already "
  812. "running.";
  813. recording_service_remote_.reset();
  814. recording_service_client_receiver_.reset();
  815. drive_fs_quota_delegate_receiver_.reset();
  816. recording_service_remote_ = delegate_->LaunchRecordingService();
  817. recording_service_remote_.set_disconnect_handler(
  818. base::BindOnce(&CaptureModeController::OnRecordingServiceDisconnected,
  819. base::Unretained(this)));
  820. // Prepare the pending remotes of the client, the video capturer, and the
  821. // audio stream factory.
  822. mojo::PendingRemote<recording::mojom::RecordingServiceClient> client =
  823. recording_service_client_receiver_.BindNewPipeAndPassRemote();
  824. mojo::Remote<viz::mojom::FrameSinkVideoCapturer> video_capturer_remote;
  825. aura::Env::GetInstance()
  826. ->context_factory()
  827. ->GetHostFrameSinkManager()
  828. ->CreateVideoCapturer(video_capturer_remote.BindNewPipeAndPassReceiver());
  829. // The overlay is to be rendered on top of the video frames.
  830. constexpr int kStackingIndex = 1;
  831. video_capturer_remote->CreateOverlay(kStackingIndex,
  832. std::move(cursor_overlay));
  833. // We bind the audio stream factory only if audio recording is enabled. This
  834. // is ok since the |audio_stream_factory| parameter in the recording service
  835. // APIs is optional, and can be not bound.
  836. mojo::PendingRemote<media::mojom::AudioStreamFactory> audio_stream_factory;
  837. if (enable_audio_recording_) {
  838. delegate_->BindAudioStreamFactory(
  839. audio_stream_factory.InitWithNewPipeAndPassReceiver());
  840. }
  841. // Only act as a `DriveFsQuotaDelegate` for the recording service if the video
  842. // file will be saved to a location in DriveFS.
  843. mojo::PendingRemote<recording::mojom::DriveFsQuotaDelegate>
  844. drive_fs_quota_delegate;
  845. const auto file_location = GetSaveToOption(current_video_file_path_);
  846. if (file_location == CaptureModeSaveToLocation::kDrive ||
  847. file_location == CaptureModeSaveToLocation::kDriveFolder) {
  848. drive_fs_quota_delegate =
  849. drive_fs_quota_delegate_receiver_.BindNewPipeAndPassRemote();
  850. }
  851. auto* root_window = capture_params.window->GetRootWindow();
  852. const auto frame_sink_id = root_window->GetFrameSinkId();
  853. DCHECK(frame_sink_id.is_valid());
  854. const float device_scale_factor =
  855. root_window->GetHost()->device_scale_factor();
  856. const gfx::Size frame_sink_size_dip = root_window->bounds().size();
  857. const auto bounds = capture_params.bounds;
  858. switch (source_) {
  859. case CaptureModeSource::kFullscreen:
  860. recording_service_remote_->RecordFullscreen(
  861. std::move(client), video_capturer_remote.Unbind(),
  862. std::move(audio_stream_factory), std::move(drive_fs_quota_delegate),
  863. current_video_file_path_, frame_sink_id, frame_sink_size_dip,
  864. device_scale_factor);
  865. break;
  866. case CaptureModeSource::kWindow:
  867. // Non-root window are not capturable by the |FrameSinkVideoCapturer|
  868. // unless its layer tree is identified by a |viz::SubtreeCaptureId|.
  869. // The |VideoRecordingWatcher| that we create while recording is in
  870. // progress creates a request to mark that window as capturable.
  871. // See https://crbug.com/1143930 for more details.
  872. DCHECK(!capture_params.window->IsRootWindow());
  873. DCHECK(capture_params.window->subtree_capture_id().is_valid());
  874. recording_service_remote_->RecordWindow(
  875. std::move(client), video_capturer_remote.Unbind(),
  876. std::move(audio_stream_factory), std::move(drive_fs_quota_delegate),
  877. current_video_file_path_, frame_sink_id, frame_sink_size_dip,
  878. device_scale_factor, capture_params.window->subtree_capture_id(),
  879. bounds.size());
  880. break;
  881. case CaptureModeSource::kRegion:
  882. recording_service_remote_->RecordRegion(
  883. std::move(client), video_capturer_remote.Unbind(),
  884. std::move(audio_stream_factory), std::move(drive_fs_quota_delegate),
  885. current_video_file_path_, frame_sink_id, frame_sink_size_dip,
  886. device_scale_factor, bounds);
  887. break;
  888. }
  889. }
  890. void CaptureModeController::OnRecordingServiceDisconnected() {
  891. // TODO(afakhry): Consider what to do if the service crashes during an ongoing
  892. // video recording. Do we try to resume recording, or notify with failure?
  893. // For now, just end the recording.
  894. // Note that the service could disconnect between the time we ask it to
  895. // StopRecording(), and it calling us back with OnRecordingEnded(), so we call
  896. // FinalizeRecording() in all cases.
  897. RecordEndRecordingReason(EndRecordingReason::kRecordingServiceDisconnected);
  898. FinalizeRecording(/*success=*/false, gfx::ImageSkia());
  899. }
  900. void CaptureModeController::FinalizeRecording(bool success,
  901. const gfx::ImageSkia& thumbnail) {
  902. // If |success| is false, then recording has been force-terminated due to a
  903. // failure on the service side, or a disconnection to it. We need to terminate
  904. // the recording-related UI elements.
  905. if (!success) {
  906. // TODO(afakhry): Show user a failure message.
  907. TerminateRecordingUiElements();
  908. }
  909. // Resetting the service remote would terminate its process.
  910. recording_service_remote_.reset();
  911. delegate_->OnServiceRemoteReset();
  912. recording_service_client_receiver_.reset();
  913. drive_fs_quota_delegate_receiver_.reset();
  914. const bool was_in_projector_mode =
  915. video_recording_watcher_->is_in_projector_mode();
  916. video_recording_watcher_.reset();
  917. delegate_->StopObservingRestrictedContent(
  918. base::BindOnce(&CaptureModeController::OnDlpRestrictionCheckedAtVideoEnd,
  919. weak_ptr_factory_.GetWeakPtr(), thumbnail, success,
  920. was_in_projector_mode));
  921. }
  922. void CaptureModeController::TerminateRecordingUiElements() {
  923. if (!is_recording_in_progress())
  924. return;
  925. capture_mode_util::SetStopRecordingButtonVisibility(
  926. video_recording_watcher_->window_being_recorded()->GetRootWindow(),
  927. false);
  928. capture_mode_util::TriggerAccessibilityAlert(
  929. IDS_ASH_SCREEN_CAPTURE_ALERT_RECORDING_STOPPED);
  930. video_recording_watcher_->ShutDown();
  931. }
  932. void CaptureModeController::CaptureImage(const CaptureParams& capture_params,
  933. const base::FilePath& path) {
  934. // Note that |type_| may not necessarily be |kImage| here, since this may be
  935. // called to take an instant fullscreen screenshot for the keyboard shortcut,
  936. // which doesn't go through the capture mode UI, and doesn't change |type_|.
  937. DCHECK(delegate_->IsCaptureAllowedByPolicy());
  938. // Stop the capture session now, so as not to take a screenshot of the capture
  939. // bar.
  940. if (IsActive())
  941. Stop();
  942. DCHECK(!capture_params.bounds.IsEmpty());
  943. auto* cursor_manager = Shell::Get()->cursor_manager();
  944. bool was_cursor_originally_blocked = cursor_manager->IsCursorLocked();
  945. if (!was_cursor_originally_blocked) {
  946. cursor_manager->HideCursor();
  947. cursor_manager->LockCursor();
  948. }
  949. ui::GrabWindowSnapshotAsyncPNG(
  950. capture_params.window, capture_params.bounds,
  951. base::BindOnce(&CaptureModeController::OnImageCaptured,
  952. weak_ptr_factory_.GetWeakPtr(), path,
  953. was_cursor_originally_blocked));
  954. ++num_screenshots_taken_in_last_day_;
  955. ++num_screenshots_taken_in_last_week_;
  956. ++num_consecutive_screenshots_;
  957. num_consecutive_screenshots_scheduler_.Reset();
  958. capture_mode_util::TriggerAccessibilityAlert(
  959. IDS_ASH_SCREEN_CAPTURE_ALERT_SCREENSHOT_CAPTURED);
  960. delegate_->OnCaptureImageAttempted(capture_params.window,
  961. capture_params.bounds);
  962. }
  963. void CaptureModeController::CaptureVideo(const CaptureParams& capture_params) {
  964. DCHECK_EQ(CaptureModeType::kVideo, type_);
  965. DCHECK(delegate_->IsCaptureAllowedByPolicy());
  966. if (skip_count_down_ui_) {
  967. OnVideoRecordCountDownFinished();
  968. return;
  969. }
  970. capture_mode_session_->StartCountDown(
  971. base::BindOnce(&CaptureModeController::OnVideoRecordCountDownFinished,
  972. weak_ptr_factory_.GetWeakPtr()));
  973. capture_mode_util::TriggerAccessibilityAlert(
  974. IDS_ASH_SCREEN_CAPTURE_ALERT_RECORDING_STARTING);
  975. }
  976. void CaptureModeController::OnImageCaptured(
  977. const base::FilePath& path,
  978. bool was_cursor_originally_blocked,
  979. scoped_refptr<base::RefCountedMemory> png_bytes) {
  980. if (!was_cursor_originally_blocked) {
  981. auto* shell = Shell::Get();
  982. auto* cursor_manager = shell->cursor_manager();
  983. if (!shell->tablet_mode_controller()->InTabletMode())
  984. cursor_manager->ShowCursor();
  985. cursor_manager->UnlockCursor();
  986. }
  987. if (!png_bytes || !png_bytes->size()) {
  988. LOG(ERROR) << "Failed to capture image.";
  989. ShowFailureNotification();
  990. return;
  991. }
  992. blocking_task_runner_->PostTaskAndReplyWithResult(
  993. FROM_HERE,
  994. base::BindOnce(&SaveFile, png_bytes, path,
  995. GetFallbackFilePathFromFile(path)),
  996. base::BindOnce(&CaptureModeController::OnImageFileSaved,
  997. weak_ptr_factory_.GetWeakPtr(), png_bytes));
  998. }
  999. void CaptureModeController::OnImageFileSaved(
  1000. scoped_refptr<base::RefCountedMemory> png_bytes,
  1001. const base::FilePath& file_saved_path) {
  1002. if (file_saved_path.empty()) {
  1003. ShowFailureNotification();
  1004. return;
  1005. }
  1006. if (on_file_saved_callback_for_test_)
  1007. std::move(on_file_saved_callback_for_test_).Run(file_saved_path);
  1008. DCHECK(png_bytes && png_bytes->size());
  1009. const auto image = gfx::Image::CreateFrom1xPNGBytes(png_bytes);
  1010. CopyImageToClipboard(image);
  1011. ShowPreviewNotification(file_saved_path, image, CaptureModeType::kImage);
  1012. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted())
  1013. RecordSaveToLocation(GetSaveToOption(file_saved_path));
  1014. HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
  1015. if (client) // May be `nullptr` in tests.
  1016. client->AddScreenshot(file_saved_path);
  1017. }
  1018. void CaptureModeController::OnVideoFileSaved(
  1019. const base::FilePath& saved_video_file_path,
  1020. const gfx::ImageSkia& video_thumbnail,
  1021. bool success,
  1022. bool in_projector_mode) {
  1023. DCHECK(base::CurrentUIThread::IsSet());
  1024. if (!success) {
  1025. ShowFailureNotification();
  1026. } else {
  1027. if (!in_projector_mode) {
  1028. ShowPreviewNotification(saved_video_file_path,
  1029. gfx::Image(video_thumbnail),
  1030. CaptureModeType::kVideo);
  1031. HoldingSpaceClient* client = HoldingSpaceController::Get()->client();
  1032. if (client) // May be `nullptr` in tests.
  1033. client->AddScreenRecording(saved_video_file_path);
  1034. }
  1035. DCHECK(!recording_start_time_.is_null());
  1036. RecordCaptureModeRecordTime(
  1037. (base::TimeTicks::Now() - recording_start_time_).InSeconds(),
  1038. in_projector_mode);
  1039. }
  1040. if (Shell::Get()->session_controller()->IsActiveUserSessionStarted())
  1041. RecordSaveToLocation(GetSaveToOption(saved_video_file_path));
  1042. if (on_file_saved_callback_for_test_)
  1043. std::move(on_file_saved_callback_for_test_).Run(saved_video_file_path);
  1044. }
  1045. void CaptureModeController::ShowPreviewNotification(
  1046. const base::FilePath& screen_capture_path,
  1047. const gfx::Image& preview_image,
  1048. const CaptureModeType type) {
  1049. const bool for_video = type == CaptureModeType::kVideo;
  1050. const int title_id = for_video ? IDS_ASH_SCREEN_CAPTURE_RECORDING_TITLE
  1051. : IDS_ASH_SCREEN_CAPTURE_SCREENSHOT_TITLE;
  1052. const int message_id = for_video && low_disk_space_threshold_reached_
  1053. ? IDS_ASH_SCREEN_CAPTURE_LOW_STORAGE_SPACE_MESSAGE
  1054. : IDS_ASH_SCREEN_CAPTURE_MESSAGE;
  1055. message_center::RichNotificationData optional_fields;
  1056. message_center::ButtonInfo edit_button(
  1057. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_BUTTON_EDIT));
  1058. if (!for_video && !Shell::Get()->session_controller()->IsUserSessionBlocked())
  1059. optional_fields.buttons.push_back(edit_button);
  1060. message_center::ButtonInfo delete_button(
  1061. l10n_util::GetStringUTF16(IDS_ASH_SCREEN_CAPTURE_BUTTON_DELETE));
  1062. optional_fields.buttons.push_back(delete_button);
  1063. optional_fields.image = preview_image;
  1064. ShowNotification(
  1065. kScreenCaptureNotificationId, title_id, message_id, optional_fields,
  1066. base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
  1067. base::BindRepeating(&CaptureModeController::HandleNotificationClicked,
  1068. weak_ptr_factory_.GetWeakPtr(),
  1069. screen_capture_path, type)),
  1070. message_center::SystemNotificationWarningLevel::NORMAL, kCaptureModeIcon,
  1071. for_video);
  1072. }
  1073. void CaptureModeController::HandleNotificationClicked(
  1074. const base::FilePath& screen_capture_path,
  1075. const CaptureModeType type,
  1076. absl::optional<int> button_index) {
  1077. if (!button_index.has_value()) {
  1078. // Show the item in the folder.
  1079. delegate_->ShowScreenCaptureItemInFolder(screen_capture_path);
  1080. RecordScreenshotNotificationQuickAction(CaptureQuickAction::kFiles);
  1081. } else {
  1082. const int button_index_value = button_index.value();
  1083. if (type == CaptureModeType::kVideo) {
  1084. DCHECK_EQ(button_index_value,
  1085. VideoNotificationButtonIndex::BUTTON_DELETE_VIDEO);
  1086. DeleteFileAsync(blocking_task_runner_, screen_capture_path,
  1087. std::move(on_file_deleted_callback_for_test_));
  1088. } else {
  1089. DCHECK_EQ(type, CaptureModeType::kImage);
  1090. switch (button_index_value) {
  1091. case ScreenshotNotificationButtonIndex::BUTTON_EDIT:
  1092. delegate_->OpenScreenshotInImageEditor(screen_capture_path);
  1093. RecordScreenshotNotificationQuickAction(
  1094. CaptureQuickAction::kBacklight);
  1095. break;
  1096. case ScreenshotNotificationButtonIndex::BUTTON_DELETE:
  1097. DeleteFileAsync(blocking_task_runner_, screen_capture_path,
  1098. std::move(on_file_deleted_callback_for_test_));
  1099. RecordScreenshotNotificationQuickAction(CaptureQuickAction::kDelete);
  1100. break;
  1101. default:
  1102. NOTREACHED();
  1103. break;
  1104. }
  1105. }
  1106. }
  1107. // This has to be done at the end to avoid a use-after-free crash, since
  1108. // removing the notification will delete its delegate, which owns the callback
  1109. // to this function. The callback's state owns any passed-by-ref arguments,
  1110. // such as |screen_capture_path| which we use in this function.
  1111. message_center::MessageCenter::Get()->RemoveNotification(
  1112. kScreenCaptureNotificationId, /*by_user=*/false);
  1113. }
  1114. base::FilePath CaptureModeController::BuildImagePath() const {
  1115. return BuildPathNoExtension(kScreenshotFileNameFmtStr, base::Time::Now())
  1116. .AddExtension("png");
  1117. }
  1118. base::FilePath CaptureModeController::BuildVideoPath() const {
  1119. return BuildPathNoExtension(kVideoFileNameFmtStr, base::Time::Now())
  1120. .AddExtension("webm");
  1121. }
  1122. base::FilePath CaptureModeController::BuildImagePathForDisplay(
  1123. int display_index) const {
  1124. auto path_str =
  1125. BuildPathNoExtension(kScreenshotFileNameFmtStr, base::Time::Now())
  1126. .value();
  1127. auto full_path = base::StringPrintf("%s - Display %d.png", path_str.c_str(),
  1128. display_index);
  1129. return base::FilePath(full_path);
  1130. }
  1131. base::FilePath CaptureModeController::BuildPathNoExtension(
  1132. const char* const format_string,
  1133. base::Time timestamp) const {
  1134. base::Time::Exploded exploded_time;
  1135. timestamp.LocalExplode(&exploded_time);
  1136. return GetCurrentCaptureFolder().path.AppendASCII(base::StringPrintf(
  1137. format_string, GetDateStr(exploded_time).c_str(),
  1138. GetTimeStr(exploded_time, delegate_->Uses24HourFormat()).c_str()));
  1139. }
  1140. base::FilePath CaptureModeController::GetFallbackFilePathFromFile(
  1141. const base::FilePath& path) {
  1142. auto* session_controller = Shell::Get()->session_controller();
  1143. const auto fallback_dir = session_controller->IsActiveUserSessionStarted()
  1144. ? delegate_->GetUserDefaultDownloadsFolder()
  1145. : GetTempDir();
  1146. return fallback_dir.Append(path.BaseName());
  1147. }
  1148. void CaptureModeController::RecordAndResetScreenshotsTakenInLastDay() {
  1149. RecordNumberOfScreenshotsTakenInLastDay(num_screenshots_taken_in_last_day_);
  1150. num_screenshots_taken_in_last_day_ = 0;
  1151. }
  1152. void CaptureModeController::RecordAndResetScreenshotsTakenInLastWeek() {
  1153. RecordNumberOfScreenshotsTakenInLastWeek(num_screenshots_taken_in_last_week_);
  1154. num_screenshots_taken_in_last_week_ = 0;
  1155. }
  1156. void CaptureModeController::RecordAndResetConsecutiveScreenshots() {
  1157. RecordNumberOfConsecutiveScreenshots(num_consecutive_screenshots_);
  1158. num_consecutive_screenshots_ = 0;
  1159. }
  1160. void CaptureModeController::OnVideoRecordCountDownFinished() {
  1161. // Ensure `on_countdown_finished_callback_for_test_` is run after this
  1162. // function.
  1163. base::ScopedClosureRunner scoped_closure(
  1164. std::move(on_countdown_finished_callback_for_test_));
  1165. // If this event is dispatched after the capture session was cancelled or
  1166. // destroyed, this should be a no-op.
  1167. if (!IsActive())
  1168. return;
  1169. const absl::optional<CaptureParams> capture_params = GetCaptureParams();
  1170. if (!capture_params) {
  1171. // There's nothing to capture, so we'll stop the session and skip the rest.
  1172. Stop();
  1173. return;
  1174. }
  1175. // During the 3-second count down, screen content might have changed. We must
  1176. // check again the DLP restrictions.
  1177. DCHECK(!pending_dlp_check_);
  1178. pending_dlp_check_ = true;
  1179. capture_mode_session_->OnWaitingForDlpConfirmationStarted();
  1180. delegate_->CheckCaptureOperationRestrictionByDlp(
  1181. capture_params->window, capture_params->bounds,
  1182. base::BindOnce(
  1183. &CaptureModeController::OnDlpRestrictionCheckedAtCountDownFinished,
  1184. weak_ptr_factory_.GetWeakPtr()));
  1185. }
  1186. void CaptureModeController::OnProjectorContainerFolderCreated(
  1187. const CaptureParams& capture_params,
  1188. const base::FilePath& file_path_no_extension) {
  1189. if (!IsActive()) {
  1190. // This function gets called asynchronously, and until it gets called, the
  1191. // session could end due e.g. locking the screen, suspending, or switching
  1192. // users.
  1193. return;
  1194. }
  1195. // An empty path is sent to indicate an error.
  1196. if (file_path_no_extension.empty()) {
  1197. Stop();
  1198. return;
  1199. }
  1200. BeginVideoRecording(capture_params, /*for_projector=*/true,
  1201. file_path_no_extension.AddExtension("webm"));
  1202. }
  1203. void CaptureModeController::BeginVideoRecording(
  1204. const CaptureParams& capture_params,
  1205. bool for_projector,
  1206. const base::FilePath& video_file_path) {
  1207. DCHECK_EQ(capture_mode_session_->is_in_projector_mode(), for_projector);
  1208. DCHECK(!video_file_path.empty());
  1209. DCHECK(video_file_path.MatchesExtension(".webm"));
  1210. if (!IsActive()) {
  1211. // This function gets called asynchronously, and until it gets called, the
  1212. // session could end due to e.g. locking the screen, suspending, or
  1213. // switching users.
  1214. return;
  1215. }
  1216. base::AutoReset<bool> initializing_resetter(&is_initializing_recording_,
  1217. true);
  1218. // Do not trigger an alert when exiting the session, since we end the session
  1219. // to start recording.
  1220. capture_mode_session_->set_a11y_alert_on_session_exit(false);
  1221. // Acquire the session's layer in order to potentially reuse it for painting
  1222. // a highlight around the region being recorded.
  1223. std::unique_ptr<ui::Layer> session_layer =
  1224. capture_mode_session_->ReleaseLayer();
  1225. session_layer->set_delegate(nullptr);
  1226. // At this point, recording is guaranteed to start, and cannot be prevented by
  1227. // DLP or user cancellation.
  1228. capture_mode_session_->set_is_stopping_to_start_video_recording(true);
  1229. // Stop the capture session now, so the bar doesn't show up in the captured
  1230. // video.
  1231. Stop();
  1232. mojo::PendingRemote<viz::mojom::FrameSinkVideoCaptureOverlay>
  1233. cursor_capture_overlay;
  1234. auto cursor_overlay_receiver =
  1235. cursor_capture_overlay.InitWithNewPipeAndPassReceiver();
  1236. video_recording_watcher_ = std::make_unique<VideoRecordingWatcher>(
  1237. this, capture_params.window, std::move(cursor_capture_overlay),
  1238. for_projector);
  1239. // We only paint the recorded area highlight for window and region captures.
  1240. if (source_ != CaptureModeSource::kFullscreen)
  1241. video_recording_watcher_->Reset(std::move(session_layer));
  1242. DCHECK(current_video_file_path_.empty());
  1243. recording_start_time_ = base::TimeTicks::Now();
  1244. current_video_file_path_ = video_file_path;
  1245. LaunchRecordingServiceAndStartRecording(capture_params,
  1246. std::move(cursor_overlay_receiver));
  1247. capture_mode_util::SetStopRecordingButtonVisibility(
  1248. capture_params.window->GetRootWindow(), true);
  1249. delegate_->StartObservingRestrictedContent(
  1250. capture_params.window, capture_params.bounds,
  1251. base::BindOnce(&CaptureModeController::InterruptVideoRecording,
  1252. weak_ptr_factory_.GetWeakPtr()));
  1253. }
  1254. void CaptureModeController::InterruptVideoRecording() {
  1255. EndVideoRecording(EndRecordingReason::kDlpInterruption);
  1256. }
  1257. void CaptureModeController::OnDlpRestrictionCheckedAtPerformingCapture(
  1258. bool proceed) {
  1259. pending_dlp_check_ = false;
  1260. if (!IsActive()) {
  1261. // This function gets called asynchronously, and until it gets called, the
  1262. // session could end due to e.g. locking the screen, suspending, or
  1263. // switching users.
  1264. return;
  1265. }
  1266. // We don't need to bring capture mode UIs back if `proceed` is false or if
  1267. // `type_` is `CaptureModeType::kImage`, since the session is about to
  1268. // shutdown anyways at these use cases, so it's better to avoid any wasted
  1269. // effort. In the case of video recording, we need to reshow the UIs so that
  1270. // we can start the 3-second count down animation.
  1271. capture_mode_session_->OnWaitingForDlpConfirmationEnded(
  1272. /*reshow_uis=*/proceed && type_ != CaptureModeType::kImage);
  1273. if (!proceed) {
  1274. Stop();
  1275. return;
  1276. }
  1277. const absl::optional<CaptureParams> capture_params = GetCaptureParams();
  1278. DCHECK(capture_params);
  1279. if (!delegate_->IsCaptureAllowedByPolicy()) {
  1280. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  1281. Stop();
  1282. return;
  1283. }
  1284. if (type_ == CaptureModeType::kImage) {
  1285. CaptureImage(*capture_params, BuildImagePath());
  1286. } else {
  1287. // HDCP affects only video recording.
  1288. if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
  1289. ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
  1290. Stop();
  1291. return;
  1292. }
  1293. CaptureVideo(*capture_params);
  1294. }
  1295. }
  1296. void CaptureModeController::OnDlpRestrictionCheckedAtCountDownFinished(
  1297. bool proceed) {
  1298. pending_dlp_check_ = false;
  1299. if (!IsActive()) {
  1300. // This function gets called asynchronously, and until it gets called, the
  1301. // session could end due to e.g. locking the screen, suspending, or
  1302. // switching users.
  1303. return;
  1304. }
  1305. // We don't need to bring back capture mode UIs on 3-second count down
  1306. // finished, since the session is about to shutdown anyways for starting the
  1307. // video recording.
  1308. capture_mode_session_->OnWaitingForDlpConfirmationEnded(/*reshow_uis=*/false);
  1309. if (!proceed) {
  1310. Stop();
  1311. return;
  1312. }
  1313. const absl::optional<CaptureParams> capture_params = GetCaptureParams();
  1314. if (!capture_params) {
  1315. Stop();
  1316. return;
  1317. }
  1318. // Now that we're done with DLP restrictions checks, we can perform the policy
  1319. // and HDCP checks, which may have changed during the 3-second count down and
  1320. // during the time the DLP warning dialog was shown.
  1321. if (!delegate_->IsCaptureAllowedByPolicy()) {
  1322. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  1323. Stop();
  1324. return;
  1325. }
  1326. if (ShouldBlockRecordingForContentProtection(capture_params->window)) {
  1327. Stop();
  1328. ShowDisabledNotification(CaptureAllowance::kDisallowedByHdcp);
  1329. return;
  1330. }
  1331. // In Projector mode, the creation of the DriveFS folder that will host the
  1332. // video is asynchronous. We don't want the user to be able to bail out of the
  1333. // session at this point, since we don't want to create that folder in vain.
  1334. capture_mode_session_->set_can_exit_on_escape(false);
  1335. if (capture_mode_session_->is_in_projector_mode()) {
  1336. ProjectorControllerImpl::Get()->CreateScreencastContainerFolder(
  1337. base::BindOnce(
  1338. &CaptureModeController::OnProjectorContainerFolderCreated,
  1339. weak_ptr_factory_.GetWeakPtr(), *capture_params));
  1340. return;
  1341. }
  1342. const base::FilePath current_path = BuildVideoPath();
  1343. // If the current capture folder is not the default `Downloads` folder, we
  1344. // need to validate the current folder first before starting the video
  1345. // recording.
  1346. if (!GetCurrentCaptureFolder().is_default_downloads_folder) {
  1347. blocking_task_runner_->PostTaskAndReplyWithResult(
  1348. FROM_HERE,
  1349. base::BindOnce(&SelectFilePathForCapturedFile, current_path,
  1350. GetFallbackFilePathFromFile(current_path)),
  1351. base::BindOnce(&CaptureModeController::BeginVideoRecording,
  1352. weak_ptr_factory_.GetWeakPtr(), *capture_params,
  1353. /*for_projector=*/false));
  1354. return;
  1355. }
  1356. BeginVideoRecording(*capture_params, /*for_projector=*/false, current_path);
  1357. }
  1358. void CaptureModeController::OnDlpRestrictionCheckedAtSessionInit(
  1359. CaptureModeEntryType entry_type,
  1360. bool proceed) {
  1361. pending_dlp_check_ = false;
  1362. if (!proceed)
  1363. return;
  1364. DCHECK(!capture_mode_session_);
  1365. // Check policy again even though we checked in Start(), but due to the DLP
  1366. // warning dialog can be accepted after a long wait, maybe something changed
  1367. // in the middle.
  1368. if (!delegate_->IsCaptureAllowedByPolicy()) {
  1369. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  1370. return;
  1371. }
  1372. // Starting capture mode from the Projector app will put it in a special mode
  1373. // where only video recording is allowed, with audio recording enabled.
  1374. bool for_projector = false;
  1375. // Before we start the session, if video recording is in progress, we need to
  1376. // set the current type to image, as we can't have more than one recording at
  1377. // a time. The video toggle button in the capture mode bar will be disabled.
  1378. if (is_recording_in_progress()) {
  1379. SetType(CaptureModeType::kImage);
  1380. } else if (entry_type == CaptureModeEntryType::kProjector) {
  1381. DCHECK(features::IsProjectorEnabled());
  1382. for_projector = true;
  1383. // TODO(afakhry): Discuss with PM whether we want this to affect the audio
  1384. // settings of future generic capture mode sessions.
  1385. enable_audio_recording_ = true;
  1386. SetType(CaptureModeType::kVideo);
  1387. }
  1388. RecordCaptureModeEntryType(entry_type);
  1389. // Reset the user capture region if enough time has passed as it can be
  1390. // annoying to still have the old capture region from the previous session
  1391. // long time ago.
  1392. if (!user_capture_region_.IsEmpty() &&
  1393. base::TimeTicks::Now() - last_capture_region_update_time_ >
  1394. kResetCaptureRegionDuration) {
  1395. SetUserCaptureRegion(gfx::Rect(), /*by_user=*/false);
  1396. }
  1397. delegate_->OnSessionStateChanged(/*started=*/true);
  1398. capture_mode_session_ =
  1399. std::make_unique<CaptureModeSession>(this, for_projector);
  1400. capture_mode_session_->Initialize();
  1401. camera_controller_->OnCaptureSessionStarted();
  1402. }
  1403. void CaptureModeController::OnDlpRestrictionCheckedAtVideoEnd(
  1404. const gfx::ImageSkia& video_thumbnail,
  1405. bool success,
  1406. bool in_projector_mode,
  1407. bool proceed) {
  1408. const bool should_delete_file = !proceed;
  1409. const auto video_file_path = current_video_file_path_;
  1410. current_video_file_path_.clear();
  1411. if (should_delete_file) {
  1412. DeleteFileAsync(blocking_task_runner_, video_file_path,
  1413. std::move(on_file_deleted_callback_for_test_));
  1414. } else {
  1415. OnVideoFileSaved(video_file_path, video_thumbnail, success,
  1416. in_projector_mode);
  1417. }
  1418. if (features::IsProjectorEnabled()) {
  1419. ProjectorControllerImpl::Get()->OnDlpRestrictionCheckedAtVideoEnd(
  1420. in_projector_mode, should_delete_file, video_thumbnail);
  1421. }
  1422. low_disk_space_threshold_reached_ = false;
  1423. recording_start_time_ = base::TimeTicks();
  1424. }
  1425. void CaptureModeController::
  1426. OnDlpRestrictionCheckedAtCaptureScreenshotsOfAllDisplays(bool proceed) {
  1427. pending_dlp_check_ = false;
  1428. if (!proceed)
  1429. return;
  1430. // Due to fact that the DLP warning dialog may take a while, check policy
  1431. // again even though we checked in CaptureScreenshotsOfAllDisplays().
  1432. if (!delegate_->IsCaptureAllowedByPolicy()) {
  1433. ShowDisabledNotification(CaptureAllowance::kDisallowedByPolicy);
  1434. return;
  1435. }
  1436. // Get a vector of RootWindowControllers with primary root window at first.
  1437. const std::vector<RootWindowController*> controllers =
  1438. RootWindowController::root_window_controllers();
  1439. // Capture screenshot for each individual display.
  1440. int display_index = 1;
  1441. for (RootWindowController* controller : controllers) {
  1442. // TODO(shidi): Check with UX what notification should show if
  1443. // some (but not all) of the displays have restricted content and
  1444. // whether we should localize the display name.
  1445. const CaptureParams capture_params{controller->GetRootWindow(),
  1446. controller->GetRootWindow()->bounds()};
  1447. CaptureImage(capture_params, controllers.size() == 1
  1448. ? BuildImagePath()
  1449. : BuildImagePathForDisplay(display_index));
  1450. ++display_index;
  1451. }
  1452. // Since this doesn't create a capture mode session, log metrics here.
  1453. RecordCaptureModeEntryType(CaptureModeEntryType::kCaptureAllDisplays);
  1454. RecordCaptureModeConfiguration(
  1455. CaptureModeType::kImage, CaptureModeSource::kFullscreen,
  1456. /*audio_on=*/false, /*is_in_projector_mode=*/false);
  1457. }
  1458. CaptureModeSaveToLocation CaptureModeController::GetSaveToOption(
  1459. const base::FilePath& path) {
  1460. DCHECK(Shell::Get()->session_controller()->IsActiveUserSessionStarted());
  1461. const auto dir_path = path.DirName();
  1462. if (dir_path == delegate_->GetUserDefaultDownloadsFolder())
  1463. return CaptureModeSaveToLocation::kDefault;
  1464. base::FilePath mounted_path;
  1465. if (delegate_->GetDriveFsMountPointPath(&mounted_path)) {
  1466. const auto drive_root_path = mounted_path.Append("root");
  1467. if (dir_path == drive_root_path)
  1468. return CaptureModeSaveToLocation::kDrive;
  1469. if (drive_root_path.IsParent(dir_path))
  1470. return CaptureModeSaveToLocation::kDriveFolder;
  1471. }
  1472. return CaptureModeSaveToLocation::kCustomizedFolder;
  1473. }
  1474. } // namespace ash