blob: df967397c977c655610c761095482bc79309d5be [file] [log] [blame]
// Copyright (C) 2007-2021 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// this file implements the support of the new 'hardware control'
// qemud communication channel, which is used by libs/hardware on
// the system image to communicate with the emulator program for
// emulating the following:
//
// - power management
// - led(s) brightness
// - vibrator
// - flashlight
//
#include "android/hw-control.h"
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "aemu/base/utils/stream.h"
#include "android/emulation/android_qemud.h"
#include "android/utils/debug.h"
#define D(...) VERBOSE_PRINT(hw_control, __VA_ARGS__)
/* define T_ACTIVE to 1 to debug transport communications */
#define T_ACTIVE 0
#if T_ACTIVE
#define T(...) VERBOSE_PRINT(hw_control, __VA_ARGS__)
#else
#define T(...) ((void)0)
#endif
namespace {
struct HwControlState {
// Button brightness. Ranged from 0-255.
uint8_t button_brightness = 0;
// Keyboard brightness. Ranged from 0-255.
uint8_t keyboard_brightness = 0;
// Screen LCD brightness. Ranged from 0-255.
uint8_t lcd_brightness;
};
QemudClient* _hw_control_qemud_connect(void* opaque,
QemudService* service,
int channel,
const char* client_param);
class HwControl;
class HwControlClient {
public:
explicit HwControlClient(HwControl* hwControl) : mHwControl(hwControl) {}
static HwControlClient* create(HwControl* hwControl);
static void close(void* opaque);
static int load(Stream* stream, QemudClient* qemudClient, void* opaque);
static void save(Stream* stream, QemudClient* qemudClient, void* opaque);
static void recv(void* opaque,
uint8_t* msg,
int msglen,
QemudClient* qemudClient);
void send(const uint8_t* msg, size_t len) const;
void setQemudClient(QemudClient* client) { mQemudClient = client; }
private:
HwControl* mHwControl = nullptr;
QemudClient* mQemudClient = nullptr;
};
class HwControl {
public:
explicit HwControl(void* client = nullptr,
AndroidHwControlFuncs clientFuncs = {});
void setClient(void* client, AndroidHwControlFuncs clientFuncs);
void onQuery(std::string_view msg, HwControlClient* client);
void setBrightness(const std::string& name, uint8_t value);
void setBrightness(std::string_view args);
uint32_t getBrightness(std::string_view name) const;
private:
QemudService* mService = nullptr;
HwControlState mHwState = {};
std::vector<std::pair<void*, AndroidHwControlFuncs>> mClientCallbacks;
std::unique_ptr<std::mutex> mClientCallbacksLock;
};
HwControl::HwControl(void* client, AndroidHwControlFuncs clientFuncs)
: mClientCallbacksLock(std::make_unique<std::mutex>()) {
mService = qemud_service_register("hw-control", 0, this,
_hw_control_qemud_connect, NULL, NULL);
if (client != nullptr)
mClientCallbacks.push_back({client, clientFuncs});
}
void HwControl::setClient(void* client, AndroidHwControlFuncs clientFuncs) {
std::lock_guard<std::mutex> guard(*mClientCallbacksLock);
mClientCallbacks.push_back({client, clientFuncs});
}
void HwControl::onQuery(std::string_view msg, HwControlClient* client) {
T("%s: query %4lu '%.*s'", __FUNCTION__, msg.size(),
static_cast<int>(msg.size()), msg.data());
constexpr std::string_view kSetBrightnessPattern =
"power:light:brightness:";
if (msg.find(kSetBrightnessPattern) == 0) {
std::string_view args = msg.substr(kSetBrightnessPattern.length());
setBrightness(args);
return;
}
constexpr std::string_view kGetBrightnessPattern =
"power:light:get-brightness:";
if (msg.find(kGetBrightnessPattern) == 0) {
std::string_view args = msg.substr(kGetBrightnessPattern.length());
uint32_t brightness = getBrightness(args);
char out[4];
snprintf(out, sizeof(out), "%03d", brightness);
client->send(reinterpret_cast<const uint8_t*>(out), strlen(out) + 1);
return;
}
D("%s: query not matched", __func__);
}
void HwControl::setBrightness(const std::string& name, uint8_t value) {
T("%s: name=%s value=%u", __func__, name.c_str(), value);
if (name == "lcd_backlight") {
mHwState.lcd_brightness = value;
} else if (name == "keyboard_backlight") {
mHwState.keyboard_brightness = value;
} else if (name == "button_backlight") {
mHwState.button_brightness = value;
} else {
D("%s: invalid power:light:brightness light name", __func__);
return;
}
std::lock_guard<std::mutex> guard(*mClientCallbacksLock);
for (const auto [client, funcs] : mClientCallbacks) {
if (funcs.light_brightness != nullptr) {
funcs.light_brightness(client, name.c_str(), value);
}
}
}
void HwControl::setBrightness(std::string_view args) {
if (auto colon = args.find(':'); colon != std::string_view::npos) {
std::string name(args.substr(0, colon));
std::string valueStr(args.substr(colon + 1));
errno = 0;
uint64_t value = strtoul(valueStr.c_str(), nullptr, /*base=*/10);
if (errno != 0) {
D("%s: invalid power:light:brightness value: \"%s\", errno = "
"%d",
__func__, valueStr.c_str(), errno);
return;
}
if (value > UINT8_MAX) {
D("%s: brightness value out of range: %lu", __func__, value);
return;
}
setBrightness(name, static_cast<uint8_t>(value));
} else {
D("%s: invalid power:light:brightness command", __func__);
}
}
uint32_t HwControl::getBrightness(std::string_view name) const {
T("%s: name=%s", __func__, std::string(name).c_str());
if (name == "lcd_backlight") {
return mHwState.lcd_brightness;
}
if (name == "keyboard_backlight") {
return mHwState.keyboard_brightness;
}
if (name == "button_backlight") {
return mHwState.button_brightness;
}
D("%s: invalid power:light:get-brightness light name: %s", __func__,
std::string(name).c_str());
return 0;
}
// static
HwControlClient* HwControlClient::create(HwControl* hwControl) {
return new HwControlClient(hwControl);
}
// static
void HwControlClient::close(void* opaque) {
auto* client = static_cast<HwControlClient*>(opaque);
delete client;
}
// static
int HwControlClient::load(Stream* stream,
QemudClient* qemudClient,
void* opaque) {
auto* client = static_cast<HwControlClient*>(opaque);
stream_put_be32(stream, client->mHwControl->getBrightness("lcd_backlight"));
stream_put_be32(stream,
client->mHwControl->getBrightness("keyboard_backlight"));
stream_put_be32(stream,
client->mHwControl->getBrightness("button_backlight"));
return 0;
}
// static
void HwControlClient::save(Stream* stream,
QemudClient* qemudClient,
void* opaque) {
auto* client = static_cast<HwControlClient*>(opaque);
client->mHwControl->setBrightness("lcd_backlight", stream_get_be32(stream));
client->mHwControl->setBrightness("keyboard_backlight",
stream_get_be32(stream));
client->mHwControl->setBrightness("button_backlight",
stream_get_be32(stream));
}
// static
void HwControlClient::recv(void* opaque,
uint8_t* msg,
int msglen,
QemudClient* qemudClient) {
T("%s: hw-control query: %.*s", __FUNCTION__, msglen, msg);
auto* client = static_cast<HwControlClient*>(opaque);
client->mHwControl->onQuery(
std::string_view(reinterpret_cast<const char*>(msg), msglen),
client);
}
void HwControlClient::send(const uint8_t* msg, size_t len) const {
T("%s: hw-control query: %.*s", __FUNCTION__, static_cast<int>(len),
reinterpret_cast<const char*>(msg));
qemud_client_send(mQemudClient, msg, static_cast<int>(len));
}
std::optional<HwControl> sHwControl;
HwControl& getHwControl() {
if (!sHwControl) {
android_hw_control_init();
}
return *sHwControl;
}
// Called when a qemud client connects to the service
QemudClient* _hw_control_qemud_connect(void* opaque,
QemudService* service,
int channel,
const char* client_param) {
QemudClient* qemudClient;
HwControlClient* client = HwControlClient::create(&getHwControl());
qemudClient =
qemud_client_new(service, channel, client_param, client,
HwControlClient::recv, HwControlClient::close,
HwControlClient::save, HwControlClient::load);
client->setQemudClient(qemudClient);
qemud_client_set_framing(qemudClient, 1);
return qemudClient;
}
} // namespace
void android_hw_control_init(void) {
sHwControl = std::make_optional<HwControl>();
D("%s: hw-control qemud handler initialized", __FUNCTION__);
}
void android_hw_control_set(void* opaque, const AndroidHwControlFuncs* funcs) {
getHwControl().setClient(opaque, *funcs);
}
void android_hw_control_set_brightness(const char* light_name, uint32_t value) {
getHwControl().setBrightness(std::string(light_name), value);
}
uint32_t android_hw_control_get_brightness(const char* light_name) {
return getHwControl().getBrightness(light_name);
}