1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "ash/media/media_controller_impl.h"
6 
7 #include "ash/public/cpp/ash_features.h"
8 #include "ash/public/cpp/ash_pref_names.h"
9 #include "ash/public/cpp/media_client.h"
10 #include "ash/session/session_controller_impl.h"
11 #include "ash/shell.h"
12 #include "ash/shell_delegate.h"
13 #include "base/bind.h"
14 #include "base/feature_list.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "components/prefs/pref_registry_simple.h"
17 #include "components/prefs/pref_service.h"
18 #include "media/base/media_switches.h"
19 #include "services/media_session/public/mojom/constants.mojom.h"
20 #include "services/media_session/public/mojom/media_session.mojom.h"
21 #include "ui/base/accelerators/media_keys_util.h"
22 
23 namespace ash {
24 
25 namespace {
26 
27 constexpr base::TimeDelta kDefaultSeekTime =
28     base::TimeDelta::FromSeconds(media_session::mojom::kDefaultSeekTimeSeconds);
29 
IsMediaSessionActionEligibleForKeyControl(media_session::mojom::MediaSessionAction action)30 bool IsMediaSessionActionEligibleForKeyControl(
31     media_session::mojom::MediaSessionAction action) {
32   return action == media_session::mojom::MediaSessionAction::kPlay ||
33          action == media_session::mojom::MediaSessionAction::kPause ||
34          action == media_session::mojom::MediaSessionAction::kPreviousTrack ||
35          action == media_session::mojom::MediaSessionAction::kNextTrack;
36 }
37 
38 }  // namespace
39 
MediaControllerImpl()40 MediaControllerImpl::MediaControllerImpl() {
41   // If media session media key handling is enabled this will setup a connection
42   // and bind an observer to the media session service.
43   if (base::FeatureList::IsEnabled(media::kHardwareMediaKeyHandling))
44     GetMediaSessionController();
45 }
46 
47 MediaControllerImpl::~MediaControllerImpl() = default;
48 
49 // static
RegisterProfilePrefs(PrefRegistrySimple * registry)50 void MediaControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) {
51   registry->RegisterBooleanPref(prefs::kLockScreenMediaControlsEnabled, true);
52 }
53 
AreLockScreenMediaKeysEnabled() const54 bool MediaControllerImpl::AreLockScreenMediaKeysEnabled() const {
55   PrefService* prefs =
56       Shell::Get()->session_controller()->GetPrimaryUserPrefService();
57   DCHECK(prefs);
58 
59   return base::FeatureList::IsEnabled(features::kLockScreenMediaControls) &&
60          prefs->GetBoolean(prefs::kLockScreenMediaControlsEnabled) &&
61          !media_controls_dismissed_;
62 }
63 
SetMediaControlsDismissed(bool media_controls_dismissed)64 void MediaControllerImpl::SetMediaControlsDismissed(
65     bool media_controls_dismissed) {
66   media_controls_dismissed_ = media_controls_dismissed;
67 }
68 
AddObserver(MediaCaptureObserver * observer)69 void MediaControllerImpl::AddObserver(MediaCaptureObserver* observer) {
70   observers_.AddObserver(observer);
71 }
72 
RemoveObserver(MediaCaptureObserver * observer)73 void MediaControllerImpl::RemoveObserver(MediaCaptureObserver* observer) {
74   observers_.RemoveObserver(observer);
75 }
76 
SetClient(MediaClient * client)77 void MediaControllerImpl::SetClient(MediaClient* client) {
78   client_ = client;
79 
80   // When |client_| is changed or encounters an error we should reset the
81   // |force_media_client_key_handling_| bit.
82   ResetForceMediaClientKeyHandling();
83 }
84 
SetForceMediaClientKeyHandling(bool enabled)85 void MediaControllerImpl::SetForceMediaClientKeyHandling(bool enabled) {
86   force_media_client_key_handling_ = enabled;
87 }
88 
NotifyCaptureState(const base::flat_map<AccountId,MediaCaptureState> & capture_states)89 void MediaControllerImpl::NotifyCaptureState(
90     const base::flat_map<AccountId, MediaCaptureState>& capture_states) {
91   for (auto& observer : observers_)
92     observer.OnMediaCaptureChanged(capture_states);
93 }
94 
NotifyVmCaptureState(MediaCaptureState capture_state)95 void MediaControllerImpl::NotifyVmCaptureState(
96     MediaCaptureState capture_state) {
97   for (auto& observer : observers_)
98     observer.OnVmMediaCaptureChanged(capture_state);
99 }
100 
HandleMediaPlayPause()101 void MediaControllerImpl::HandleMediaPlayPause() {
102   if (Shell::Get()->session_controller()->IsScreenLocked() &&
103       !AreLockScreenMediaKeysEnabled()) {
104     return;
105   }
106 
107   // If the |client_| is force handling the keys then we should forward them.
108   if (client_ && force_media_client_key_handling_) {
109     ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlayPause);
110     client_->HandleMediaPlayPause();
111     return;
112   }
113 
114   // If media session media key handling is enabled. Toggle play pause using the
115   // media session service.
116   if (ShouldUseMediaSession()) {
117     switch (media_session_info_->playback_state) {
118       case media_session::mojom::MediaPlaybackState::kPaused:
119         GetMediaSessionController()->Resume();
120         ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlay);
121         break;
122       case media_session::mojom::MediaPlaybackState::kPlaying:
123         GetMediaSessionController()->Suspend();
124         ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPause);
125         break;
126     }
127 
128     return;
129   }
130 
131   // If media session does not handle the key then we don't know whether the
132   // action will play or pause so we should record a generic "play/pause".
133   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlayPause);
134 
135   if (client_)
136     client_->HandleMediaPlayPause();
137 }
138 
HandleMediaPlay()139 void MediaControllerImpl::HandleMediaPlay() {
140   if (Shell::Get()->session_controller()->IsScreenLocked() &&
141       !AreLockScreenMediaKeysEnabled()) {
142     return;
143   }
144 
145   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPlay);
146 
147   // If the |client_| is force handling the keys then we should forward them.
148   if (client_ && force_media_client_key_handling_) {
149     client_->HandleMediaPlay();
150     return;
151   }
152 
153   // If media session media key handling is enabled. Fire play using the media
154   // session service.
155   if (ShouldUseMediaSession()) {
156     GetMediaSessionController()->Resume();
157     return;
158   }
159 
160   if (client_)
161     client_->HandleMediaPlay();
162 }
163 
HandleMediaPause()164 void MediaControllerImpl::HandleMediaPause() {
165   if (Shell::Get()->session_controller()->IsScreenLocked() &&
166       !AreLockScreenMediaKeysEnabled()) {
167     return;
168   }
169 
170   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPause);
171 
172   // If the |client_| is force handling the keys then we should forward them.
173   if (client_ && force_media_client_key_handling_) {
174     client_->HandleMediaPause();
175     return;
176   }
177 
178   // If media session media key handling is enabled. Fire pause using the media
179   // session service.
180   if (ShouldUseMediaSession()) {
181     GetMediaSessionController()->Suspend();
182     return;
183   }
184 
185   if (client_)
186     client_->HandleMediaPause();
187 }
188 
HandleMediaStop()189 void MediaControllerImpl::HandleMediaStop() {
190   if (Shell::Get()->session_controller()->IsScreenLocked() &&
191       !AreLockScreenMediaKeysEnabled()) {
192     return;
193   }
194 
195   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kStop);
196 
197   // If the |client_| is force handling the keys then we should forward them.
198   if (client_ && force_media_client_key_handling_) {
199     client_->HandleMediaStop();
200     return;
201   }
202 
203   // If media session media key handling is enabled. Fire stop using the media
204   // session service.
205   if (ShouldUseMediaSession()) {
206     GetMediaSessionController()->Stop();
207     return;
208   }
209 
210   if (client_)
211     client_->HandleMediaStop();
212 }
213 
HandleMediaNextTrack()214 void MediaControllerImpl::HandleMediaNextTrack() {
215   if (Shell::Get()->session_controller()->IsScreenLocked() &&
216       !AreLockScreenMediaKeysEnabled()) {
217     return;
218   }
219 
220   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kNextTrack);
221 
222   // If the |client_| is force handling the keys then we should forward them.
223   if (client_ && force_media_client_key_handling_) {
224     client_->HandleMediaNextTrack();
225     return;
226   }
227 
228   // If media session media key handling is enabled. Fire next track using the
229   // media session service.
230   if (ShouldUseMediaSession()) {
231     GetMediaSessionController()->NextTrack();
232     return;
233   }
234 
235   if (client_)
236     client_->HandleMediaNextTrack();
237 }
238 
HandleMediaPrevTrack()239 void MediaControllerImpl::HandleMediaPrevTrack() {
240   if (Shell::Get()->session_controller()->IsScreenLocked() &&
241       !AreLockScreenMediaKeysEnabled()) {
242     return;
243   }
244 
245   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kPreviousTrack);
246 
247   // If the |client_| is force handling the keys then we should forward them.
248   if (client_ && force_media_client_key_handling_) {
249     client_->HandleMediaPrevTrack();
250     return;
251   }
252 
253   // If media session media key handling is enabled. Fire previous track using
254   // the media session service.
255   if (ShouldUseMediaSession()) {
256     GetMediaSessionController()->PreviousTrack();
257     return;
258   }
259 
260   if (client_)
261     client_->HandleMediaPrevTrack();
262 }
263 
HandleMediaSeekBackward()264 void MediaControllerImpl::HandleMediaSeekBackward() {
265   if (Shell::Get()->session_controller()->IsScreenLocked() &&
266       !AreLockScreenMediaKeysEnabled()) {
267     return;
268   }
269 
270   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kSeekBackward);
271 
272   // If the |client_| is force handling the keys then we should forward them.
273   if (client_ && force_media_client_key_handling_) {
274     client_->HandleMediaSeekBackward();
275     return;
276   }
277 
278   // If media session media key handling is enabled. Seek backward with
279   // kDefaultSeekTime using the media session service.
280   if (ShouldUseMediaSession()) {
281     GetMediaSessionController()->Seek(kDefaultSeekTime * -1);
282     return;
283   }
284 
285   if (client_)
286     client_->HandleMediaSeekBackward();
287 }
288 
HandleMediaSeekForward()289 void MediaControllerImpl::HandleMediaSeekForward() {
290   if (Shell::Get()->session_controller()->IsScreenLocked() &&
291       !AreLockScreenMediaKeysEnabled()) {
292     return;
293   }
294 
295   ui::RecordMediaHardwareKeyAction(ui::MediaHardwareKeyAction::kSeekForward);
296 
297   // If the |client_| is force handling the keys then we should forward them.
298   if (client_ && force_media_client_key_handling_) {
299     client_->HandleMediaSeekForward();
300     return;
301   }
302 
303   // If media session media key handling is enabled. Seek forward with
304   // kDefaultSeekTime using the media session service.
305   if (ShouldUseMediaSession()) {
306     GetMediaSessionController()->Seek(kDefaultSeekTime);
307     return;
308   }
309 
310   if (client_)
311     client_->HandleMediaSeekForward();
312 }
313 
RequestCaptureState()314 void MediaControllerImpl::RequestCaptureState() {
315   if (client_)
316     client_->RequestCaptureState();
317 }
318 
SuspendMediaSessions()319 void MediaControllerImpl::SuspendMediaSessions() {
320   if (client_)
321     client_->SuspendMediaSessions();
322 }
323 
MediaSessionInfoChanged(media_session::mojom::MediaSessionInfoPtr session_info)324 void MediaControllerImpl::MediaSessionInfoChanged(
325     media_session::mojom::MediaSessionInfoPtr session_info) {
326   media_session_info_ = std::move(session_info);
327 }
328 
MediaSessionActionsChanged(const std::vector<media_session::mojom::MediaSessionAction> & actions)329 void MediaControllerImpl::MediaSessionActionsChanged(
330     const std::vector<media_session::mojom::MediaSessionAction>& actions) {
331   supported_media_session_action_ = false;
332 
333   for (auto action : actions) {
334     if (IsMediaSessionActionEligibleForKeyControl(action)) {
335       supported_media_session_action_ = true;
336       return;
337     }
338   }
339 }
340 
SetMediaSessionControllerForTest(mojo::Remote<media_session::mojom::MediaController> controller)341 void MediaControllerImpl::SetMediaSessionControllerForTest(
342     mojo::Remote<media_session::mojom::MediaController> controller) {
343   media_session_controller_remote_ = std::move(controller);
344   BindMediaControllerObserver();
345 }
346 
FlushForTesting()347 void MediaControllerImpl::FlushForTesting() {
348   if (media_session_controller_remote_)
349     media_session_controller_remote_.FlushForTesting();
350 }
351 
352 media_session::mojom::MediaController*
GetMediaSessionController()353 MediaControllerImpl::GetMediaSessionController() {
354   // NOTE: |media_session_controller_remote_| may be overridden by tests and
355   // therefore non-null even if the real Media Session Service is unavailable.
356   if (media_session_controller_remote_)
357     return media_session_controller_remote_.get();
358 
359   // The service may be unavailable in some test environments.
360   if (!Shell::HasInstance())
361     return nullptr;
362 
363   media_session::MediaSessionService* service =
364       Shell::Get()->shell_delegate()->GetMediaSessionService();
365   if (!service)
366     return nullptr;
367 
368   mojo::Remote<media_session::mojom::MediaControllerManager>
369       controller_manager_remote;
370   service->BindMediaControllerManager(
371       controller_manager_remote.BindNewPipeAndPassReceiver());
372   controller_manager_remote->CreateActiveMediaController(
373       media_session_controller_remote_.BindNewPipeAndPassReceiver());
374 
375   media_session_controller_remote_.set_disconnect_handler(
376       base::BindOnce(&MediaControllerImpl::OnMediaSessionControllerError,
377                      base::Unretained(this)));
378 
379   BindMediaControllerObserver();
380 
381   return media_session_controller_remote_.get();
382 }
383 
OnMediaSessionControllerError()384 void MediaControllerImpl::OnMediaSessionControllerError() {
385   media_session_controller_remote_.reset();
386   supported_media_session_action_ = false;
387 }
388 
BindMediaControllerObserver()389 void MediaControllerImpl::BindMediaControllerObserver() {
390   if (!media_session_controller_remote_.is_bound())
391     return;
392 
393   media_session_controller_remote_->AddObserver(
394       media_controller_observer_receiver_.BindNewPipeAndPassRemote());
395 }
396 
ShouldUseMediaSession()397 bool MediaControllerImpl::ShouldUseMediaSession() {
398   return base::FeatureList::IsEnabled(media::kHardwareMediaKeyHandling) &&
399          GetMediaSessionController() && supported_media_session_action_ &&
400          !media_session_info_.is_null();
401 }
402 
ResetForceMediaClientKeyHandling()403 void MediaControllerImpl::ResetForceMediaClientKeyHandling() {
404   force_media_client_key_handling_ = false;
405 }
406 
407 }  // namespace ash
408