summaryrefslogtreecommitdiffstats
path: root/chromeos/audio/cras_audio_handler.h
blob: 3d325daadf04eb775ba0bee51328a6010bb57b71 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_AUDIO_CRAS_AUDIO_HANDLER_H_
#define CHROMEOS_AUDIO_CRAS_AUDIO_HANDLER_H_

#include <stddef.h>
#include <stdint.h>
#include <queue>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/timer/timer.h"
#include "chromeos/audio/audio_device.h"
#include "chromeos/audio/audio_devices_pref_handler.h"
#include "chromeos/audio/audio_pref_observer.h"
#include "chromeos/dbus/audio_node.h"
#include "chromeos/dbus/cras_audio_client.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/dbus/volume_state.h"

class PrefRegistrySimple;
class PrefService;

namespace chromeos {

class AudioDevicesPrefHandler;

class CHROMEOS_EXPORT CrasAudioHandler : public CrasAudioClient::Observer,
                                         public AudioPrefObserver,
                                         public SessionManagerClient::Observer {
 public:
  typedef std::priority_queue<AudioDevice,
                              std::vector<AudioDevice>,
                              AudioDeviceCompare> AudioDevicePriorityQueue;
  typedef std::vector<uint64_t> NodeIdList;

  class AudioObserver {
   public:
    // Called when an active output volume changed.
    virtual void OnOutputNodeVolumeChanged(uint64_t node_id, int volume);

    // Called when output mute state changed.
    // |mute_on|: True if output is muted.
    // |system_adjust|: True if the mute state is adjusted by the system
    // automatically(i.e. not by user). UI should reflect the system's mute
    // state, but it should not be too loud, e.g., the volume pop up window
    // should not be triggered.
    virtual void OnOutputMuteChanged(bool mute_on, bool system_adjust);

    // Called when active input node's gain changed.
    virtual void OnInputNodeGainChanged(uint64_t node_id, int gain);

    // Called when input mute state changed.
    virtual void OnInputMuteChanged(bool mute_on);

    // Called when audio nodes changed.
    virtual void OnAudioNodesChanged();

    // Called when active audio node changed.
    virtual void OnActiveOutputNodeChanged();

    // Called when active audio input node changed.
    virtual void OnActiveInputNodeChanged();

   protected:
    AudioObserver();
    virtual ~AudioObserver();
    DISALLOW_COPY_AND_ASSIGN(AudioObserver);
  };

  enum DeviceActivateType {
    ACTIVATE_BY_PRIORITY = 0,
    ACTIVATE_BY_USER,
    ACTIVATE_BY_RESTORE_PREVIOUS_STATE,
  };

  // Sets the global instance. Must be called before any calls to Get().
  static void Initialize(
      scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler);

  // Sets the global instance for testing.
  static void InitializeForTesting();

  // Destroys the global instance.
  static void Shutdown();

  // Returns true if the global instance is initialized.
  static bool IsInitialized();

  // Gets the global instance. Initialize must be called first.
  static CrasAudioHandler* Get();

  // Adds an audio observer.
  virtual void AddAudioObserver(AudioObserver* observer);

  // Removes an audio observer.
  virtual void RemoveAudioObserver(AudioObserver* observer);

  // Returns true if keyboard mic exists.
  virtual bool HasKeyboardMic();

  // Returns true if audio output is muted for the system.
  virtual bool IsOutputMuted();

  // Returns true if audio output is muted for a device.
  virtual bool IsOutputMutedForDevice(uint64_t device_id);

  // Returns true if audio input is muted.
  virtual bool IsInputMuted();

  // Returns true if audio input is muted for a device.
  virtual bool IsInputMutedForDevice(uint64_t device_id);

  // Returns true if the output volume is below the default mute volume level.
  virtual bool IsOutputVolumeBelowDefaultMuteLevel();

  // Returns volume level in 0-100% range at which the volume should be muted.
  virtual int GetOutputDefaultVolumeMuteThreshold();

  // Gets volume level in 0-100% range (0 being pure silence) for the current
  // active node.
  virtual int GetOutputVolumePercent();

  // Gets volume level in 0-100% range (0 being pure silence) for a device.
  virtual int GetOutputVolumePercentForDevice(uint64_t device_id);

  // Gets gain level in 0-100% range (0 being pure silence) for the current
  // active node.
  virtual int GetInputGainPercent();

  // Gets volume level in 0-100% range (0 being pure silence) for a device.
  virtual int GetInputGainPercentForDevice(uint64_t device_id);

  // Returns node_id of the primary active output node.
  virtual uint64_t GetPrimaryActiveOutputNode() const;

  // Returns the node_id of the primary active input node.
  virtual uint64_t GetPrimaryActiveInputNode() const;

  // Gets the audio devices back in |device_list|.
  // This call can be invoked from I/O thread or UI thread because
  // it does not need to access CrasAudioClient on DBus.
  virtual void GetAudioDevices(AudioDeviceList* device_list) const;

  virtual bool GetPrimaryActiveOutputDevice(AudioDevice* device) const;

  // Whether there is alternative input/output audio device.
  virtual bool has_alternative_input() const;
  virtual bool has_alternative_output() const;

  // Sets all active output devices' volume level to |volume_percent|, whose
  // range is from 0-100%.
  virtual void SetOutputVolumePercent(int volume_percent);

  // Sets all active input devices' gain level to |gain_percent|, whose range is
  // from 0-100%.
  virtual void SetInputGainPercent(int gain_percent);

  // Adjusts all active output devices' volume up (positive percentage) or down
  // (negative percentage).
  virtual void AdjustOutputVolumeByPercent(int adjust_by_percent);

  // Adjusts all active output devices' volume to a minimum audible level if it
  // is too low.
  virtual void AdjustOutputVolumeToAudibleLevel();

  // Mutes or unmutes audio output device.
  virtual void SetOutputMute(bool mute_on);

  // Mutes or unmutes audio input device.
  virtual void SetInputMute(bool mute_on);

  // Switches active audio device to |device|. |activate_by| indicates why
  // the device is switched to active: by user's manual choice, by priority,
  // or by restoring to its previous active state.
  virtual void SwitchToDevice(const AudioDevice& device,
                              bool notify,
                              DeviceActivateType activate_by);

  // Sets volume/gain level for a device.
  virtual void SetVolumeGainPercentForDevice(uint64_t device_id, int value);

  // Sets the mute for device.
  virtual void SetMuteForDevice(uint64_t device_id, bool mute_on);

  // Activates or deactivates keyboard mic if there's one.
  virtual void SetKeyboardMicActive(bool active);

  // Changes the active nodes to the nodes specified by |new_active_ids|.
  // The caller can pass in the "complete" active node list of either input
  // nodes, or output nodes, or both. If only input nodes are passed in,
  // it will only change the input nodes' active status, output nodes will NOT
  // be changed; similarly for the case if only output nodes are passed.
  // If the nodes specified in |new_active_ids| are already active, they will
  // remain active. Otherwise, the old active nodes will be de-activated before
  // we activate the new nodes with the same type(input/output).
  virtual void ChangeActiveNodes(const NodeIdList& new_active_ids);

  // Swaps the left and right channel of the internal speaker.
  // Swap the left and right channel if |swap| is true; otherwise, swap the left
  // and right channel back to the normal mode.
  // If the feature is not supported on the device, nothing happens.
  virtual void SwapInternalSpeakerLeftRightChannel(bool swap);

  // Enables error logging.
  virtual void LogErrors();

  // If necessary, sets the starting point for re-discovering the active HDMI
  // output device caused by device entering/exiting docking mode, HDMI display
  // changing resolution, or chromeos device suspend/resume. If
  // |force_rediscovering| is true, it will force to set the starting point for
  // re-discovering the active HDMI output device again if it has been in the
  // middle of rediscovering the HDMI active output device.
  virtual void SetActiveHDMIOutoutRediscoveringIfNecessary(
      bool force_rediscovering);

 protected:
  explicit CrasAudioHandler(
      scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler);
  ~CrasAudioHandler() override;

 private:
  friend class CrasAudioHandlerTest;

  // CrasAudioClient::Observer overrides.
  void AudioClientRestarted() override;
  void NodesChanged() override;
  void ActiveOutputNodeChanged(uint64_t node_id) override;
  void ActiveInputNodeChanged(uint64_t node_id) override;

  // AudioPrefObserver overrides.
  void OnAudioPolicyPrefChanged() override;

  // SessionManagerClient::Observer overrides.
  void EmitLoginPromptVisibleCalled() override;

  // Sets the |active_device| to be active.
  // If |notify|, notifies Active*NodeChange.
  // Saves device active states in prefs. |activate_by| indicates how
  // the device was activated.
  void SetActiveDevice(const AudioDevice& active_device,
                       bool notify,
                       DeviceActivateType activate_by);

  // Saves |device|'s state in pref. If |active| is true, |activate_by|
  // indicates how |device| is activated.
  void SaveDeviceState(const AudioDevice& device,
                       bool active,
                       DeviceActivateType activate_by);

  // Sets up the audio device state based on audio policy and audio settings
  // saved in prefs.
  void SetupAudioInputState();
  void SetupAudioOutputState();

  // Sets up the additional active audio node's state.
  void SetupAdditionalActiveAudioNodeState(uint64_t node_id);

  const AudioDevice* GetDeviceFromId(uint64_t device_id) const;
  const AudioDevice* GetDeviceFromStableDeviceId(
      uint64_t stable_device_id) const;
  const AudioDevice* GetKeyboardMic() const;

  // Initializes audio state, which should only be called when CrasAudioHandler
  // is created or cras audio client is restarted.
  void InitializeAudioState();

  void InitializeAudioAfterCrasServiceAvailable(bool service_is_available);

  // Applies the audio muting policies whenever the user logs in or policy
  // change notification is received.
  void ApplyAudioPolicy();

  // Sets output volume of |node_id| to |volume|.
  void SetOutputNodeVolume(uint64_t node_id, int volume);

  void SetOutputNodeVolumePercent(uint64_t node_id, int volume_percent);

  // Sets output mute state to |mute_on| internally, returns true if output mute
  // is set.
  bool SetOutputMuteInternal(bool mute_on);

  // Sets input gain of |node_id| to |gain|.
  void SetInputNodeGain(uint64_t node_id, int gain);

  void SetInputNodeGainPercent(uint64_t node_id, int gain_percent);

  // Sets input mute state to |mute_on| internally.
  void SetInputMuteInternal(bool mute_on);

  // Calling dbus to get nodes data.
  void GetNodes();

  // Updates the current audio nodes list and switches the active device
  // if needed.
  void UpdateDevicesAndSwitchActive(const AudioNodeList& nodes);

  // Returns true if the current active device is changed to
  // |new_active_device|.
  bool ChangeActiveDevice(const AudioDevice& new_active_device);

  // Returns true if there are any device changes for input or output
  // specified by |is_input|, by comparing |audio_devices_| with |new_nodes|.
  // Passes the new nodes discovered in *|new_discovered|.
  // *|device_removed| indicates if any devices have been removed.
  // *|active_device_removed| indicates if the current active device has been
  // removed.
  bool HasDeviceChange(const AudioNodeList& new_nodes,
                       bool is_input,
                       AudioDevicePriorityQueue* new_discovered,
                       bool* device_removed,
                       bool* active_device_removed);

  // Handles dbus callback for GetNodes.
  void HandleGetNodes(const chromeos::AudioNodeList& node_list, bool success);

  // Handles the dbus error callback.
  void HandleGetNodesError(const std::string& error_name,
                           const std::string& error_msg);

  // Adds an active node.
  // If there is no active node, |node_id| will be switched to become the
  // primary active node. Otherwise, it will be added as an additional active
  // node.
  void AddActiveNode(uint64_t node_id, bool notify);

  // Adds |node_id| into additional active nodes.
  void AddAdditionalActiveNode(uint64_t node_id, bool notify);

  // Removes |node_id| from additional active nodes.
  void RemoveActiveNodeInternal(uint64_t node_id, bool notify);

  void UpdateAudioAfterHDMIRediscoverGracePeriod();

  bool IsHDMIPrimaryOutputDevice() const;

  void StartHDMIRediscoverGracePeriod();

  bool hdmi_rediscovering() const { return hdmi_rediscovering_; }

  void SetHDMIRediscoverGracePeriodForTesting(int duration_in_ms);

  enum DeviceStatus {
    OLD_DEVICE,
    NEW_DEVICE,
    CHANGED_DEVICE,
  };

  // Checks if |device| is a newly discovered, changed, or existing device for
  // the nodes sent from NodesChanged signal.
  DeviceStatus CheckDeviceStatus(const AudioDevice& device);

  void NotifyActiveNodeChanged(bool is_input);

  // Returns true if it retrieves an active audio device from user preference
  // among the current |audio_devices_|.
  bool GetActiveDeviceFromUserPref(bool is_input, AudioDevice* device);

  // Handles either input or output device changes, specified by |is_input|.
  void HandleAudioDeviceChange(bool is_input,
                               const AudioDevicePriorityQueue& devices_pq,
                               const AudioDevicePriorityQueue& hotplug_nodes,
                               bool has_device_change,
                               bool has_device_removed,
                               bool active_device_removed);

  // Handles non-hotplug nodes change cases.
  void HandleNonHotplugNodesChange(
      bool is_input,
      const AudioDevicePriorityQueue& hotplug_nodes,
      bool has_device_change,
      bool has_device_removed,
      bool active_device_removed);

  // Handles the regular user hotplug case.
  void HandleHotPlugDevice(
      const AudioDevice& hotplug_device,
      const AudioDevicePriorityQueue& device_priority_queue);

  void SwitchToTopPriorityDevice(bool is_input);

  // Switch to previous active device if it is found, otherwise, switch
  // to the top priority device.
  void SwitchToPreviousActiveDeviceIfAvailable(bool is_input);

  scoped_refptr<AudioDevicesPrefHandler> audio_pref_handler_;
  base::ObserverList<AudioObserver> observers_;

  // Audio data and state.
  AudioDeviceMap audio_devices_;

  AudioDevicePriorityQueue input_devices_pq_;
  AudioDevicePriorityQueue output_devices_pq_;

  bool output_mute_on_;
  bool input_mute_on_;
  int output_volume_;
  int input_gain_;
  uint64_t active_output_node_id_;
  uint64_t active_input_node_id_;
  bool has_alternative_input_;
  bool has_alternative_output_;

  bool output_mute_locked_;

  // Failures are not logged at startup, since CRAS may not be running yet.
  bool log_errors_;

  // Timer for HDMI re-discovering grace period.
  base::OneShotTimer hdmi_rediscover_timer_;
  int hdmi_rediscover_grace_period_duration_in_ms_;
  bool hdmi_rediscovering_;

  bool cras_service_available_ = false;

  base::WeakPtrFactory<CrasAudioHandler> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(CrasAudioHandler);
};

}  // namespace chromeos

#endif  // CHROMEOS_AUDIO_CRAS_AUDIO_HANDLER_H_