Fix emulator build error
[platform/framework/web/chromium-efl.git] / services / media_session / audio_focus_manager.cc
1 // Copyright 2018 The Chromium Authors
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 "services/media_session/audio_focus_manager.h"
6
7 #include <iterator>
8 #include <utility>
9
10 #include "base/containers/adapters.h"
11 #include "base/containers/cxx20_erase.h"
12 #include "base/functional/bind.h"
13 #include "base/power_monitor/power_monitor.h"
14 #include "base/power_monitor/power_observer.h"
15 #include "base/unguessable_token.h"
16 #include "mojo/public/cpp/bindings/remote.h"
17 #include "services/media_session/audio_focus_request.h"
18 #include "services/media_session/public/cpp/features.h"
19 #include "services/media_session/public/mojom/audio_focus.mojom.h"
20
21 namespace media_session {
22
23 namespace {
24
25 mojom::EnforcementMode GetDefaultEnforcementMode() {
26   if (base::FeatureList::IsEnabled(features::kAudioFocusEnforcement)) {
27     if (base::FeatureList::IsEnabled(features::kAudioFocusSessionGrouping))
28       return mojom::EnforcementMode::kSingleGroup;
29     return mojom::EnforcementMode::kSingleSession;
30   }
31
32   return mojom::EnforcementMode::kNone;
33 }
34
35 }  // namespace
36
37 // MediaPowerDelegate will pause all playback if the device is suspended.
38 class MediaPowerDelegate : public base::PowerSuspendObserver {
39  public:
40   explicit MediaPowerDelegate(base::WeakPtr<AudioFocusManager> owner)
41       : owner_(owner) {
42     base::PowerMonitor::AddPowerSuspendObserver(this);
43   }
44
45   MediaPowerDelegate(const MediaPowerDelegate&) = delete;
46   MediaPowerDelegate& operator=(const MediaPowerDelegate&) = delete;
47
48   ~MediaPowerDelegate() override {
49     base::PowerMonitor::RemovePowerSuspendObserver(this);
50   }
51
52   // base::PowerSuspendObserver:
53   void OnSuspend() override {
54     DCHECK(owner_);
55     owner_->SuspendAllSessions();
56   }
57
58  private:
59   const base::WeakPtr<AudioFocusManager> owner_;
60 };
61
62 class AudioFocusManager::SourceObserverHolder {
63  public:
64   SourceObserverHolder(AudioFocusManager* owner,
65                        const base::UnguessableToken& source_id,
66                        mojo::PendingRemote<mojom::AudioFocusObserver> observer)
67       : identity_(source_id), observer_(std::move(observer)) {
68     // Set a connection error handler so that we will remove observers that have
69     // had an error / been closed.
70     observer_.set_disconnect_handler(base::BindOnce(
71         &AudioFocusManager::CleanupSourceObservers, base::Unretained(owner)));
72   }
73
74   SourceObserverHolder(const SourceObserverHolder&) = delete;
75   SourceObserverHolder& operator=(const SourceObserverHolder&) = delete;
76
77   ~SourceObserverHolder() = default;
78
79   bool is_valid() const { return observer_.is_connected(); }
80
81   const base::UnguessableToken& identity() const { return identity_; }
82
83   void OnFocusGained(mojom::AudioFocusRequestStatePtr session) {
84     observer_->OnFocusGained(std::move(session));
85   }
86
87   void OnFocusLost(mojom::AudioFocusRequestStatePtr session) {
88     observer_->OnFocusLost(std::move(session));
89   }
90
91   void OnRequestIdReleased(const base::UnguessableToken& request_id) {
92     observer_->OnRequestIdReleased(request_id);
93   }
94
95  private:
96   const base::UnguessableToken identity_;
97   mojo::Remote<mojom::AudioFocusObserver> observer_;
98 };
99
100 void AudioFocusManager::RequestAudioFocus(
101     mojo::PendingReceiver<mojom::AudioFocusRequestClient> receiver,
102     mojo::PendingRemote<mojom::MediaSession> session,
103     mojom::MediaSessionInfoPtr session_info,
104     mojom::AudioFocusType type,
105     RequestAudioFocusCallback callback) {
106   auto request_id = base::UnguessableToken::Create();
107
108   RequestAudioFocusInternal(
109       std::make_unique<AudioFocusRequest>(
110           weak_ptr_factory_.GetWeakPtr(), std::move(receiver),
111           std::move(session), std::move(session_info), type, request_id,
112           GetBindingSourceName(), base::UnguessableToken::Create(),
113           GetBindingIdentity()),
114       type);
115
116   std::move(callback).Run(request_id);
117 }
118
119 void AudioFocusManager::RequestGroupedAudioFocus(
120     const base::UnguessableToken& request_id,
121     mojo::PendingReceiver<mojom::AudioFocusRequestClient> receiver,
122     mojo::PendingRemote<mojom::MediaSession> session,
123     mojom::MediaSessionInfoPtr session_info,
124     mojom::AudioFocusType type,
125     const base::UnguessableToken& group_id,
126     RequestGroupedAudioFocusCallback callback) {
127   if (IsFocusEntryPresent(request_id)) {
128     std::move(callback).Run(false /* success */);
129     return;
130   }
131
132   RequestAudioFocusInternal(
133       std::make_unique<AudioFocusRequest>(
134           weak_ptr_factory_.GetWeakPtr(), std::move(receiver),
135           std::move(session), std::move(session_info), type, request_id,
136           GetBindingSourceName(), group_id, GetBindingIdentity()),
137       type);
138
139   std::move(callback).Run(true /* success */);
140 }
141
142 void AudioFocusManager::GetFocusRequests(GetFocusRequestsCallback callback) {
143   std::vector<mojom::AudioFocusRequestStatePtr> requests;
144
145   for (const auto& row : audio_focus_stack_)
146     requests.push_back(row->ToAudioFocusRequestState());
147
148   std::move(callback).Run(std::move(requests));
149 }
150
151 void AudioFocusManager::GetDebugInfoForRequest(
152     const RequestId& request_id,
153     GetDebugInfoForRequestCallback callback) {
154   for (auto& row : audio_focus_stack_) {
155     if (row->id() != request_id)
156       continue;
157
158     row->ipc()->GetDebugInfo(base::BindOnce(
159         [](const base::UnguessableToken& group_id,
160            const base::UnguessableToken& identity,
161            GetDebugInfoForRequestCallback callback,
162            mojom::MediaSessionDebugInfoPtr info) {
163           // Inject the |group_id| into the state string. This is because in
164           // some cases the group id is automatically generated by the media
165           // session service so the session is unaware of it.
166           if (!info->state.empty())
167             info->state += " ";
168           info->state += "GroupId=" + group_id.ToString();
169
170           // Inject the identity into the state string.
171           info->state += " Identity=" + identity.ToString();
172
173           std::move(callback).Run(std::move(info));
174         },
175         row->group_id(), row->identity(), std::move(callback)));
176     return;
177   }
178
179   std::move(callback).Run(mojom::MediaSessionDebugInfo::New());
180 }
181
182 void AudioFocusManager::AbandonAudioFocusInternal(RequestId id) {
183   if (audio_focus_stack_.empty())
184     return;
185
186   bool was_top_most_session = audio_focus_stack_.back()->id() == id;
187
188   auto row = RemoveFocusEntryIfPresent(id);
189   if (!row)
190     return;
191
192   EnforceAudioFocus();
193   MaybeUpdateActiveSession();
194
195   // Notify observers that we lost audio focus.
196   mojom::AudioFocusRequestStatePtr session_state =
197       row->ToAudioFocusRequestState();
198
199   for (const auto& observer : observers_)
200     observer->OnFocusLost(session_state.Clone());
201
202   for (auto& holder : source_observers_) {
203     if (holder->identity() == row->identity())
204       holder->OnFocusLost(session_state.Clone());
205   }
206
207   if (!was_top_most_session || audio_focus_stack_.empty())
208     return;
209
210   // Notify observers that the session on top gained focus.
211   AudioFocusRequest* new_session = audio_focus_stack_.back().get();
212
213   for (const auto& observer : observers_)
214     observer->OnFocusGained(new_session->ToAudioFocusRequestState());
215
216   for (auto& holder : source_observers_) {
217     if (holder->identity() == new_session->identity())
218       holder->OnFocusGained(new_session->ToAudioFocusRequestState());
219   }
220 }
221
222 void AudioFocusManager::AddObserver(
223     mojo::PendingRemote<mojom::AudioFocusObserver> observer) {
224   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
225   observers_.Add(std::move(observer));
226 }
227
228 void AudioFocusManager::SetSource(const base::UnguessableToken& identity,
229                                   const std::string& name) {
230   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
231
232   auto& context = receivers_.current_context();
233   context->identity = identity;
234   context->source_name = name;
235 }
236
237 void AudioFocusManager::SetEnforcementMode(mojom::EnforcementMode mode) {
238   if (mode == mojom::EnforcementMode::kDefault)
239     mode = GetDefaultEnforcementMode();
240
241   if (mode == enforcement_mode_)
242     return;
243
244   enforcement_mode_ = mode;
245
246   if (audio_focus_stack_.empty())
247     return;
248
249   EnforceAudioFocus();
250 }
251
252 void AudioFocusManager::AddSourceObserver(
253     const base::UnguessableToken& source_id,
254     mojo::PendingRemote<mojom::AudioFocusObserver> observer) {
255   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
256   source_observers_.push_back(std::make_unique<SourceObserverHolder>(
257       this, source_id, std::move(observer)));
258 }
259
260 void AudioFocusManager::GetSourceFocusRequests(
261     const base::UnguessableToken& source_id,
262     GetFocusRequestsCallback callback) {
263   std::vector<mojom::AudioFocusRequestStatePtr> requests;
264
265   for (const auto& row : audio_focus_stack_) {
266     if (row->identity() == source_id)
267       requests.push_back(row->ToAudioFocusRequestState());
268   }
269
270   std::move(callback).Run(std::move(requests));
271 }
272
273 void AudioFocusManager::RequestIdReleased(
274     const base::UnguessableToken& request_id) {
275   for (const auto& observer : observers_)
276     observer->OnRequestIdReleased(request_id);
277
278   const base::UnguessableToken& source_id = GetBindingIdentity();
279   for (auto& holder : source_observers_) {
280     if (holder->identity() == source_id)
281       holder->OnRequestIdReleased(request_id);
282   }
283 }
284
285 void AudioFocusManager::CreateActiveMediaController(
286     mojo::PendingReceiver<mojom::MediaController> receiver) {
287   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
288   active_media_controller_.BindToInterface(std::move(receiver));
289 }
290
291 void AudioFocusManager::CreateMediaControllerForSession(
292     mojo::PendingReceiver<mojom::MediaController> receiver,
293     const base::UnguessableToken& receiver_id) {
294   for (auto& row : audio_focus_stack_) {
295     if (row->id() != receiver_id)
296       continue;
297
298     row->BindToMediaController(std::move(receiver));
299     break;
300   }
301 }
302
303 void AudioFocusManager::SuspendAllSessions() {
304   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
305
306   for (auto& row : audio_focus_stack_)
307     row->ipc()->Suspend(mojom::MediaSession::SuspendType::kUI);
308 }
309
310 void AudioFocusManager::BindToInterface(
311     mojo::PendingReceiver<mojom::AudioFocusManager> receiver) {
312   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
313   receivers_.Add(this, std::move(receiver),
314                  std::make_unique<ReceiverContext>());
315 }
316
317 void AudioFocusManager::BindToDebugInterface(
318     mojo::PendingReceiver<mojom::AudioFocusManagerDebug> receiver) {
319   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
320   debug_receivers_.Add(this, std::move(receiver));
321 }
322
323 void AudioFocusManager::BindToControllerManagerInterface(
324     mojo::PendingReceiver<mojom::MediaControllerManager> receiver) {
325   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
326   controller_receivers_.Add(this, std::move(receiver));
327 }
328
329 void AudioFocusManager::RequestAudioFocusInternal(
330     std::unique_ptr<AudioFocusRequest> row,
331     mojom::AudioFocusType type) {
332   const auto& identity = row->identity();
333   row->set_audio_focus_type(type);
334   audio_focus_stack_.push_back(std::move(row));
335
336   EnforceAudioFocus();
337   MaybeUpdateActiveSession();
338
339   // Notify observers that we were gained audio focus.
340   mojom::AudioFocusRequestStatePtr session_state =
341       audio_focus_stack_.back()->ToAudioFocusRequestState();
342   for (const auto& observer : observers_)
343     observer->OnFocusGained(session_state.Clone());
344
345   for (auto& holder : source_observers_) {
346     if (holder->identity() == identity)
347       holder->OnFocusGained(session_state.Clone());
348   }
349 }
350
351 void AudioFocusManager::EnforceAudioFocus() {
352   DCHECK_NE(mojom::EnforcementMode::kDefault, enforcement_mode_);
353   if (audio_focus_stack_.empty())
354     return;
355
356   EnforcementState state;
357
358   for (auto& session : base::Reversed(audio_focus_stack_)) {
359     EnforceSingleSession(session.get(), state);
360
361     // Update the flags based on the audio focus type of this session. If the
362     // session is suspended then any transient audio focus type should not have
363     // an effect.
364     switch (session->audio_focus_type()) {
365       case mojom::AudioFocusType::kGain:
366         state.should_stop = true;
367         break;
368       case mojom::AudioFocusType::kGainTransient:
369         if (!session->IsSuspended())
370           state.should_suspend = true;
371         break;
372       case mojom::AudioFocusType::kGainTransientMayDuck:
373         if (!session->IsSuspended())
374           state.should_duck = true;
375         break;
376       case mojom::AudioFocusType::kAmbient:
377         break;
378     }
379   }
380 }
381
382 void AudioFocusManager::MaybeUpdateActiveSession() {
383   AudioFocusRequest* active = nullptr;
384
385   for (auto& row : base::Reversed(audio_focus_stack_)) {
386     if (!row->info()->is_controllable)
387       continue;
388
389     active = row.get();
390     break;
391   }
392
393   if (active) {
394     active_media_controller_.SetMediaSession(active);
395   } else {
396     active_media_controller_.ClearMediaSession();
397   }
398 }
399
400 AudioFocusManager::AudioFocusManager()
401     : enforcement_mode_(GetDefaultEnforcementMode()) {
402   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
403
404   power_delegate_ =
405       std::make_unique<MediaPowerDelegate>(weak_ptr_factory_.GetWeakPtr());
406 }
407
408 AudioFocusManager::~AudioFocusManager() = default;
409
410 std::unique_ptr<AudioFocusRequest> AudioFocusManager::RemoveFocusEntryIfPresent(
411     RequestId id) {
412   std::unique_ptr<AudioFocusRequest> row;
413
414   for (auto iter = audio_focus_stack_.begin(); iter != audio_focus_stack_.end();
415        ++iter) {
416     if ((*iter)->id() == id) {
417       row.swap((*iter));
418       audio_focus_stack_.erase(iter);
419       break;
420     }
421   }
422
423   return row;
424 }
425
426 bool AudioFocusManager::IsFocusEntryPresent(
427     const base::UnguessableToken& id) const {
428   for (auto& row : audio_focus_stack_) {
429     if (row->id() == id)
430       return true;
431   }
432
433   return false;
434 }
435
436 const std::string& AudioFocusManager::GetBindingSourceName() const {
437   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
438   return receivers_.current_context()->source_name;
439 }
440
441 const base::UnguessableToken& AudioFocusManager::GetBindingIdentity() const {
442   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
443   return receivers_.current_context()->identity;
444 }
445
446 bool AudioFocusManager::IsSessionOnTopOfAudioFocusStack(
447     RequestId id,
448     mojom::AudioFocusType type) const {
449   return !audio_focus_stack_.empty() && audio_focus_stack_.back()->id() == id &&
450          audio_focus_stack_.back()->audio_focus_type() == type;
451 }
452
453 bool AudioFocusManager::ShouldSessionBeSuspended(
454     const AudioFocusRequest* session,
455     const EnforcementState& state) const {
456   bool should_suspend_any = state.should_stop || state.should_suspend;
457
458   switch (enforcement_mode_) {
459     case mojom::EnforcementMode::kSingleSession:
460       return should_suspend_any;
461     case mojom::EnforcementMode::kSingleGroup:
462       return should_suspend_any &&
463              session->group_id() != audio_focus_stack_.back()->group_id();
464     case mojom::EnforcementMode::kNone:
465       return false;
466     case mojom::EnforcementMode::kDefault:
467       NOTIMPLEMENTED();
468       return false;
469   }
470 }
471
472 bool AudioFocusManager::ShouldSessionBeDucked(
473     const AudioFocusRequest* session,
474     const EnforcementState& state) const {
475   switch (enforcement_mode_) {
476     case mojom::EnforcementMode::kSingleSession:
477     case mojom::EnforcementMode::kSingleGroup:
478       if (session->info()->force_duck)
479         return state.should_duck || ShouldSessionBeSuspended(session, state);
480       return state.should_duck;
481     case mojom::EnforcementMode::kNone:
482       return false;
483     case mojom::EnforcementMode::kDefault:
484       NOTIMPLEMENTED();
485       return false;
486   }
487 }
488
489 void AudioFocusManager::EnforceSingleSession(AudioFocusRequest* session,
490                                              const EnforcementState& state) {
491   if (ShouldSessionBeDucked(session, state)) {
492     session->ipc()->StartDucking();
493   } else {
494     session->ipc()->StopDucking();
495   }
496
497   // If the session wants to be ducked instead of suspended we should stop now.
498   if (session->info()->force_duck)
499     return;
500
501   if (ShouldSessionBeSuspended(session, state)) {
502     session->Suspend(state);
503   } else {
504     session->ReleaseTransientHold();
505   }
506 }
507
508 void AudioFocusManager::CleanupSourceObservers() {
509   base::EraseIf(source_observers_,
510                 [](const auto& holder) { return !holder->is_valid(); });
511 }
512
513 }  // namespace media_session