Add the support of lights HAL

Bug: 230288032
Test: atest VtsHalLightTargetTest
Change-Id: I076e76f589a202833095b14a839500a26c35e9b3
Signed-off-by: Chungjui Fan <chungjuifan@google.com>
This commit is contained in:
Chungjui Fan 2022-06-08 15:02:49 +08:00
parent fdca91b2ac
commit 0d44479f24
6 changed files with 348 additions and 0 deletions

23
lights/Android.bp Normal file
View file

@ -0,0 +1,23 @@
package {
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "//device/google/tangorpro:device_google_tangorpro_license"
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["//device/google/tangorpro:device_google_tangorpro_license"],
}
cc_binary {
name: "android.hardware.lights-service.tangorpro",
relative_install_path: "hw",
init_rc: ["android.hardware.lights-service.tangorpro.rc"],
vintf_fragments: ["android.hardware.lights-service.tangorpro.xml"],
vendor: true,
shared_libs: [
"libbase",
"libbinder_ndk",
"android.hardware.light-V2-ndk",
],
srcs: ["Lights.cpp", "led_lut_calibrator.cpp"],
}

151
lights/Lights.cpp Normal file
View file

@ -0,0 +1,151 @@
/*
* Copyright (C) 2022 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 <aidl/android/hardware/light/BnLights.h>
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <fcntl.h>
#include "led_lut_calibrator.h"
using ::aidl::android::hardware::light::BnLights;
using ::aidl::android::hardware::light::HwLight;
using ::aidl::android::hardware::light::HwLightState;
using ::aidl::android::hardware::light::ILights;
using ::aidl::android::hardware::light::LightType;
using ::ndk::ScopedAStatus;
using ::ndk::SharedRefBase;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
char const *const GREEN_LED_FILE = "/sys/class/leds/green/brightness";
enum { ARGB_ON_IN_DAY = 0x0000ff00, ARGB_ON_IN_NIGHT = 0x00008000, ARGB_OFF = 0x00000000 };
enum { DAY = 4095, NIGHT = 0 };
static int sys_write_int(int fd, int value) {
char buffer[16];
size_t bytes;
ssize_t amount;
bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
if (bytes >= sizeof(buffer)) {
return -EINVAL;
}
amount = write(fd, buffer, bytes);
return amount == -1 ? -errno : 0;
}
class Lights : public BnLights {
private:
std::vector<HwLight> availableLights;
LedLutCalibrator calibrator;
void addLight(LightType const type, int const ordinal) {
HwLight light{};
light.id = availableLights.size();
light.type = type;
light.ordinal = ordinal;
availableLights.emplace_back(light);
}
void writeLed(const char *path, int color) {
int fd = open(path, O_WRONLY);
if (fd < 0) {
LOG(ERROR) << "Failed to open LED device " << path << strerror(errno);
return;
}
sys_write_int(fd, color);
close(fd);
}
public:
Lights() : BnLights() {
pthread_mutex_init(&g_lock, NULL);
addLight(LightType::BACKLIGHT, 0);
addLight(LightType::KEYBOARD, 0);
addLight(LightType::BUTTONS, 0);
addLight(LightType::BATTERY, 0);
addLight(LightType::NOTIFICATIONS, 0);
addLight(LightType::ATTENTION, 0);
addLight(LightType::BLUETOOTH, 0);
addLight(LightType::WIFI, 0);
addLight(LightType::MICROPHONE, 0);
addLight(LightType::CAMERA, 0);
}
ScopedAStatus setLightState(int id, const HwLightState &state) override {
if (!(0 <= id && id < availableLights.size())) {
LOG(ERROR) << "Light id " << (int32_t)id << " does not exist.";
return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
HwLight const &light = availableLights[id];
int cal_color = 0;
switch (light.type) {
case LightType::CAMERA:
if (state.color == ARGB_ON_IN_DAY) {
cal_color = calibrator.GetByColorIntensity("green", DAY);
} else if (state.color == ARGB_ON_IN_NIGHT) {
cal_color = calibrator.GetByColorIntensity("green", NIGHT);
} else if (state.color == ARGB_OFF) {
cal_color = 0;
} else {
goto setLightState_end;
}
if (cal_color < 0) {
goto setLightState_end;
}
pthread_mutex_lock(&g_lock);
writeLed(GREEN_LED_FILE, cal_color);
pthread_mutex_unlock(&g_lock);
break;
default:
break;
}
setLightState_end:
return ScopedAStatus::ok();
}
ScopedAStatus getLights(std::vector<HwLight> *lights) override {
for (auto i = availableLights.begin(); i != availableLights.end(); i++) {
lights->push_back(*i);
}
return ScopedAStatus::ok();
}
};
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Lights> light = SharedRefBase::make<Lights>();
const std::string instance = std::string() + ILights::descriptor + "/default";
binder_status_t status = AServiceManager_addService(light->asBinder().get(), instance.c_str());
if (status != STATUS_OK) {
LOG(ERROR) << "Failed to register" << instance;
}
ABinderProcess_joinThreadPool();
return -1; // should not reach
}

View file

@ -0,0 +1,11 @@
service vendor.lights-tangotron /vendor/bin/hw/android.hardware.lights-service.tangorpro
interface aidl android.hardware.light.ILights/default
class hal
user system
group system
# shutting off lights while powering-off
shutdown critical
on post-fs
chown system system /sys/class/leds/green/brightness
chmod 664 /sys/class/leds/green/brightness

View file

@ -0,0 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.light</name>
<fqname>ILights/default</fqname>
<version>2</version>
</hal>
</manifest>

View file

@ -0,0 +1,117 @@
/*
* Copyright (C) 2022 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 "led_lut_calibrator.h"
#include <android-base/logging.h>
#include <fcntl.h>
#include <iostream>
#include <sstream>
const char kCalibrationPath[] = "/mnt/vendor/persist/led/led_calibration_LUT.txt";
LedLutCalibrator::LedLutCalibrator() {
if (!ReadCalibrationTable()) {
LOG(ERROR) << "Failed to read calibration table";
cal_table_.clear();
}
}
int LedLutCalibrator::GetByColorIntensity(const std::string &color, int intensity) const {
std::string key = MakeLutKey(color, intensity);
const auto cal_data = cal_table_.find(key);
if (cal_data == cal_table_.end()) {
LOG(ERROR) << "Failed to get calibration data";
return -1;
}
return cal_data->second;
}
bool LedLutCalibrator::ReadCalibrationTable() {
int fd;
int bytes;
bool ret = true;
std::vector<uint8_t> buffer;
buffer.resize(512);
fd = open(kCalibrationPath, O_RDONLY);
if (fd < 0) {
LOG(ERROR) << "Failed to open " << kCalibrationPath;
ret = false;
goto ReadCalibrationTable_err;
}
bytes = read(fd, buffer.data(), buffer.size());
if (bytes == -1) {
LOG(ERROR) << "Failed to read " << kCalibrationPath;
ret = false;
goto ReadCalibrationTable_err;
}
if (!ParseBlock(&buffer)) {
LOG(ERROR) << "Failed to parse calibration table ";
ret = false;
goto ReadCalibrationTable_err;
}
ReadCalibrationTable_err:
if (fd != -1)
close(fd);
return ret;
}
bool LedLutCalibrator::ParseBlock(const std::vector<uint8_t> *table) {
std::string str(table->begin(), table->end());
auto result = std::vector<std::string>{};
auto ss = std::stringstream{str};
for (std::string line; std::getline(ss, line, '\n');) {
size_t start;
size_t end = 0;
std::vector<std::string> colon_separated;
while ((start = line.find_first_not_of(':', end)) != std::string::npos) {
end = line.find(':', start);
if (end == std::string::npos) {
end = line.size();
}
colon_separated.push_back(line.substr(start, end - start));
}
if (colon_separated.size() != 3) {
continue;
}
std::string color_name = colon_separated[0];
uint16_t intensity = atoi(colon_separated[1].c_str());
uint16_t pwm = atoi(colon_separated[2].c_str());
std::cout << color_name << intensity << pwm << std::endl;
std::string key = MakeLutKey(color_name, intensity);
cal_table_[key] = pwm;
}
return true;
}
std::string LedLutCalibrator::MakeLutKey(const std::string &color, int intensity) const {
std::string intensity_str = std::to_string(intensity);
std::string terms{color};
terms.append(intensity_str);
return terms;
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2022 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.
*/
#ifndef GOOGLE_TANGOTRON_LIGHTS_LED_LUT_CALIBRATOR_H_
#define GOOGLE_TANGOTRON_LIGHTS_LED_LUT_CALIBRATOR_H_
#include <string>
#include <unordered_map>
#include <vector>
class LedLutCalibrator {
public:
LedLutCalibrator();
LedLutCalibrator &operator=(const LedLutCalibrator &) = delete;
~LedLutCalibrator() = default;
int GetByColorIntensity(const std::string &color, int intensity) const;
private:
bool ReadCalibrationTable();
bool ParseBlock(const std::vector<uint8_t> *table);
std::string MakeLutKey(const std::string &color, int intensity) const;
std::unordered_map<std::string, int> cal_table_;
};
#endif // GOOGLE_TANGOTRON_LIGHTS_LED_LUT_CALIBRATOR_H_