blob: bce34203bded98a96ee4f29b43334101143719bc [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <sysexits.h>
#include <base/bind.h>
#include <base/command_line.h>
#include <base/macros.h>
#include <base/memory/weak_ptr.h>
#include <binderwrapper/binder_wrapper.h>
#include <brillo/binder_watcher.h>
#include <brillo/daemons/daemon.h>
#include <brillo/syslog_logging.h>
#include <libweaved/service.h>
#include "animation.h"
#include "binder_constants.h"
#include "brillo/examples/ledflasher/ILEDService.h"
using android::String16;
namespace {
const char kLedFlasherComponent[] = "ledflasher";
const char kLedFlasherTrait[] = "_ledflasher";
const char kBaseComponent[] = "base";
const char kBaseTrait[] = "base";
const char kLedComponentPrefix[] = "led";
const char kOnOffTrait[] = "onOff";
const char kLedInfoTrait[] = "_ledInfo";
} // anonymous namespace
using brillo::examples::ledflasher::ILEDService;
class Daemon final : public brillo::Daemon {
public:
Daemon() = default;
protected:
int OnInit() override;
private:
void OnWeaveServiceConnected(const std::weak_ptr<weaved::Service>& service);
void ConnectToLEDService();
void OnLEDServiceDisconnected();
void OnPairingInfoChanged(const weaved::Service::PairingInfo* pairing_info);
void CreateLedComponentsIfNeeded();
// Particular command handlers for various commands.
void OnSetConfig(size_t led_index, std::unique_ptr<weaved::Command> command);
void OnAnimate(std::unique_ptr<weaved::Command> command);
void OnIdentify(std::unique_ptr<weaved::Command> command);
// Helper methods to propagate device state changes to Buffet and hence to
// the cloud server or local clients.
void UpdateDeviceState();
void StartAnimation(const std::string& type, base::TimeDelta duration);
void StopAnimation();
std::weak_ptr<weaved::Service> weave_service_;
// Device state variables.
std::string status_{"idle"};
// LED service interface.
android::sp<ILEDService> led_service_;
// Current animation;
std::unique_ptr<Animation> animation_;
brillo::BinderWatcher binder_watcher_;
std::unique_ptr<weaved::Service::Subscription> weave_service_subscription_;
bool led_components_added_{false};
base::WeakPtrFactory<Daemon> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(Daemon);
};
int Daemon::OnInit() {
int return_code = brillo::Daemon::OnInit();
if (return_code != EX_OK)
return return_code;
android::BinderWrapper::Create();
if (!binder_watcher_.Init())
return EX_OSERR;
weave_service_subscription_ = weaved::Service::Connect(
brillo::MessageLoop::current(),
base::Bind(&Daemon::OnWeaveServiceConnected,
weak_ptr_factory_.GetWeakPtr()));
ConnectToLEDService();
LOG(INFO) << "Waiting for commands...";
return EX_OK;
}
void Daemon::OnWeaveServiceConnected(
const std::weak_ptr<weaved::Service>& service) {
LOG(INFO) << "Daemon::OnWeaveServiceConnected";
weave_service_ = service;
auto weave_service = weave_service_.lock();
if (!weave_service)
return;
weave_service->AddComponent(
kLedFlasherComponent, {kLedFlasherTrait}, nullptr);
weave_service->AddCommandHandler(
kLedFlasherComponent,
kLedFlasherTrait,
"animate",
base::Bind(&Daemon::OnAnimate, weak_ptr_factory_.GetWeakPtr()));
weave_service->AddCommandHandler(
kBaseComponent, kBaseTrait, "identify",
base::Bind(&Daemon::OnIdentify, weak_ptr_factory_.GetWeakPtr()));
weave_service->SetPairingInfoListener(
base::Bind(&Daemon::OnPairingInfoChanged,
weak_ptr_factory_.GetWeakPtr()));
led_components_added_ = false;
CreateLedComponentsIfNeeded();
UpdateDeviceState();
}
void Daemon::ConnectToLEDService() {
android::BinderWrapper* binder_wrapper = android::BinderWrapper::Get();
auto binder = binder_wrapper->GetService(ledservice::kBinderServiceName);
if (!binder.get()) {
brillo::MessageLoop::current()->PostDelayedTask(
base::Bind(&Daemon::ConnectToLEDService,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(1));
return;
}
binder_wrapper->RegisterForDeathNotifications(
binder,
base::Bind(&Daemon::OnLEDServiceDisconnected,
weak_ptr_factory_.GetWeakPtr()));
led_service_ = android::interface_cast<ILEDService>(binder);
CreateLedComponentsIfNeeded();
UpdateDeviceState();
}
void Daemon::CreateLedComponentsIfNeeded() {
if (led_components_added_ || !led_service_.get())
return;
auto weave_service = weave_service_.lock();
if (!weave_service)
return;
std::vector<bool> leds;
std::vector<String16> ledNames;
if (!led_service_->getAllLEDs(&leds).isOk())
return;
if (!led_service_->getAllLEDNames(&ledNames).isOk())
return;
for (size_t led_index = 0; led_index < leds.size(); led_index++) {
std::string led_name = android::String8{ledNames[led_index]}.string();
std::string component_name =
kLedComponentPrefix + std::to_string(led_index + 1);
if (weave_service->AddComponent(
component_name, {kOnOffTrait, kLedInfoTrait}, nullptr)) {
weave_service->AddCommandHandler(
component_name,
kOnOffTrait,
"setConfig",
base::Bind(
&Daemon::OnSetConfig, weak_ptr_factory_.GetWeakPtr(), led_index));
weave_service->SetStateProperty(
component_name,
kOnOffTrait,
"state",
*brillo::ToValue(leds[led_index] ? "on" : "off"),
nullptr);
weave_service->SetStateProperty(component_name,
kLedInfoTrait,
"name",
*brillo::ToValue(led_name),
nullptr);
}
}
led_components_added_ = true;
}
void Daemon::OnLEDServiceDisconnected() {
animation_.reset();
led_service_ = nullptr;
ConnectToLEDService();
}
void Daemon::OnSetConfig(size_t led_index,
std::unique_ptr<weaved::Command> command) {
if (!led_service_.get()) {
command->Abort("_system_error", "ledservice unavailable", nullptr);
return;
}
auto state = command->GetParameter<std::string>("state");
bool on = (state == "on");
android::binder::Status status = led_service_->setLED(led_index, on);
if (!status.isOk()) {
command->AbortWithCustomError(status, nullptr);
return;
}
if (animation_) {
animation_.reset();
status_ = "idle";
UpdateDeviceState();
}
auto weave_service = weave_service_.lock();
if (weave_service) {
std::string component_name =
kLedComponentPrefix + std::to_string(led_index + 1);
weave_service->SetStateProperty(component_name,
kOnOffTrait,
"state",
*brillo::ToValue(on ? "on" : "off"),
nullptr);
}
command->Complete({}, nullptr);
}
void Daemon::OnAnimate(std::unique_ptr<weaved::Command> command) {
if (!led_service_.get()) {
command->Abort("_system_error", "ledservice unavailable", nullptr);
return;
}
double duration = command->GetParameter<double>("duration");
if(duration <= 0.0) {
command->Abort("_invalid_parameter", "Invalid parameter value", nullptr);
return;
}
std::string type = command->GetParameter<std::string>("type");
StartAnimation(type, base::TimeDelta::FromSecondsD(duration));
command->Complete({}, nullptr);
}
void Daemon::OnIdentify(std::unique_ptr<weaved::Command> command) {
if (!led_service_.get()) {
command->Abort("_system_error", "ledservice unavailable", nullptr);
return;
}
StartAnimation("blink", base::TimeDelta::FromMilliseconds(500));
command->Complete({}, nullptr);
brillo::MessageLoop::current()->PostDelayedTask(
base::Bind(&Daemon::StopAnimation, weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromSeconds(2));
}
void Daemon::OnPairingInfoChanged(
const weaved::Service::PairingInfo* pairing_info) {
LOG(INFO) << "Daemon::OnPairingInfoChanged: " << pairing_info;
if (!pairing_info)
return StopAnimation();
StartAnimation("blink", base::TimeDelta::FromMilliseconds(500));
}
void Daemon::StartAnimation(const std::string& type, base::TimeDelta duration) {
animation_ = Animation::Create(led_service_, type, duration);
if (animation_) {
status_ = "animating";
animation_->Start();
} else {
status_ = "idle";
}
UpdateDeviceState();
}
void Daemon::StopAnimation() {
if (!animation_)
return;
animation_.reset();
status_ = "idle";
UpdateDeviceState();
}
void Daemon::UpdateDeviceState() {
auto weave_service = weave_service_.lock();
if (!weave_service)
return;
weave_service->SetStateProperty(kLedFlasherComponent,
kLedFlasherTrait,
"status",
*brillo::ToValue(status_),
nullptr);
}
int main(int argc, char* argv[]) {
base::CommandLine::Init(argc, argv);
brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader);
Daemon daemon;
return daemon.Run();
}