Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: chromeos/audio/cras_audio_handler.cc

Issue 1746843002: Persist the user's active audio device choice across chromeos session and reboots. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix nits. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chromeos/audio/cras_audio_handler.h ('k') | chromeos/audio/cras_audio_handler_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromeos/audio/cras_audio_handler.h" 5 #include "chromeos/audio/cras_audio_handler.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
11 #include <cmath> 11 #include <cmath>
12 12
13 #include "base/bind.h" 13 #include "base/bind.h"
14 #include "base/bind_helpers.h" 14 #include "base/bind_helpers.h"
15 #include "base/logging.h" 15 #include "base/logging.h"
16 #include "chromeos/audio/audio_devices_pref_handler.h" 16 #include "base/sys_info.h"
17 #include "chromeos/audio/audio_devices_pref_handler_stub.h" 17 #include "chromeos/audio/audio_devices_pref_handler_stub.h"
18 #include "chromeos/dbus/dbus_thread_manager.h" 18 #include "chromeos/dbus/dbus_thread_manager.h"
19 19
20 using std::max; 20 using std::max;
21 using std::min; 21 using std::min;
22 22
23 namespace chromeos { 23 namespace chromeos {
24 24
25 namespace { 25 namespace {
26 26
27 // Default value for unmuting, as a percent in the range [0, 100]. 27 // Default value for unmuting, as a percent in the range [0, 100].
28 // Used when sound is unmuted, but volume was less than kMuteThresholdPercent. 28 // Used when sound is unmuted, but volume was less than kMuteThresholdPercent.
29 const int kDefaultUnmuteVolumePercent = 4; 29 const int kDefaultUnmuteVolumePercent = 4;
30 30
31 // Volume value which should be considered as muted in range [0, 100]. 31 // Volume value which should be considered as muted in range [0, 100].
32 const int kMuteThresholdPercent = 1; 32 const int kMuteThresholdPercent = 1;
33 33
34 // The duration of HDMI output re-discover grace period in milliseconds. 34 // The duration of HDMI output re-discover grace period in milliseconds.
35 const int kHDMIRediscoverGracePeriodDurationInMs = 2000; 35 const int kHDMIRediscoverGracePeriodDurationInMs = 2000;
36 36
37 static CrasAudioHandler* g_cras_audio_handler = NULL; 37 static CrasAudioHandler* g_cras_audio_handler = NULL;
38 38
39 bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) { 39 bool IsSameAudioDevice(const AudioDevice& a, const AudioDevice& b) {
40 return a.id == b.id && a.is_input == b.is_input && a.type == b.type 40 return a.stable_device_id == b.stable_device_id && a.is_input == b.is_input &&
41 && a.device_name == b.device_name; 41 a.type == b.type && a.device_name == b.device_name;
42 } 42 }
43 43
44 bool IsInNodeList(uint64_t node_id, 44 bool IsInNodeList(uint64_t node_id,
45 const CrasAudioHandler::NodeIdList& id_list) { 45 const CrasAudioHandler::NodeIdList& id_list) {
46 return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end(); 46 return std::find(id_list.begin(), id_list.end(), node_id) != id_list.end();
47 } 47 }
48 48
49 bool IsDeviceInList(const AudioDevice& device, const AudioNodeList& node_list) {
50 for (const AudioNode& node : node_list) {
51 if (device.stable_device_id == node.stable_device_id)
52 return true;
53 }
54 return false;
55 }
56
49 } // namespace 57 } // namespace
50 58
51 CrasAudioHandler::AudioObserver::AudioObserver() { 59 CrasAudioHandler::AudioObserver::AudioObserver() {
52 } 60 }
53 61
54 CrasAudioHandler::AudioObserver::~AudioObserver() { 62 CrasAudioHandler::AudioObserver::~AudioObserver() {
55 } 63 }
56 64
57 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged( 65 void CrasAudioHandler::AudioObserver::OnOutputNodeVolumeChanged(
58 uint64_t /* node_id */, 66 uint64_t /* node_id */,
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after
226 const AudioDevice* device = GetDeviceFromId(node_id); 234 const AudioDevice* device = GetDeviceFromId(node_id);
227 if (!device) { 235 if (!device) {
228 VLOG(1) << "AddActiveInputNode: Cannot find device id=" 236 VLOG(1) << "AddActiveInputNode: Cannot find device id="
229 << "0x" << std::hex << node_id; 237 << "0x" << std::hex << node_id;
230 return; 238 return;
231 } 239 }
232 240
233 // If there is no primary active device, set |node_id| to primary active node. 241 // If there is no primary active device, set |node_id| to primary active node.
234 if ((device->is_input && !active_input_node_id_) || 242 if ((device->is_input && !active_input_node_id_) ||
235 (!device->is_input && !active_output_node_id_)) { 243 (!device->is_input && !active_output_node_id_)) {
236 SwitchToDevice(*device, notify); 244 SwitchToDevice(*device, notify, ACTIVATE_BY_USER);
237 return; 245 return;
238 } 246 }
239 247
240 AddAdditionalActiveNode(node_id, notify); 248 AddAdditionalActiveNode(node_id, notify);
241 } 249 }
242 250
243 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList& new_active_ids) { 251 void CrasAudioHandler::ChangeActiveNodes(const NodeIdList& new_active_ids) {
244 // Flags for whether there are input or output nodes passed in from 252 // Flags for whether there are input or output nodes passed in from
245 // |new_active_ids|. If there are no input nodes passed in, we will not 253 // |new_active_ids|. If there are no input nodes passed in, we will not
246 // make any change for input nodes; same for the output nodes. 254 // make any change for input nodes; same for the output nodes.
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 SetOutputVolumePercent(kDefaultUnmuteVolumePercent); 389 SetOutputVolumePercent(kDefaultUnmuteVolumePercent);
382 } 390 }
383 } 391 }
384 392
385 void CrasAudioHandler::SetInputMute(bool mute_on) { 393 void CrasAudioHandler::SetInputMute(bool mute_on) {
386 SetInputMuteInternal(mute_on); 394 SetInputMuteInternal(mute_on);
387 FOR_EACH_OBSERVER(AudioObserver, observers_, 395 FOR_EACH_OBSERVER(AudioObserver, observers_,
388 OnInputMuteChanged(input_mute_on_)); 396 OnInputMuteChanged(input_mute_on_));
389 } 397 }
390 398
391 void CrasAudioHandler::SetActiveOutputNode(uint64_t node_id, bool notify) { 399 void CrasAudioHandler::SetActiveDevice(const AudioDevice& active_device,
392 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 400 bool notify,
393 SetActiveOutputNode(node_id); 401 DeviceActivateType activate_by) {
402 if (active_device.is_input) {
403 chromeos::DBusThreadManager::Get()
404 ->GetCrasAudioClient()
405 ->SetActiveInputNode(active_device.id);
406 } else {
407 chromeos::DBusThreadManager::Get()
408 ->GetCrasAudioClient()
409 ->SetActiveOutputNode(active_device.id);
410 }
411
394 if (notify) 412 if (notify)
395 NotifyActiveNodeChanged(false); 413 NotifyActiveNodeChanged(active_device.is_input);
396 414
397 // Save state for all output nodes. 415 // Save active state for the nodes.
398 for (AudioDeviceMap::iterator it = audio_devices_.begin(); 416 for (AudioDeviceMap::iterator it = audio_devices_.begin();
399 it != audio_devices_.end(); ++it) { 417 it != audio_devices_.end(); ++it) {
400 if (it->second.is_input) 418 const AudioDevice& device = it->second;
419 if (device.is_input != active_device.is_input)
401 continue; 420 continue;
402 audio_pref_handler_->SetDeviceState(it->second, it->second.active 421 SaveDeviceState(device, device.active, activate_by);
403 ? AUDIO_STATE_ACTIVE
404 : AUDIO_STATE_INACTIVE);
405 } 422 }
406 } 423 }
407 424
408 void CrasAudioHandler::SetActiveInputNode(uint64_t node_id, bool notify) { 425 void CrasAudioHandler::SaveDeviceState(const AudioDevice& device,
409 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()-> 426 bool active,
410 SetActiveInputNode(node_id); 427 DeviceActivateType activate_by) {
411 if (notify) 428 if (!active) {
412 NotifyActiveNodeChanged(true); 429 audio_pref_handler_->SetDeviceActive(device, false, false);
413 430 } else {
414 // Save state for all input nodes. 431 switch (activate_by) {
415 for (AudioDeviceMap::iterator it = audio_devices_.begin(); 432 case ACTIVATE_BY_USER:
416 it != audio_devices_.end(); ++it) { 433 audio_pref_handler_->SetDeviceActive(device, true, true);
417 if (!it->second.is_input) 434 break;
418 continue; 435 case ACTIVATE_BY_PRIORITY:
419 audio_pref_handler_->SetDeviceState(it->second, it->second.active 436 audio_pref_handler_->SetDeviceActive(device, true, false);
420 ? AUDIO_STATE_ACTIVE 437 break;
421 : AUDIO_STATE_INACTIVE); 438 default:
439 // The device is made active due to its previous active state in prefs,
440 // don't change its active state settings in prefs.
441 break;
442 }
422 } 443 }
423 } 444 }
424 445
425 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id, 446 void CrasAudioHandler::SetVolumeGainPercentForDevice(uint64_t device_id,
426 int value) { 447 int value) {
427 const AudioDevice* device = GetDeviceFromId(device_id); 448 const AudioDevice* device = GetDeviceFromId(device_id);
428 if (!device) 449 if (!device)
429 return; 450 return;
430 451
431 if (device->is_input) 452 if (device->is_input)
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
525 } 546 }
526 547
527 void CrasAudioHandler::AudioClientRestarted() { 548 void CrasAudioHandler::AudioClientRestarted() {
528 // Make sure the logging is enabled in case cras server 549 // Make sure the logging is enabled in case cras server
529 // restarts after crashing. 550 // restarts after crashing.
530 LogErrors(); 551 LogErrors();
531 InitializeAudioState(); 552 InitializeAudioState();
532 } 553 }
533 554
534 void CrasAudioHandler::NodesChanged() { 555 void CrasAudioHandler::NodesChanged() {
535 // Refresh audio nodes data. 556 if (cras_service_available_)
536 GetNodes(); 557 GetNodes();
537 } 558 }
538 559
539 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id) { 560 void CrasAudioHandler::ActiveOutputNodeChanged(uint64_t node_id) {
540 if (active_output_node_id_ == node_id) 561 if (active_output_node_id_ == node_id)
541 return; 562 return;
542 563
543 // Active audio output device should always be changed by chrome. 564 // Active audio output device should always be changed by chrome.
544 // During system boot, cras may change active input to unknown device 0x1, 565 // During system boot, cras may change active input to unknown device 0x1,
545 // we don't need to log it, since it is not an valid device. 566 // we don't need to log it, since it is not an valid device.
546 if (GetDeviceFromId(node_id)) { 567 if (GetDeviceFromId(node_id)) {
(...skipping 28 matching lines...) Expand all
575 } 596 }
576 597
577 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64_t device_id) const { 598 const AudioDevice* CrasAudioHandler::GetDeviceFromId(uint64_t device_id) const {
578 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id); 599 AudioDeviceMap::const_iterator it = audio_devices_.find(device_id);
579 if (it == audio_devices_.end()) 600 if (it == audio_devices_.end())
580 return NULL; 601 return NULL;
581 602
582 return &(it->second); 603 return &(it->second);
583 } 604 }
584 605
606 const AudioDevice* CrasAudioHandler::GetDeviceFromStableDeviceId(
607 uint64_t stable_device_id) const {
608 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
609 it != audio_devices_.end(); ++it) {
610 if (it->second.stable_device_id == stable_device_id)
611 return &(it->second);
612 }
613 return NULL;
614 }
615
585 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const { 616 const AudioDevice* CrasAudioHandler::GetKeyboardMic() const {
586 for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); 617 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
587 it != audio_devices_.end(); it++) { 618 it != audio_devices_.end(); it++) {
588 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC) 619 if (it->second.is_input && it->second.type == AUDIO_TYPE_KEYBOARD_MIC)
589 return &(it->second); 620 return &(it->second);
590 } 621 }
591 return NULL; 622 return NULL;
592 } 623 }
593 624
594 void CrasAudioHandler::SetupAudioInputState() { 625 void CrasAudioHandler::SetupAudioInputState() {
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
648 // TODO(jennyz): crbug.com/417418, track the status for the decison if 679 // TODO(jennyz): crbug.com/417418, track the status for the decison if
649 // we should persist input gain value in prefs. 680 // we should persist input gain value in prefs.
650 if (!device->is_input) { 681 if (!device->is_input) {
651 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted()); 682 audio_pref_handler_->SetMuteValue(*device, IsOutputMuted());
652 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent()); 683 SetOutputNodeVolumePercent(node_id, GetOutputVolumePercent());
653 } 684 }
654 } 685 }
655 686
656 void CrasAudioHandler::InitializeAudioState() { 687 void CrasAudioHandler::InitializeAudioState() {
657 ApplyAudioPolicy(); 688 ApplyAudioPolicy();
689
690 // Defer querying cras for GetNodes until cras service becomes available.
691 cras_service_available_ = false;
692 chromeos::DBusThreadManager::Get()
693 ->GetCrasAudioClient()
694 ->WaitForServiceToBeAvailable(base::Bind(
695 &CrasAudioHandler::InitializeAudioAfterCrasServiceAvailable,
696 weak_ptr_factory_.GetWeakPtr()));
697 }
698
699 void CrasAudioHandler::InitializeAudioAfterCrasServiceAvailable(
700 bool service_is_available) {
701 if (!service_is_available) {
702 LOG(ERROR) << "Cras service is not available";
703 cras_service_available_ = false;
704 return;
705 }
706
707 cras_service_available_ = true;
658 GetNodes(); 708 GetNodes();
659 } 709 }
660 710
661 void CrasAudioHandler::ApplyAudioPolicy() { 711 void CrasAudioHandler::ApplyAudioPolicy() {
662 output_mute_locked_ = false; 712 output_mute_locked_ = false;
663 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) { 713 if (!audio_pref_handler_->GetAudioOutputAllowedValue()) {
664 // Mute the device, but do not update the preference. 714 // Mute the device, but do not update the preference.
665 SetOutputMuteInternal(true); 715 SetOutputMuteInternal(true);
666 output_mute_locked_ = true; 716 output_mute_locked_ = true;
667 } else { 717 } else {
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
743 } 793 }
744 794
745 void CrasAudioHandler::GetNodes() { 795 void CrasAudioHandler::GetNodes() {
746 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes( 796 chromeos::DBusThreadManager::Get()->GetCrasAudioClient()->GetNodes(
747 base::Bind(&CrasAudioHandler::HandleGetNodes, 797 base::Bind(&CrasAudioHandler::HandleGetNodes,
748 weak_ptr_factory_.GetWeakPtr()), 798 weak_ptr_factory_.GetWeakPtr()),
749 base::Bind(&CrasAudioHandler::HandleGetNodesError, 799 base::Bind(&CrasAudioHandler::HandleGetNodesError,
750 weak_ptr_factory_.GetWeakPtr())); 800 weak_ptr_factory_.GetWeakPtr()));
751 } 801 }
752 802
753 bool CrasAudioHandler::ChangeActiveDevice(const AudioDevice& new_active_device, 803 bool CrasAudioHandler::ChangeActiveDevice(
754 uint64_t* current_active_node_id) { 804 const AudioDevice& new_active_device) {
805 uint64_t& current_active_node_id = new_active_device.is_input
806 ? active_input_node_id_
807 : active_output_node_id_;
755 // If the device we want to switch to is already the current active device, 808 // If the device we want to switch to is already the current active device,
756 // do nothing. 809 // do nothing.
757 if (new_active_device.active && 810 if (new_active_device.active &&
758 new_active_device.id == *current_active_node_id) { 811 new_active_device.id == current_active_node_id) {
759 return false; 812 return false;
760 } 813 }
761 814
815 bool found_new_active_device = false;
762 // Reset all other input or output devices' active status. The active audio 816 // Reset all other input or output devices' active status. The active audio
763 // device from the previous user session can be remembered by cras, but not 817 // device from the previous user session can be remembered by cras, but not
764 // in chrome. see crbug.com/273271. 818 // in chrome. see crbug.com/273271.
765 for (AudioDeviceMap::iterator it = audio_devices_.begin(); 819 for (AudioDeviceMap::iterator it = audio_devices_.begin();
766 it != audio_devices_.end(); ++it) { 820 it != audio_devices_.end(); ++it) {
767 if (it->second.is_input == new_active_device.is_input && 821 if (it->second.is_input == new_active_device.is_input &&
768 it->second.id != new_active_device.id) 822 it->second.id != new_active_device.id) {
769 it->second.active = false; 823 it->second.active = false;
824 } else if (it->second.is_input == new_active_device.is_input &&
825 it->second.id == new_active_device.id) {
826 found_new_active_device = true;
827 }
828 }
829
830 if (!found_new_active_device) {
831 LOG(ERROR) << "Invalid new active device: " << new_active_device.ToString();
832 return false;
770 } 833 }
771 834
772 // Set the current active input/output device to the new_active_device. 835 // Set the current active input/output device to the new_active_device.
773 *current_active_node_id = new_active_device.id; 836 current_active_node_id = new_active_device.id;
774 audio_devices_[*current_active_node_id].active = true; 837 audio_devices_[current_active_node_id].active = true;
775 return true; 838 return true;
776 } 839 }
777 840
778 bool CrasAudioHandler::NonActiveDeviceUnplugged(size_t old_devices_size, 841 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device,
779 size_t new_devices_size, 842 bool notify,
780 uint64_t current_active_node) { 843 DeviceActivateType activate_by) {
781 return (new_devices_size < old_devices_size && 844 if (!ChangeActiveDevice(device))
782 GetDeviceFromId(current_active_node)); 845 return;
846
847 if (device.is_input)
848 SetupAudioInputState();
849 else
850 SetupAudioOutputState();
851
852 SetActiveDevice(device, notify, activate_by);
783 } 853 }
784 854
785 void CrasAudioHandler::SwitchToDevice(const AudioDevice& device, bool notify) { 855 bool CrasAudioHandler::HasDeviceChange(const AudioNodeList& new_nodes,
786 if (device.is_input) { 856 bool is_input,
787 if (!ChangeActiveDevice(device, &active_input_node_id_)) 857 AudioDevicePriorityQueue* new_discovered,
788 return; 858 bool* device_removed,
789 SetupAudioInputState(); 859 bool* active_device_removed) {
790 SetActiveInputNode(active_input_node_id_, notify); 860 *device_removed = false;
791 } else {
792 if (!ChangeActiveDevice(device, &active_output_node_id_))
793 return;
794 SetupAudioOutputState();
795 SetActiveOutputNode(active_output_node_id_, notify);
796 }
797 }
798
799 bool CrasAudioHandler::HasDeviceChange(
800 const AudioNodeList& new_nodes,
801 bool is_input,
802 AudioDevicePriorityQueue* new_discovered) {
803 size_t num_old_devices = 0;
804 size_t num_new_devices = 0;
805 for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); 861 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
806 it != audio_devices_.end(); ++it) { 862 it != audio_devices_.end(); ++it) {
807 if (is_input == it->second.is_input) 863 const AudioDevice& device = it->second;
808 ++num_old_devices; 864 if (is_input != device.is_input)
865 continue;
866 if (!IsDeviceInList(device, new_nodes)) {
867 *device_removed = true;
868 if ((is_input && device.id == active_input_node_id_) ||
869 (!is_input && device.id == active_output_node_id_)) {
870 *active_device_removed = true;
871 }
872 }
809 } 873 }
810 874
811 bool new_or_changed_device = false; 875 bool new_or_changed_device = false;
812 while (!new_discovered->empty()) 876 while (!new_discovered->empty())
813 new_discovered->pop(); 877 new_discovered->pop();
814 for (AudioNodeList::const_iterator it = new_nodes.begin(); 878 for (AudioNodeList::const_iterator it = new_nodes.begin();
815 it != new_nodes.end(); ++it) { 879 it != new_nodes.end(); ++it) {
816 if (is_input == it->is_input) { 880 if (is_input != it->is_input)
817 ++num_new_devices; 881 continue;
818 // Look to see if the new device not in the old device list. 882 // Check if the new device is not in the old device list.
819 AudioDevice device(*it); 883 AudioDevice device(*it);
820 DeviceStatus status = CheckDeviceStatus(device); 884 DeviceStatus status = CheckDeviceStatus(device);
821 if (status == NEW_DEVICE) 885 if (status == NEW_DEVICE)
822 new_discovered->push(device); 886 new_discovered->push(device);
823 if (status == NEW_DEVICE || status == CHANGED_DEVICE) { 887 if (status == NEW_DEVICE || status == CHANGED_DEVICE) {
824 new_or_changed_device = true; 888 new_or_changed_device = true;
825 }
826 } 889 }
827 } 890 }
828 return new_or_changed_device || (num_old_devices != num_new_devices); 891 return new_or_changed_device || *device_removed;
829 } 892 }
830 893
831 CrasAudioHandler::DeviceStatus CrasAudioHandler::CheckDeviceStatus( 894 CrasAudioHandler::DeviceStatus CrasAudioHandler::CheckDeviceStatus(
832 const AudioDevice& device) { 895 const AudioDevice& device) {
833 const AudioDevice* device_found = GetDeviceFromId(device.id); 896 const AudioDevice* device_found =
897 GetDeviceFromStableDeviceId(device.stable_device_id);
834 if (!device_found) 898 if (!device_found)
835 return NEW_DEVICE; 899 return NEW_DEVICE;
836 900
837 if (!IsSameAudioDevice(device, *device_found)) { 901 if (!IsSameAudioDevice(device, *device_found)) {
838 LOG(WARNING) << "Different Audio devices with same id:" 902 LOG(ERROR) << "Different Audio devices with same stable device id:"
839 << " new device: " << device.ToString() 903 << " new device: " << device.ToString()
840 << " old device: " << device_found->ToString(); 904 << " old device: " << device_found->ToString();
841 return CHANGED_DEVICE; 905 return CHANGED_DEVICE;
842 } else if (device.active != device_found->active) { 906 } else if (device.active != device_found->active) {
843 return CHANGED_DEVICE; 907 return CHANGED_DEVICE;
844 } 908 }
845 909
846 return OLD_DEVICE; 910 return OLD_DEVICE;
847 } 911 }
848 912
849 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) { 913 void CrasAudioHandler::NotifyActiveNodeChanged(bool is_input) {
850 if (is_input) 914 if (is_input)
851 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged()); 915 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveInputNodeChanged());
852 else 916 else
853 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged()); 917 FOR_EACH_OBSERVER(AudioObserver, observers_, OnActiveOutputNodeChanged());
854 } 918 }
855 919
920 bool CrasAudioHandler::GetActiveDeviceFromUserPref(bool is_input,
921 AudioDevice* active_device) {
922 bool found_active_device = false;
923 bool last_active_device_activate_by_user = false;
924 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
925 it != audio_devices_.end(); ++it) {
926 AudioDevice device = it->second;
927 if (device.is_input != is_input)
928 continue;
929
930 bool active = false;
931 bool activate_by_user = false;
932 if (!audio_pref_handler_->GetDeviceActive(device, &active,
933 &activate_by_user) ||
934 !active) {
935 continue;
936 }
937
938 if (!found_active_device) {
939 found_active_device = true;
940 *active_device = device;
941 last_active_device_activate_by_user = activate_by_user;
942 continue;
943 }
944
945 // Choose the best one among multiple active devices from prefs.
946 if (activate_by_user) {
947 if (!last_active_device_activate_by_user) {
948 // Device activated by user has higher priority than the one
949 // is not activated by user.
950 *active_device = device;
951 last_active_device_activate_by_user = true;
952 } else {
953 // If there are more than one active devices activated by user in the
954 // prefs, most likely, after the device was shut down, and before it
955 // is rebooted, user has plugged in some previously unplugged audio
956 // devices. For such case, it does not make sense to honor the active
957 // states in the prefs.
958 VLOG(1) << "Found more than one user activated devices in the prefs.";
959 return false;
960 }
961 } else if (!last_active_device_activate_by_user) {
962 // If there are more than one active devices activated by priority in the
963 // prefs, most likely, cras is still enumerating the audio devices
964 // progressively. For such case, it does not make sense to honor the
965 // active states in the prefs.
966 VLOG(1) << "Found more than one active devices by priority in the prefs.";
967 return false;
968 }
969 }
970
971 if (found_active_device && !active_device->is_for_simple_usage()) {
972 // This is an odd case which is rare but possible to happen during cras
973 // initialization depeneding the audio device enumation process. The only
974 // audio node coming from cras is an internal audio device not visible
975 // to user, such as AUDIO_TYPE_POST_MIX_LOOPBACK.
976 return false;
977 }
978
979 return found_active_device;
980 }
981
982 void CrasAudioHandler::HandleNonHotplugNodesChange(
983 bool is_input,
984 const AudioDevicePriorityQueue& hotplug_nodes,
985 bool has_device_change,
986 bool has_device_removed,
987 bool active_device_removed) {
988 bool has_current_active_node =
989 is_input ? active_input_node_id_ : active_output_node_id_;
990
991 // No device change, extra NodesChanged signal received.
992 if (!has_device_change && has_current_active_node)
993 return;
994
995 if (hotplug_nodes.empty()) {
996 // Unplugged a non-active device.
997 if (has_device_removed) {
998 if (!active_device_removed && has_current_active_node) {
999 // Removed a non-active device, keep the current active device.
1000 return;
1001 }
1002
1003 if (active_device_removed) {
1004 // Unplugged the current active device.
1005 SwitchToTopPriorityDevice(is_input);
1006 return;
1007 }
1008 }
1009
1010 // Some unexpected error happens on cras side. See crbug.com/586026.
1011 // Either cras sent stale nodes to chrome again or cras triggered some
1012 // error. Restore the previously selected active.
1013 VLOG(1) << "Odd case from cras, the active node is lost unexpectedly. ";
1014 SwitchToPreviousActiveDeviceIfAvailable(is_input);
1015 } else {
1016 // Looks like a new chrome session starts.
1017 SwitchToPreviousActiveDeviceIfAvailable(is_input);
1018 }
1019 }
1020
1021 void CrasAudioHandler::HandleHotPlugDevice(
1022 const AudioDevice& hotplug_device,
1023 const AudioDevicePriorityQueue& device_priority_queue) {
1024 bool last_state_active = false;
1025 bool last_activate_by_user = false;
1026 if (!audio_pref_handler_->GetDeviceActive(hotplug_device, &last_state_active,
1027 &last_activate_by_user)) {
1028 // |hotplug_device| is plugged in for the first time, activate it if it
1029 // is of the highest priority.
1030 if (device_priority_queue.top().id == hotplug_device.id) {
1031 VLOG(1) << "Hotplug a device for the first time: "
1032 << hotplug_device.ToString();
1033 SwitchToDevice(hotplug_device, true, ACTIVATE_BY_PRIORITY);
1034 }
1035 } else if (last_state_active) {
1036 VLOG(1) << "Hotplug a device, restore to active: "
1037 << hotplug_device.ToString();
1038 SwitchToDevice(hotplug_device, true, ACTIVATE_BY_RESTORE_PREVIOUS_STATE);
1039 } else {
1040 // Do not active the device if its previous state is inactive.
1041 VLOG(1) << "Hotplug device remains inactive as its previous state:"
1042 << hotplug_device.ToString();
1043 }
1044 }
1045
1046 void CrasAudioHandler::SwitchToTopPriorityDevice(bool is_input) {
1047 AudioDevice top_device =
1048 is_input ? input_devices_pq_.top() : output_devices_pq_.top();
1049 SwitchToDevice(top_device, true, ACTIVATE_BY_PRIORITY);
1050 }
1051
1052 void CrasAudioHandler::SwitchToPreviousActiveDeviceIfAvailable(bool is_input) {
1053 AudioDevice previous_active_device;
1054 if (GetActiveDeviceFromUserPref(is_input, &previous_active_device)) {
1055 // Switch to previous active device stored in user prefs.
1056 SwitchToDevice(previous_active_device, true,
1057 ACTIVATE_BY_RESTORE_PREVIOUS_STATE);
1058 } else {
1059 // No previous active device, switch to the top priority device.
1060 SwitchToTopPriorityDevice(is_input);
1061 }
1062 }
1063
856 void CrasAudioHandler::UpdateDevicesAndSwitchActive( 1064 void CrasAudioHandler::UpdateDevicesAndSwitchActive(
857 const AudioNodeList& nodes) { 1065 const AudioNodeList& nodes) {
858 size_t old_output_device_size = 0; 1066 size_t old_output_device_size = 0;
859 size_t old_input_device_size = 0; 1067 size_t old_input_device_size = 0;
860 for (AudioDeviceMap::const_iterator it = audio_devices_.begin(); 1068 for (AudioDeviceMap::const_iterator it = audio_devices_.begin();
861 it != audio_devices_.end(); ++it) { 1069 it != audio_devices_.end(); ++it) {
862 if (it->second.is_input) 1070 if (it->second.is_input)
863 ++old_input_device_size; 1071 ++old_input_device_size;
864 else 1072 else
865 ++old_output_device_size; 1073 ++old_output_device_size;
866 } 1074 }
867 1075
868 AudioDevicePriorityQueue hotplug_output_nodes; 1076 AudioDevicePriorityQueue hotplug_output_nodes;
869 AudioDevicePriorityQueue hotplug_input_nodes; 1077 AudioDevicePriorityQueue hotplug_input_nodes;
1078 bool has_output_removed = false;
1079 bool has_input_removed = false;
1080 bool active_output_removed = false;
1081 bool active_input_removed = false;
870 bool output_devices_changed = 1082 bool output_devices_changed =
871 HasDeviceChange(nodes, false, &hotplug_output_nodes); 1083 HasDeviceChange(nodes, false, &hotplug_output_nodes, &has_output_removed,
1084 &active_output_removed);
872 bool input_devices_changed = 1085 bool input_devices_changed =
873 HasDeviceChange(nodes, true, &hotplug_input_nodes); 1086 HasDeviceChange(nodes, true, &hotplug_input_nodes, &has_input_removed,
1087 &active_input_removed);
874 audio_devices_.clear(); 1088 audio_devices_.clear();
875 has_alternative_input_ = false; 1089 has_alternative_input_ = false;
876 has_alternative_output_ = false; 1090 has_alternative_output_ = false;
877 1091
878 while (!input_devices_pq_.empty()) 1092 while (!input_devices_pq_.empty())
879 input_devices_pq_.pop(); 1093 input_devices_pq_.pop();
880 while (!output_devices_pq_.empty()) 1094 while (!output_devices_pq_.empty())
881 output_devices_pq_.pop(); 1095 output_devices_pq_.pop();
882 1096
883 size_t new_output_device_size = 0; 1097 size_t new_output_device_size = 0;
884 size_t new_input_device_size = 0; 1098 size_t new_input_device_size = 0;
885 for (size_t i = 0; i < nodes.size(); ++i) { 1099 for (size_t i = 0; i < nodes.size(); ++i) {
886 AudioDevice device(nodes[i]); 1100 AudioDevice device(nodes[i]);
887 audio_devices_[device.id] = device; 1101 audio_devices_[device.id] = device;
888
889 if (!has_alternative_input_ && 1102 if (!has_alternative_input_ &&
890 device.is_input && 1103 device.is_input &&
891 device.type != AUDIO_TYPE_INTERNAL_MIC && 1104 device.type != AUDIO_TYPE_INTERNAL_MIC &&
892 device.type != AUDIO_TYPE_KEYBOARD_MIC) { 1105 device.type != AUDIO_TYPE_KEYBOARD_MIC) {
893 has_alternative_input_ = true; 1106 has_alternative_input_ = true;
894 } else if (!has_alternative_output_ && 1107 } else if (!has_alternative_output_ &&
895 !device.is_input && 1108 !device.is_input &&
896 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) { 1109 device.type != AUDIO_TYPE_INTERNAL_SPEAKER) {
897 has_alternative_output_ = true; 1110 has_alternative_output_ = true;
898 } 1111 }
899 1112
900 if (device.is_input) { 1113 if (device.is_input) {
901 input_devices_pq_.push(device); 1114 input_devices_pq_.push(device);
902 ++new_input_device_size; 1115 ++new_input_device_size;
903 } else { 1116 } else {
904 output_devices_pq_.push(device); 1117 output_devices_pq_.push(device);
905 ++new_output_device_size; 1118 ++new_output_device_size;
906 } 1119 }
907 } 1120 }
908 1121
1122 // Handle output device changes.
1123 HandleAudioDeviceChange(false, output_devices_pq_, hotplug_output_nodes,
1124 output_devices_changed, has_output_removed,
1125 active_output_removed);
1126
1127 // Handle input device changes.
1128 HandleAudioDeviceChange(true, input_devices_pq_, hotplug_input_nodes,
1129 input_devices_changed, has_input_removed,
1130 active_input_removed);
1131 }
1132
1133 void CrasAudioHandler::HandleAudioDeviceChange(
1134 bool is_input,
1135 const AudioDevicePriorityQueue& devices_pq,
1136 const AudioDevicePriorityQueue& hotplug_nodes,
1137 bool has_device_change,
1138 bool has_device_removed,
1139 bool active_device_removed) {
1140 uint64_t& active_node_id =
1141 is_input ? active_input_node_id_ : active_output_node_id_;
1142
1143 // No audio devices found.
1144 if (devices_pq.empty()) {
1145 VLOG(1) << "No " << (is_input ? "input" : "output") << " devices found";
1146 active_node_id = 0;
1147 NotifyActiveNodeChanged(is_input);
1148 return;
1149 }
1150
909 // If the previous active device is removed from the new node list, 1151 // If the previous active device is removed from the new node list,
910 // or changed to inactive by cras, reset active_output_node_id_. 1152 // or changed to inactive by cras, reset active_node_id.
911 // See crbug.com/478968. 1153 // See crbug.com/478968.
912 const AudioDevice* active_output = GetDeviceFromId(active_output_node_id_); 1154 const AudioDevice* active_device = GetDeviceFromId(active_node_id);
913 if (!active_output || !active_output->active) 1155 if (!active_device || !active_device->active)
914 active_output_node_id_ = 0; 1156 active_node_id = 0;
915 const AudioDevice* active_input = GetDeviceFromId(active_input_node_id_);
916 if (!active_input || !active_input->active)
917 active_input_node_id_ = 0;
918 1157
919 // If audio nodes change is caused by unplugging some non-active audio 1158 if (!active_node_id || hotplug_nodes.empty() || hotplug_nodes.size() > 1) {
920 // devices, we wont't change the current active audio devices. 1159 HandleNonHotplugNodesChange(is_input, hotplug_nodes, has_device_change,
921 if (input_devices_changed && 1160 has_device_removed, active_device_removed);
922 !NonActiveDeviceUnplugged(old_input_device_size, 1161 } else {
923 new_input_device_size, 1162 // Typical user hotplug case.
924 active_input_node_id_)) { 1163 HandleHotPlugDevice(hotplug_nodes.top(), devices_pq);
925 // Some devices like chromeboxes don't have the internal audio input. In
926 // that case the active input node id should be reset.
927 if (input_devices_pq_.empty()) {
928 active_input_node_id_ = 0;
929 NotifyActiveNodeChanged(true);
930 } else {
931 // If there is no current active node, or, no new hot plugged node, select
932 // the active node by their priorities.
933 if (!active_input_node_id_ || hotplug_input_nodes.empty()) {
934 SwitchToDevice(input_devices_pq_.top(), true);
935 } else {
936 // If user has hot plugged any input nodes, look at the one with highest
937 // priority (normally, there is only one hot plugged input node), and
938 // consider switch to it depend on its last state stored in preference.
939 AudioDeviceState last_state =
940 audio_pref_handler_->GetDeviceState(hotplug_input_nodes.top());
941 switch (last_state) {
942 case AUDIO_STATE_ACTIVE:
943 // This node was plugged in before and was selected as the active
944 // one
945 // before it was unplugged last time, switch to this device.
946 SwitchToDevice(hotplug_input_nodes.top(), true);
947 break;
948 case AUDIO_STATE_NOT_AVAILABLE:
949 // This is a new node, not plugged in before, with the highest
950 // priority. Switch to this device.
951 if (input_devices_pq_.top().id == hotplug_input_nodes.top().id)
952 SwitchToDevice(hotplug_input_nodes.top(), true);
953 break;
954 case AUDIO_STATE_INACTIVE:
955 // This node was NOT selected as the active one last time before it
956 // got unplugged, so don't switch to it.
957 default:
958 break;
959 }
960 }
961 }
962 }
963 if (output_devices_changed &&
964 !NonActiveDeviceUnplugged(old_output_device_size,
965 new_output_device_size,
966 active_output_node_id_)) {
967 // ditto input node logic.
968 if (output_devices_pq_.empty()) {
969 active_output_node_id_ = 0;
970 NotifyActiveNodeChanged(false);
971 } else {
972 if (!active_output_node_id_ || hotplug_output_nodes.empty()) {
973 SwitchToDevice(output_devices_pq_.top(), true);
974 } else {
975 AudioDeviceState last_state =
976 audio_pref_handler_->GetDeviceState(hotplug_output_nodes.top());
977 switch (last_state) {
978 case AUDIO_STATE_ACTIVE:
979 SwitchToDevice(hotplug_output_nodes.top(), true);
980 break;
981 case AUDIO_STATE_NOT_AVAILABLE:
982 if (output_devices_pq_.top().id == hotplug_output_nodes.top().id)
983 SwitchToDevice(hotplug_output_nodes.top(), true);
984 break;
985 case AUDIO_STATE_INACTIVE:
986 default:
987 break;
988 }
989 }
990 }
991 } 1164 }
992 } 1165 }
993 1166
994 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list, 1167 void CrasAudioHandler::HandleGetNodes(const chromeos::AudioNodeList& node_list,
995 bool success) { 1168 bool success) {
996 if (!success) { 1169 if (!success) {
997 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data"; 1170 LOG_IF(ERROR, log_errors_) << "Failed to retrieve audio nodes data";
998 return; 1171 return;
999 } 1172 }
1000 1173
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
1093 hdmi_rediscover_grace_period_duration_in_ms_), 1266 hdmi_rediscover_grace_period_duration_in_ms_),
1094 this, &CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod); 1267 this, &CrasAudioHandler::UpdateAudioAfterHDMIRediscoverGracePeriod);
1095 } 1268 }
1096 1269
1097 void CrasAudioHandler::SetHDMIRediscoverGracePeriodForTesting( 1270 void CrasAudioHandler::SetHDMIRediscoverGracePeriodForTesting(
1098 int duration_in_ms) { 1271 int duration_in_ms) {
1099 hdmi_rediscover_grace_period_duration_in_ms_ = duration_in_ms; 1272 hdmi_rediscover_grace_period_duration_in_ms_ = duration_in_ms;
1100 } 1273 }
1101 1274
1102 } // namespace chromeos 1275 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/audio/cras_audio_handler.h ('k') | chromeos/audio/cras_audio_handler_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698