Skip to content
Snippets Groups Projects
ledflasher.cpp 7.00 KiB
/*
 * 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"

namespace {
const char kWeaveComponent[] = "ledflasher";
const char kWeaveTrait[] = "_ledflasher";
}  // 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();

  // Particular command handlers for various commands.
  void OnSet(std::unique_ptr<weaved::Command> command);
  void OnToggle(std::unique_ptr<weaved::Command> command);
  void OnAnimate(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();

  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_;

  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(kWeaveComponent, {kWeaveTrait}, nullptr);
  weave_service->AddCommandHandler(
      kWeaveComponent, kWeaveTrait, "set",
      base::Bind(&Daemon::OnSet, weak_ptr_factory_.GetWeakPtr()));
  weave_service->AddCommandHandler(
      kWeaveComponent, kWeaveTrait, "toggle",
      base::Bind(&Daemon::OnToggle, weak_ptr_factory_.GetWeakPtr()));
  weave_service->AddCommandHandler(
      kWeaveComponent, kWeaveTrait, "animate",
      base::Bind(&Daemon::OnAnimate, weak_ptr_factory_.GetWeakPtr()));

  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);
  UpdateDeviceState();
}

void Daemon::OnLEDServiceDisconnected() {
  animation_.reset();
  led_service_ = nullptr;
  ConnectToLEDService();
}

void Daemon::OnSet(std::unique_ptr<weaved::Command> command) {
  if (!led_service_.get()) {
    command->Abort("_system_error", "ledservice unavailable", nullptr);
    return;
  }

  int index = command->GetParameter<int>("led");
  if(index < 1 || index > 4) {
    command->Abort("_invalid_parameter", "Invalid parameter value", nullptr);
    return;
  }
  bool on = command->GetParameter<bool>("on");
  android::binder::Status status = led_service_->setLED(index - 1, on);
  if (!status.isOk()) {
    command->AbortWithCustomError(status, nullptr);
    return;
  }
  animation_.reset();
  status_ = "idle";
  UpdateDeviceState();
  command->Complete({}, nullptr);
}

void Daemon::OnToggle(std::unique_ptr<weaved::Command> command) {
  if (!led_service_.get()) {
    command->Abort("_system_error", "ledservice unavailable", nullptr);
    return;
  }

  int index = command->GetParameter<int>("led");
  if(index < 1 || index > 4) {
    command->Abort("_invalid_parameter", "Invalid parameter value", nullptr);
    return;
  }
  index--;
  bool on = false;
  android::binder::Status status = led_service_->getLED(index, &on);
  if (status.isOk())
    status = led_service_->setLED(index, !on);
  if(!status.isOk()) {
    command->AbortWithCustomError(status, nullptr);
    return;
  }
  animation_.reset();
  status_ = "idle";
  UpdateDeviceState();
  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");
  animation_ = Animation::Create(led_service_, type,
                                 base::TimeDelta::FromSecondsD(duration));
  if (animation_) {
    status_ = "animating";
    animation_->Start();
  } else {
    status_ = "idle";
  }
  UpdateDeviceState();
  command->Complete({}, nullptr);
}

void Daemon::UpdateDeviceState() {
  if (!led_service_.get())
    return;

  std::vector<bool> leds;
  if (!led_service_->getAllLEDs(&leds).isOk())
    return;

  auto weave_service = weave_service_.lock();
  if (!weave_service)
    return;

  brillo::VariantDictionary state_change{
    {"_ledflasher.status", status_},
    {"_ledflasher.leds", leds},
  };
  weave_service->SetStateProperties(kWeaveComponent, state_change, nullptr);
}

int main(int argc, char* argv[]) {
  base::CommandLine::Init(argc, argv);
  brillo::InitLog(brillo::kLogToSyslog | brillo::kLogHeader);
  Daemon daemon;
  return daemon.Run();
}