blob: 865cdffc3f36d1840fb1f0e7910b3d7330426729 [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "power_manager/powerd/system/audio_client.h"
#include <algorithm>
#include <string>
#include <chromeos/dbus/service_constants.h>
#include <dbus/message.h>
#include "power_manager/common/util.h"
#include "power_manager/powerd/system/audio_observer.h"
namespace power_manager {
namespace system {
namespace {
// Maximum amount of time to wait for a reply from CRAS, in milliseconds.
const int kCrasDBusTimeoutMs = 3000;
// Keys within node dictionaries returned by CRAS.
const char kTypeKey[] = "Type";
const char kActiveKey[] = "Active";
// Types assigned to headphone and HDMI nodes by CRAS.
const char kHeadphoneNodeType[] = "HEADPHONE";
const char kHdmiNodeType[] = "HDMI";
} // namespace
AudioClient::AudioClient()
: cras_proxy_(NULL),
num_active_streams_(0),
headphone_jack_plugged_(false),
hdmi_active_(false) {
}
AudioClient::~AudioClient() {
}
void AudioClient::Init(dbus::ObjectProxy* cras_proxy) {
DCHECK(cras_proxy);
cras_proxy_ = cras_proxy;
}
void AudioClient::AddObserver(AudioObserver* observer) {
DCHECK(observer);
observers_.AddObserver(observer);
}
void AudioClient::RemoveObserver(AudioObserver* observer) {
DCHECK(observer);
observers_.RemoveObserver(observer);
}
void AudioClient::LoadInitialState() {
UpdateDevices();
UpdateNumActiveStreams();
}
void AudioClient::UpdateDevices() {
const bool old_headphone_jack_plugged = headphone_jack_plugged_;
const bool old_hdmi_active = hdmi_active_;
headphone_jack_plugged_ = false;
hdmi_active_ = false;
dbus::MethodCall method_call(cras::kCrasControlInterface, cras::kGetNodes);
scoped_ptr<dbus::Response> response(
cras_proxy_->CallMethodAndBlock(&method_call, kCrasDBusTimeoutMs));
if (!response)
return;
// At the outer level, there's a dictionary corresponding to each audio node.
dbus::MessageReader response_reader(response.get());
dbus::MessageReader node_reader(NULL);
while (response_reader.PopArray(&node_reader)) {
std::string type;
bool active = false;
// Iterate over the dictionary's entries.
dbus::MessageReader property_reader(NULL);
while (node_reader.PopDictEntry(&property_reader)) {
std::string key;
if (!property_reader.PopString(&key)) {
LOG(WARNING) << "Skipping dictionary entry with non-string key";
continue;
}
if (key == kTypeKey) {
if (!property_reader.PopVariantOfString(&type))
LOG(WARNING) << kTypeKey << " key has non-string value";
} else if (key == kActiveKey) {
if (!property_reader.PopVariantOfBool(&active))
LOG(WARNING) << kActiveKey << " key has non-bool value";
}
}
VLOG(1) << "Saw node: type=" << type << " active=" << active;
// The D-Bus interface doesn't return unplugged nodes.
if (type == kHeadphoneNodeType)
headphone_jack_plugged_ = true;
else if (type == kHdmiNodeType && active)
hdmi_active_ = true;
}
if (headphone_jack_plugged_ != old_headphone_jack_plugged ||
hdmi_active_ != old_hdmi_active) {
LOG(INFO) << "Updated audio devices: headphones "
<< (headphone_jack_plugged_ ? "" : "un") << "plugged, "
<< "HDMI " << (hdmi_active_ ? "" : "in") << "active";
}
}
void AudioClient::UpdateNumActiveStreams() {
dbus::MethodCall method_call(cras::kCrasControlInterface,
cras::kGetNumberOfActiveStreams);
scoped_ptr<dbus::Response> response(
cras_proxy_->CallMethodAndBlock(&method_call, kCrasDBusTimeoutMs));
int num_streams = 0;
if (response) {
dbus::MessageReader reader(response.get());
if (!reader.PopInt32(&num_streams))
LOG(WARNING) << "Unable to read " << cras::kGetNumberOfActiveStreams
<< " args";
} else {
LOG(WARNING) << cras::kGetNumberOfActiveStreams << " call failed";
}
const int old_num_streams = num_active_streams_;
num_active_streams_ = std::max(num_streams, 0);
if (num_active_streams_ && !old_num_streams) {
VLOG(1) << "Audio playback started";
FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioStateChange(true));
} else if (!num_active_streams_ && old_num_streams) {
VLOG(1) << "Audio playback stopped";
FOR_EACH_OBSERVER(AudioObserver, observers_, OnAudioStateChange(false));
}
}
void AudioClient::SetSuspended(bool suspended) {
dbus::MethodCall method_call(cras::kCrasControlInterface,
cras::kSetSuspendAudio);
dbus::MessageWriter writer(&method_call);
writer.AppendBool(suspended);
scoped_ptr<dbus::Response> response(
cras_proxy_->CallMethodAndBlock(&method_call, kCrasDBusTimeoutMs));
}
} // namespace system
} // namespace power_manager