665c38c1194746baa37d35487fe1d092a079ffe4
[WebKit-https.git] / Source / WebCore / platform / audio / PlatformMediaSessionManager.cpp
1 /*
2  * Copyright (C) 2013-2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PlatformMediaSessionManager.h"
28
29 #include "AudioSession.h"
30 #include "Document.h"
31 #include "Logging.h"
32 #include "PlatformMediaSession.h"
33
34 namespace WebCore {
35
36 #if ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
37
38 #if !PLATFORM(COCOA)
39 static PlatformMediaSessionManager* platformMediaSessionManager = nullptr;
40
41 PlatformMediaSessionManager& PlatformMediaSessionManager::sharedManager()
42 {
43     if (!platformMediaSessionManager)
44         platformMediaSessionManager = new PlatformMediaSessionManager;
45     return *platformMediaSessionManager;
46 }
47
48 PlatformMediaSessionManager* PlatformMediaSessionManager::sharedManagerIfExists()
49 {
50     return platformMediaSessionManager;
51 }
52 #endif // !PLATFORM(COCOA)
53
54 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
55 {
56     if (auto existingManager = PlatformMediaSessionManager::sharedManagerIfExists())
57         existingManager->scheduleUpdateNowPlayingInfo();
58 }
59
60 PlatformMediaSessionManager::PlatformMediaSessionManager()
61     : m_systemSleepListener(PAL::SystemSleepListener::create(*this))
62 #if !RELEASE_LOG_DISABLED
63     , m_logger(AggregateLogger::create(this))
64 #endif
65 {
66     resetRestrictions();
67 }
68
69 void PlatformMediaSessionManager::resetRestrictions()
70 {
71     m_restrictions[PlatformMediaSession::Video] = NoRestrictions;
72     m_restrictions[PlatformMediaSession::Audio] = NoRestrictions;
73     m_restrictions[PlatformMediaSession::VideoAudio] = NoRestrictions;
74     m_restrictions[PlatformMediaSession::WebAudio] = NoRestrictions;
75     m_restrictions[PlatformMediaSession::MediaStreamCapturingAudio] = NoRestrictions;
76 }
77
78 bool PlatformMediaSessionManager::has(PlatformMediaSession::MediaType type) const
79 {
80     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
81
82     return anyOfSessions([type] (PlatformMediaSession& session, size_t) {
83         return session.mediaType() == type;
84     });
85 }
86
87 bool PlatformMediaSessionManager::activeAudioSessionRequired() const
88 {
89     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
90         return session.activeAudioSessionRequired();
91     });
92 }
93
94 bool PlatformMediaSessionManager::canProduceAudio() const
95 {
96     return anyOfSessions([] (PlatformMediaSession& session, size_t) {
97         return session.canProduceAudio();
98     });
99 }
100
101 int PlatformMediaSessionManager::count(PlatformMediaSession::MediaType type) const
102 {
103     ASSERT(type >= PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
104
105     int count = 0;
106     for (auto* session : m_sessions) {
107         if (session->mediaType() == type)
108             ++count;
109     }
110
111     return count;
112 }
113
114 void PlatformMediaSessionManager::beginInterruption(PlatformMediaSession::InterruptionType type)
115 {
116     ALWAYS_LOG(LOGIDENTIFIER);
117
118     m_interrupted = true;
119     forEachSession([type] (PlatformMediaSession& session, size_t) {
120         session.beginInterruption(type);
121     });
122     updateSessionState();
123 }
124
125 void PlatformMediaSessionManager::endInterruption(PlatformMediaSession::EndInterruptionFlags flags)
126 {
127     ALWAYS_LOG(LOGIDENTIFIER);
128
129     m_interrupted = false;
130     forEachSession([flags] (PlatformMediaSession& session, size_t) {
131         session.endInterruption(flags);
132     });
133 }
134
135 void PlatformMediaSessionManager::addSession(PlatformMediaSession& session)
136 {
137     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
138     
139     m_sessions.append(&session);
140     if (m_interrupted)
141         session.setState(PlatformMediaSession::Interrupted);
142
143     if (!m_remoteCommandListener)
144         m_remoteCommandListener = RemoteCommandListener::create(*this);
145
146     if (!m_audioHardwareListener)
147         m_audioHardwareListener = AudioHardwareListener::create(*this);
148
149 #if !RELEASE_LOG_DISABLED
150     m_logger->addLogger(session.logger());
151 #endif
152
153     updateSessionState();
154 }
155
156 void PlatformMediaSessionManager::removeSession(PlatformMediaSession& session)
157 {
158     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
159     
160     size_t index = m_sessions.find(&session);
161     if (index == notFound)
162         return;
163
164     if (m_iteratingOverSessions)
165         m_sessions.at(index) = nullptr;
166     else
167         m_sessions.remove(index);
168
169     if (m_sessions.isEmpty() || std::all_of(m_sessions.begin(), m_sessions.end(), std::logical_not<void>())) {
170         m_remoteCommandListener = nullptr;
171         m_audioHardwareListener = nullptr;
172 #if USE(AUDIO_SESSION)
173         if (m_becameActive && shouldDeactivateAudioSession()) {
174             AudioSession::sharedSession().tryToSetActive(false);
175             m_becameActive = false;
176         }
177 #endif
178     }
179
180 #if !RELEASE_LOG_DISABLED
181     m_logger->removeLogger(session.logger());
182 #endif
183
184     updateSessionState();
185 }
186
187 void PlatformMediaSessionManager::addRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
188 {
189     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
190     m_restrictions[type] |= restriction;
191 }
192
193 void PlatformMediaSessionManager::removeRestriction(PlatformMediaSession::MediaType type, SessionRestrictions restriction)
194 {
195     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
196     m_restrictions[type] &= ~restriction;
197 }
198
199 PlatformMediaSessionManager::SessionRestrictions PlatformMediaSessionManager::restrictions(PlatformMediaSession::MediaType type)
200 {
201     ASSERT(type > PlatformMediaSession::None && type <= PlatformMediaSession::MediaStreamCapturingAudio);
202     return m_restrictions[type];
203 }
204
205 bool PlatformMediaSessionManager::sessionWillBeginPlayback(PlatformMediaSession& session)
206 {
207     setCurrentSession(session);
208
209     PlatformMediaSession::MediaType sessionType = session.mediaType();
210     SessionRestrictions restrictions = m_restrictions[sessionType];
211     if (session.state() == PlatformMediaSession::Interrupted && restrictions & InterruptedPlaybackNotPermitted) {
212         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false because session.state() is Interrupted, and InterruptedPlaybackNotPermitted");
213         return false;
214     }
215
216     if (m_processIsSuspended) {
217         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false because process is suspended");
218         return false;
219     }
220
221 #if USE(AUDIO_SESSION)
222     if (activeAudioSessionRequired()) {
223         if (!AudioSession::sharedSession().tryToSetActive(true)) {
224             ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning false failed to set active AudioSession");
225             return false;
226         }
227
228         ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " sucessfully activated AudioSession");
229         m_becameActive = true;
230     }
231 #endif
232
233     if (m_interrupted)
234         endInterruption(PlatformMediaSession::NoFlags);
235
236     forEachSession([&] (PlatformMediaSession& oneSession, size_t) {
237         if (&oneSession == &session)
238             return;
239         if (oneSession.mediaType() == sessionType
240             && restrictions & ConcurrentPlaybackNotPermitted
241             && oneSession.state() == PlatformMediaSession::Playing)
242             oneSession.pauseSession();
243     });
244
245     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier(), " returning true");
246     return true;
247 }
248     
249 void PlatformMediaSessionManager::sessionWillEndPlayback(PlatformMediaSession& session)
250 {
251     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
252     
253     if (m_sessions.size() < 2)
254         return;
255     
256     size_t pausingSessionIndex = notFound;
257     size_t lastPlayingSessionIndex = notFound;
258     anyOfSessions([&] (PlatformMediaSession& oneSession, size_t i) {
259         if (&oneSession == &session) {
260             pausingSessionIndex = i;
261             return false;
262         }
263         if (oneSession.state() == PlatformMediaSession::Playing) {
264             lastPlayingSessionIndex = i;
265             return false;
266         }
267         return oneSession.state() != PlatformMediaSession::Playing;
268     });
269     if (lastPlayingSessionIndex == notFound || pausingSessionIndex == notFound)
270         return;
271     
272     if (pausingSessionIndex > lastPlayingSessionIndex)
273         return;
274     
275     m_sessions.remove(pausingSessionIndex);
276     m_sessions.insert(lastPlayingSessionIndex, &session);
277     
278     ALWAYS_LOG(LOGIDENTIFIER, "session moved from index ", pausingSessionIndex, " to ", lastPlayingSessionIndex);
279 }
280
281 void PlatformMediaSessionManager::sessionStateChanged(PlatformMediaSession&)
282 {
283     updateSessionState();
284 }
285
286 void PlatformMediaSessionManager::setCurrentSession(PlatformMediaSession& session)
287 {
288     ALWAYS_LOG(LOGIDENTIFIER, session.logIdentifier());
289     
290     if (m_sessions.size() < 2)
291         return;
292     
293     size_t index = m_sessions.find(&session);
294     ASSERT(index != notFound);
295     if (!index || index == notFound)
296         return;
297
298     m_sessions.remove(index);
299     m_sessions.insert(0, &session);
300     if (m_remoteCommandListener)
301         m_remoteCommandListener->updateSupportedCommands();
302     
303     ALWAYS_LOG(LOGIDENTIFIER, "session moved from index ", index, " to 0");
304 }
305     
306 PlatformMediaSession* PlatformMediaSessionManager::currentSession() const
307 {
308     if (!m_sessions.size())
309         return nullptr;
310
311     return m_sessions[0];
312 }
313
314 Vector<PlatformMediaSession*> PlatformMediaSessionManager::currentSessionsMatching(const WTF::Function<bool(const PlatformMediaSession&)>& filter)
315 {
316     Vector<PlatformMediaSession*> matchingSessions;
317     forEachSession([&] (PlatformMediaSession& session, size_t) {
318         if (filter(session))
319             matchingSessions.append(&session);
320     });
321     return matchingSessions;
322 }
323
324 void PlatformMediaSessionManager::applicationWillBecomeInactive() const
325 {
326     ALWAYS_LOG(LOGIDENTIFIER);
327
328     forEachSession([&] (PlatformMediaSession& session, size_t) {
329         if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
330             session.beginInterruption(PlatformMediaSession::ProcessInactive);
331     });
332 }
333
334 void PlatformMediaSessionManager::applicationDidBecomeActive() const
335 {
336     ALWAYS_LOG(LOGIDENTIFIER);
337
338     forEachSession([&] (PlatformMediaSession& session, size_t) {
339         if (m_restrictions[session.mediaType()] & InactiveProcessPlaybackRestricted)
340             session.endInterruption(PlatformMediaSession::MayResumePlaying);
341     });
342 }
343
344 void PlatformMediaSessionManager::applicationDidEnterBackground(bool suspendedUnderLock) const
345 {
346     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
347
348     if (m_isApplicationInBackground)
349         return;
350
351     m_isApplicationInBackground = true;
352
353     forEachSession([&] (PlatformMediaSession& session, size_t) {
354         if (suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted)
355             session.beginInterruption(PlatformMediaSession::SuspendedUnderLock);
356         else if (m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
357             session.beginInterruption(PlatformMediaSession::EnteringBackground);
358     });
359 }
360
361 void PlatformMediaSessionManager::applicationWillEnterForeground(bool suspendedUnderLock) const
362 {
363     ALWAYS_LOG(LOGIDENTIFIER, "suspendedUnderLock: ", suspendedUnderLock);
364
365     if (!m_isApplicationInBackground)
366         return;
367
368     m_isApplicationInBackground = false;
369
370     forEachSession([&] (PlatformMediaSession& session, size_t) {
371         if ((suspendedUnderLock && m_restrictions[session.mediaType()] & SuspendedUnderLockPlaybackRestricted) || m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted)
372             session.endInterruption(PlatformMediaSession::MayResumePlaying);
373     });
374 }
375
376 void PlatformMediaSessionManager::processWillSuspend()
377 {
378     if (m_processIsSuspended)
379         return;
380     m_processIsSuspended = true;
381
382 #if USE(AUDIO_SESSION)
383     if (m_becameActive && shouldDeactivateAudioSession()) {
384         AudioSession::sharedSession().tryToSetActive(false);
385         ALWAYS_LOG(LOGIDENTIFIER, "tried to set inactive AudioSession");
386         m_becameActive = false;
387     }
388 #endif
389 }
390
391 void PlatformMediaSessionManager::processDidResume()
392 {
393     if (!m_processIsSuspended)
394         return;
395     m_processIsSuspended = false;
396
397 #if USE(AUDIO_SESSION)
398     if (!m_becameActive && activeAudioSessionRequired()) {
399         m_becameActive = AudioSession::sharedSession().tryToSetActive(true);
400         ALWAYS_LOG(LOGIDENTIFIER, "tried to set active AudioSession, ", m_becameActive ? "succeeded" : "failed");
401     }
402 #endif
403 }
404
405
406 void PlatformMediaSessionManager::sessionIsPlayingToWirelessPlaybackTargetChanged(PlatformMediaSession& session)
407 {
408     if (!m_isApplicationInBackground || !(m_restrictions[session.mediaType()] & BackgroundProcessPlaybackRestricted))
409         return;
410
411     if (session.state() != PlatformMediaSession::Interrupted)
412         session.beginInterruption(PlatformMediaSession::EnteringBackground);
413 }
414
415 void PlatformMediaSessionManager::sessionCanProduceAudioChanged(PlatformMediaSession&)
416 {
417     updateSessionState();
418 }
419
420 void PlatformMediaSessionManager::didReceiveRemoteControlCommand(PlatformMediaSession::RemoteControlCommandType command, const PlatformMediaSession::RemoteCommandArgument* argument)
421 {
422     PlatformMediaSession* activeSession = currentSession();
423     if (!activeSession || !activeSession->canReceiveRemoteControlCommands())
424         return;
425     activeSession->didReceiveRemoteControlCommand(command, argument);
426 }
427
428 bool PlatformMediaSessionManager::supportsSeeking() const
429 {
430     PlatformMediaSession* activeSession = currentSession();
431     if (!activeSession)
432         return false;
433     return activeSession->supportsSeeking();
434 }
435
436 void PlatformMediaSessionManager::systemWillSleep()
437 {
438     if (m_interrupted)
439         return;
440
441     forEachSession([] (PlatformMediaSession& session, size_t) {
442         session.beginInterruption(PlatformMediaSession::SystemSleep);
443     });
444 }
445
446 void PlatformMediaSessionManager::systemDidWake()
447 {
448     if (m_interrupted)
449         return;
450
451     forEachSession([] (PlatformMediaSession& session, size_t) {
452         session.endInterruption(PlatformMediaSession::MayResumePlaying);
453     });
454 }
455
456 void PlatformMediaSessionManager::audioOutputDeviceChanged()
457 {
458     updateSessionState();
459 }
460
461 void PlatformMediaSessionManager::stopAllMediaPlaybackForDocument(const Document* document)
462 {
463     forEachSession([document] (PlatformMediaSession& session, size_t) {
464         if (session.client().hostingDocument() == document)
465             session.pauseSession();
466     });
467 }
468
469 void PlatformMediaSessionManager::stopAllMediaPlaybackForProcess()
470 {
471     forEachSession([] (PlatformMediaSession& session, size_t) {
472         session.stopSession();
473     });
474 }
475
476 void PlatformMediaSessionManager::suspendAllMediaPlaybackForDocument(const Document& document)
477 {
478     forEachSession([&] (PlatformMediaSession& session, size_t) {
479         if (session.client().hostingDocument() == &document)
480             session.beginInterruption(PlatformMediaSession::PlaybackSuspended);
481     });
482 }
483
484 void PlatformMediaSessionManager::resumeAllMediaPlaybackForDocument(const Document& document)
485 {
486     forEachSession([&] (PlatformMediaSession& session, size_t) {
487         if (session.client().hostingDocument() == &document)
488             session.endInterruption(PlatformMediaSession::MayResumePlaying);
489     });
490 }
491
492 void PlatformMediaSessionManager::suspendAllMediaBufferingForDocument(const Document& document)
493 {
494     forEachSession([&] (PlatformMediaSession& session, size_t) {
495         if (session.client().hostingDocument() == &document)
496             session.suspendBuffering();
497     });
498 }
499
500 void PlatformMediaSessionManager::resumeAllMediaBufferingForDocument(const Document& document)
501 {
502     forEachSession([&] (PlatformMediaSession& session, size_t) {
503         if (session.client().hostingDocument() == &document)
504             session.resumeBuffering();
505     });
506 }
507
508 void PlatformMediaSessionManager::forEachSession(const Function<void(PlatformMediaSession&, size_t)>& predicate) const
509 {
510     ++m_iteratingOverSessions;
511
512     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
513         auto session = m_sessions[i];
514         if (!session)
515             continue;
516         predicate(*session, i);
517     }
518
519     --m_iteratingOverSessions;
520     if (!m_iteratingOverSessions)
521         m_sessions.removeAll(nullptr);
522 }
523
524 PlatformMediaSession* PlatformMediaSessionManager::findSession(const Function<bool(PlatformMediaSession&, size_t)>& predicate) const
525 {
526     ++m_iteratingOverSessions;
527
528     PlatformMediaSession* foundSession = nullptr;
529     for (size_t i = 0, size = m_sessions.size(); i < size; ++i) {
530         auto session = m_sessions[i];
531         if (!session)
532             continue;
533
534         if (!predicate(*session, i))
535             continue;
536
537         foundSession = session;
538         break;
539     }
540
541     --m_iteratingOverSessions;
542     if (!m_iteratingOverSessions)
543         m_sessions.removeAll(nullptr);
544
545     return foundSession;
546 }
547
548 static bool& deactivateAudioSession()
549 {
550     static bool deactivate;
551     return deactivate;
552 }
553
554 bool PlatformMediaSessionManager::shouldDeactivateAudioSession()
555 {
556     return deactivateAudioSession();
557 }
558
559 void PlatformMediaSessionManager::setShouldDeactivateAudioSession(bool deactivate)
560 {
561     deactivateAudioSession() = deactivate;
562 }
563
564 #else // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
565
566 void PlatformMediaSessionManager::updateNowPlayingInfoIfNecessary()
567 {
568
569 }
570
571 #endif // ENABLE(VIDEO) || ENABLE(WEB_AUDIO)
572
573 #if !RELEASE_LOG_DISABLED
574 WTFLogChannel& PlatformMediaSessionManager::logChannel() const
575 {
576     return LogMedia;
577 }
578 #endif
579
580 } // namespace WebCore