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 "components/arc/audio/arc_audio_bridge.h"
6 
7 #include <utility>
8 
9 #include "ash/public/cpp/system_tray.h"
10 #include "base/logging.h"
11 #include "base/memory/singleton.h"
12 #include "chromeos/audio/audio_device.h"
13 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
14 #include "components/arc/session/arc_bridge_service.h"
15 
16 namespace arc {
17 namespace {
18 
19 // Singleton factory for ArcAccessibilityHelperBridge.
20 class ArcAudioBridgeFactory
21     : public internal::ArcBrowserContextKeyedServiceFactoryBase<
22           ArcAudioBridge,
23           ArcAudioBridgeFactory> {
24  public:
25   // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
26   static constexpr const char* kName = "ArcAudioBridgeFactory";
27 
GetInstance()28   static ArcAudioBridgeFactory* GetInstance() {
29     return base::Singleton<ArcAudioBridgeFactory>::get();
30   }
31 
32  private:
33   friend base::DefaultSingletonTraits<ArcAudioBridgeFactory>;
34   ArcAudioBridgeFactory() = default;
35   ~ArcAudioBridgeFactory() override = default;
36 };
37 
38 }  // namespace
39 
40 // static
GetForBrowserContext(content::BrowserContext * context)41 ArcAudioBridge* ArcAudioBridge::GetForBrowserContext(
42     content::BrowserContext* context) {
43   return ArcAudioBridgeFactory::GetForBrowserContext(context);
44 }
45 
ArcAudioBridge(content::BrowserContext * context,ArcBridgeService * bridge_service)46 ArcAudioBridge::ArcAudioBridge(content::BrowserContext* context,
47                                ArcBridgeService* bridge_service)
48     : arc_bridge_service_(bridge_service),
49       cras_audio_handler_(chromeos::CrasAudioHandler::Get()) {
50   arc_bridge_service_->audio()->SetHost(this);
51   arc_bridge_service_->audio()->AddObserver(this);
52   cras_audio_handler_->AddAudioObserver(this);
53 }
54 
~ArcAudioBridge()55 ArcAudioBridge::~ArcAudioBridge() {
56   cras_audio_handler_->RemoveAudioObserver(this);
57   arc_bridge_service_->audio()->RemoveObserver(this);
58   arc_bridge_service_->audio()->SetHost(nullptr);
59 }
60 
OnConnectionReady()61 void ArcAudioBridge::OnConnectionReady() {
62   // TODO(hidehiko): Replace with ConnectionHolder::IsConnected().
63   available_ = true;
64 }
65 
OnConnectionClosed()66 void ArcAudioBridge::OnConnectionClosed() {
67   available_ = false;
68 }
69 
ShowVolumeControls()70 void ArcAudioBridge::ShowVolumeControls() {
71   DVLOG(2) << "ArcAudioBridge::ShowVolumeControls";
72   ash::SystemTray::Get()->ShowVolumeSliderBubble();
73 }
74 
OnSystemVolumeUpdateRequest(int32_t percent)75 void ArcAudioBridge::OnSystemVolumeUpdateRequest(int32_t percent) {
76   if (percent < 0 || percent > 100)
77     return;
78   cras_audio_handler_->SetOutputVolumePercent(percent);
79   bool is_muted =
80       percent <= cras_audio_handler_->GetOutputDefaultVolumeMuteThreshold();
81   if (cras_audio_handler_->IsOutputMuted() != is_muted)
82     cras_audio_handler_->SetOutputMute(is_muted);
83 }
84 
OnAudioNodesChanged()85 void ArcAudioBridge::OnAudioNodesChanged() {
86   uint64_t output_id = cras_audio_handler_->GetPrimaryActiveOutputNode();
87   const chromeos::AudioDevice* output_device =
88       cras_audio_handler_->GetDeviceFromId(output_id);
89   bool headphone_inserted =
90       (output_device &&
91        (output_device->type ==
92         chromeos::AudioDeviceType::AUDIO_TYPE_HEADPHONE ||
93         output_device->type == chromeos::AudioDeviceType::AUDIO_TYPE_USB ||
94         output_device->type == chromeos::AudioDeviceType::AUDIO_TYPE_LINEOUT));
95 
96   uint64_t input_id = cras_audio_handler_->GetPrimaryActiveInputNode();
97   const chromeos::AudioDevice* input_device =
98       cras_audio_handler_->GetDeviceFromId(input_id);
99   bool microphone_inserted =
100       (input_device &&
101        (input_device->type == chromeos::AudioDeviceType::AUDIO_TYPE_MIC ||
102         input_device->type == chromeos::AudioDeviceType::AUDIO_TYPE_USB));
103 
104   DVLOG(1) << "HEADPHONE " << headphone_inserted << " MICROPHONE "
105            << microphone_inserted;
106   SendSwitchState(headphone_inserted, microphone_inserted);
107 }
108 
OnOutputNodeVolumeChanged(uint64_t node_id,int volume)109 void ArcAudioBridge::OnOutputNodeVolumeChanged(uint64_t node_id, int volume) {
110   DVLOG(1) << "Output node " << node_id << " volume " << volume;
111   volume_ = volume;
112   SendVolumeState();
113 }
114 
OnOutputMuteChanged(bool mute_on)115 void ArcAudioBridge::OnOutputMuteChanged(bool mute_on) {
116   DVLOG(1) << "Output mute " << mute_on;
117   muted_ = mute_on;
118   SendVolumeState();
119 }
120 
SendSwitchState(bool headphone_inserted,bool microphone_inserted)121 void ArcAudioBridge::SendSwitchState(bool headphone_inserted,
122                                      bool microphone_inserted) {
123   uint32_t switch_state = 0;
124   if (headphone_inserted) {
125     switch_state |=
126         (1 << static_cast<uint32_t>(mojom::AudioSwitch::SW_HEADPHONE_INSERT));
127   }
128   if (microphone_inserted) {
129     switch_state |=
130         (1 << static_cast<uint32_t>(mojom::AudioSwitch::SW_MICROPHONE_INSERT));
131   }
132 
133   DVLOG(1) << "Send switch state " << switch_state;
134   if (!available_)
135     return;
136   mojom::AudioInstance* audio_instance = ARC_GET_INSTANCE_FOR_METHOD(
137       arc_bridge_service_->audio(), NotifySwitchState);
138   if (audio_instance)
139     audio_instance->NotifySwitchState(switch_state);
140 }
141 
SendVolumeState()142 void ArcAudioBridge::SendVolumeState() {
143   DVLOG(1) << "Send volume " << volume_ << " muted " << muted_;
144   if (!available_)
145     return;
146   mojom::AudioInstance* audio_instance = ARC_GET_INSTANCE_FOR_METHOD(
147       arc_bridge_service_->audio(), NotifyVolumeState);
148   if (audio_instance)
149     audio_instance->NotifyVolumeState(volume_, muted_);
150 }
151 
152 }  // namespace arc
153