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)30bool 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()40MediaControllerImpl::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)50void MediaControllerImpl::RegisterProfilePrefs(PrefRegistrySimple* registry) { 51 registry->RegisterBooleanPref(prefs::kLockScreenMediaControlsEnabled, true); 52 } 53 AreLockScreenMediaKeysEnabled() const54bool 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)64void MediaControllerImpl::SetMediaControlsDismissed( 65 bool media_controls_dismissed) { 66 media_controls_dismissed_ = media_controls_dismissed; 67 } 68 AddObserver(MediaCaptureObserver * observer)69void MediaControllerImpl::AddObserver(MediaCaptureObserver* observer) { 70 observers_.AddObserver(observer); 71 } 72 RemoveObserver(MediaCaptureObserver * observer)73void MediaControllerImpl::RemoveObserver(MediaCaptureObserver* observer) { 74 observers_.RemoveObserver(observer); 75 } 76 SetClient(MediaClient * client)77void 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)85void MediaControllerImpl::SetForceMediaClientKeyHandling(bool enabled) { 86 force_media_client_key_handling_ = enabled; 87 } 88 NotifyCaptureState(const base::flat_map<AccountId,MediaCaptureState> & capture_states)89void 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)95void MediaControllerImpl::NotifyVmCaptureState( 96 MediaCaptureState capture_state) { 97 for (auto& observer : observers_) 98 observer.OnVmMediaCaptureChanged(capture_state); 99 } 100 HandleMediaPlayPause()101void 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()139void 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()164void 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()189void 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()214void 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()239void 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()264void 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()289void 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()314void MediaControllerImpl::RequestCaptureState() { 315 if (client_) 316 client_->RequestCaptureState(); 317 } 318 SuspendMediaSessions()319void MediaControllerImpl::SuspendMediaSessions() { 320 if (client_) 321 client_->SuspendMediaSessions(); 322 } 323 MediaSessionInfoChanged(media_session::mojom::MediaSessionInfoPtr session_info)324void 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)329void 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)341void MediaControllerImpl::SetMediaSessionControllerForTest( 342 mojo::Remote<media_session::mojom::MediaController> controller) { 343 media_session_controller_remote_ = std::move(controller); 344 BindMediaControllerObserver(); 345 } 346 FlushForTesting()347void MediaControllerImpl::FlushForTesting() { 348 if (media_session_controller_remote_) 349 media_session_controller_remote_.FlushForTesting(); 350 } 351 352 media_session::mojom::MediaController* GetMediaSessionController()353MediaControllerImpl::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()384void MediaControllerImpl::OnMediaSessionControllerError() { 385 media_session_controller_remote_.reset(); 386 supported_media_session_action_ = false; 387 } 388 BindMediaControllerObserver()389void 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()397bool MediaControllerImpl::ShouldUseMediaSession() { 398 return base::FeatureList::IsEnabled(media::kHardwareMediaKeyHandling) && 399 GetMediaSessionController() && supported_media_session_action_ && 400 !media_session_info_.is_null(); 401 } 402 ResetForceMediaClientKeyHandling()403void MediaControllerImpl::ResetForceMediaClientKeyHandling() { 404 force_media_client_key_handling_ = false; 405 } 406 407 } // namespace ash 408