Merge "Add AIDL Lights HAL to support multiple lights per type"

This commit is contained in:
Ivailo Karamanolev
2020-01-23 16:05:07 +00:00
committed by Android (Google) Code Review
18 changed files with 725 additions and 32 deletions

View File

@@ -289,6 +289,13 @@
<instance>default</instance>
</interface>
</hal>
<hal format="aidl" optional="true">
<name>android.hardware.light</name>
<interface>
<name>ILights</name>
<instance>default</instance>
</interface>
</hal>
<hal format="hidl" optional="true">
<name>android.hardware.media.c2</name>
<version>1.0-1</version>

18
light/aidl/Android.bp Normal file
View File

@@ -0,0 +1,18 @@
aidl_interface {
name: "android.hardware.light",
vendor_available: true,
srcs: [
"android/hardware/light/*.aidl",
],
stability: "vintf",
backend: {
java: {
platform_apis: true,
},
ndk: {
vndk: {
enabled: true,
},
},
},
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.light;
@VintfStability
enum BrightnessMode {
/**
* Light brightness is managed by a user setting.
*/
USER = 0,
/**
* Light brightness is managed by a light sensor. This is typically used
* to control the display backlight, but not limited to it. HALs and
* hardware implementations are free to support sensor for other lights or
* none whatsoever.
*/
SENSOR = 1,
/**
* Use a low-persistence mode for display backlights, where the pixel
* color transition times are lowered.
*
* When set, the device driver must switch to a mode optimized for low display
* persistence that is intended to be used when the device is being treated as a
* head mounted display (HMD). The actual display brightness in this mode is
* implementation dependent, and any value set for color in LightState may be
* overridden by the HAL implementation.
*
* For an optimal HMD viewing experience, the display must meet the following
* criteria in this mode:
* - Gray-to-Gray, White-to-Black, and Black-to-White switching time must be ≤ 3 ms.
* - The display must support low-persistence with ≤ 3.5 ms persistence.
* Persistence is defined as the amount of time for which a pixel is
* emitting light for a single frame.
* - Any "smart panel" or other frame buffering options that increase display
* latency are disabled.
* - Display brightness is set so that the display is still visible to the user
* under normal indoor lighting.
* - The display must update at 60 Hz at least, but higher refresh rates are
* recommended for low latency.
*
*/
LOW_PERSISTENCE = 2,
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.light;
@VintfStability
enum FlashMode {
/**
* Keep the light steady on or off.
*/
NONE = 0,
/**
* Flash the light at specified rate, potentially using a software-based
* implementation.
*/
TIMED = 1,
/**
* Flash the light using hardware flashing support. This may or may not
* support a user-defined flashing rate or other features.
*/
HARDWARE = 2,
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 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.
*/
package android.hardware.light;
import android.hardware.light.LightType;
/**
* A description of a single light. Multiple lights can map to the same physical
* LED. Separate physical LEDs are always represented by separate instances.
*/
@VintfStability
parcelable HwLight {
/**
* Integer ID used for controlling this light
*/
int id;
/**
* For a group of lights of the same logical type, sorting by ordinal should
* be give their physical order. No other meaning is carried by it.
*/
int ordinal;
/**
* Logical type use of this light.
*/
LightType type;
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.light;
import android.hardware.light.BrightnessMode;
import android.hardware.light.FlashMode;
/**
* The parameters that can be set for a given light.
*
* Not all lights must support all parameters. If you
* can do something backward-compatible, do it.
*/
@VintfStability
parcelable HwLightState {
/**
* The color of the LED in ARGB.
*
* The implementation of this in the HAL and hardware is a best-effort one.
* - If a light can only do red or green and blue is requested, green
* should be shown.
* - If only a brightness ramp is supported, then this formula applies:
* unsigned char brightness = ((77*((color>>16)&0x00ff))
* + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;
* - If only on and off are supported, 0 is off, anything else is on.
*
* The high byte should be ignored. Callers should set it to 0xff (which
* would correspond to 255 alpha).
*/
int color;
/**
* To flash the light at a given rate, set flashMode to FLASH_TIMED.
*/
FlashMode flashMode;
/**
* flashOnMs should be set to the number of milliseconds to turn the
* light on, before it's turned off.
*/
int flashOnMs;
/**
* flashOfMs should be set to the number of milliseconds to turn the
* light off, before it's turned back on.
*/
int flashOffMs;
BrightnessMode brightnessMode;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.light;
import android.hardware.light.HwLightState;
import android.hardware.light.HwLight;
/**
* Allows controlling logical lights/indicators, mapped to LEDs in a
* hardware-specific manner by the HAL implementation.
*/
@VintfStability
interface ILights {
/**
* Set light identified by id to the provided state.
*
* If control over an invalid light is requested, this method exists with
* EX_UNSUPPORTED_OPERATION. Control over supported lights is done on a
* device-specific best-effort basis and unsupported sub-features will not
* be reported.
*
* @param id ID of logical light to set as returned by getLights()
* @param state describes what the light should look like.
*/
void setLightState(in int id, in HwLightState state);
/**
* Discover what lights are supported by the HAL implementation.
*
* @return List of available lights
*/
HwLight[] getLights();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2020 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.
*/
package android.hardware.light;
/**
* These light IDs correspond to logical lights, not physical.
* So for example, if your INDICATOR light is in line with your
* BUTTONS, it might make sense to also light the INDICATOR
* light to a reasonable color when the BUTTONS are lit.
*/
@VintfStability
enum LightType {
BACKLIGHT = 0,
KEYBOARD = 1,
BUTTONS = 2,
BATTERY = 3,
NOTIFICATIONS = 4,
ATTENTION = 5,
BLUETOOTH = 6,
WIFI = 7,
MICROPHONE = 8,
}

View File

@@ -0,0 +1,16 @@
cc_binary {
name: "android.hardware.lights-service.example",
relative_install_path: "hw",
init_rc: ["lights-default.rc"],
vintf_fragments: ["lights-default.xml"],
vendor: true,
shared_libs: [
"libbase",
"libbinder_ndk",
"android.hardware.light-ndk_platform",
],
srcs: [
"Lights.cpp",
"main.cpp",
],
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 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 "Lights.h"
#include <android-base/logging.h>
namespace aidl {
namespace android {
namespace hardware {
namespace light {
ndk::ScopedAStatus Lights::setLightState(int id, const HwLightState& state) {
LOG(INFO) << "Lights setting state for id=" << id << " to color " << std::hex << state.color;
return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
}
ndk::ScopedAStatus Lights::getLights(std::vector<HwLight>* /*lights*/) {
LOG(INFO) << "Lights reporting supported lights";
return ndk::ScopedAStatus::ok();
}
} // namespace light
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <aidl/android/hardware/light/BnLights.h>
namespace aidl {
namespace android {
namespace hardware {
namespace light {
// Default implementation that reports no supported lights.
class Lights : public BnLights {
ndk::ScopedAStatus setLightState(int id, const HwLightState& state) override;
ndk::ScopedAStatus getLights(std::vector<HwLight>* types) override;
};
} // namespace light
} // namespace hardware
} // namespace android
} // namespace aidl

View File

@@ -0,0 +1,5 @@
service vendor.light-default /vendor/bin/hw/android.hardware.lights-service.example
class hal
user nobody
group nobody
shutdown critical

View File

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

View File

@@ -0,0 +1,35 @@
/*
* Copyright (C) 2020 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 "Lights.h"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
using ::aidl::android::hardware::light::Lights;
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Lights> lights = ndk::SharedRefBase::make<Lights>();
const std::string instance = std::string() + Lights::descriptor + "/default";
binder_status_t status = AServiceManager_addService(lights->asBinder().get(), instance.c_str());
CHECK(status == STATUS_OK);
ABinderProcess_joinThreadPool();
return EXIT_FAILURE; // should not reached
}

View File

@@ -0,0 +1,35 @@
//
// Copyright (C) 2020 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.
//
cc_test {
name: "VtsHalLightTargetTest",
defaults: [
"VtsHalTargetTestDefaults",
"use_libaidlvintf_gtest_helper_static",
],
srcs: [
"VtsHalLightTargetTest.cpp",
],
shared_libs: [
"libbinder",
],
static_libs: [
"android.hardware.light-cpp",
],
test_suites: [
"vts-core",
],
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (C) 2020 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.
*/
#define LOG_TAG "light_aidl_hal_test"
#include <aidl/Gtest.h>
#include <aidl/Vintf.h>
#include <android-base/logging.h>
#include <android/hardware/light/ILights.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
#include <gtest/gtest.h>
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#include <unistd.h>
#include <set>
using android::ProcessState;
using android::sp;
using android::String16;
using android::binder::Status;
using android::hardware::hidl_vec;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::light::BrightnessMode;
using android::hardware::light::FlashMode;
using android::hardware::light::HwLight;
using android::hardware::light::HwLightState;
using android::hardware::light::ILights;
using android::hardware::light::LightType;
#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
const std::set<LightType> kAllTypes{android::enum_range<LightType>().begin(),
android::enum_range<LightType>().end()};
class LightsAidl : public testing::TestWithParam<std::string> {
public:
virtual void SetUp() override {
lights = android::waitForDeclaredService<ILights>(String16(GetParam().c_str()));
ASSERT_NE(lights, nullptr);
ASSERT_TRUE(lights->getLights(&supportedLights).isOk());
}
sp<ILights> lights;
std::vector<HwLight> supportedLights;
virtual void TearDown() override {
for (const HwLight& light : supportedLights) {
HwLightState off;
off.color = 0x00000000;
off.flashMode = FlashMode::NONE;
off.brightnessMode = BrightnessMode::USER;
EXPECT_TRUE(lights->setLightState(light.id, off).isOk());
}
// must leave the device in a useable condition
for (const HwLight& light : supportedLights) {
if (light.type == LightType::BACKLIGHT) {
HwLightState backlightOn;
backlightOn.color = 0xFFFFFFFF;
backlightOn.flashMode = FlashMode::TIMED;
backlightOn.brightnessMode = BrightnessMode::USER;
EXPECT_TRUE(lights->setLightState(light.id, backlightOn).isOk());
}
}
}
};
/**
* Ensure all reported lights actually work.
*/
TEST_P(LightsAidl, TestSupported) {
HwLightState whiteFlashing;
whiteFlashing.color = 0xFFFFFFFF;
whiteFlashing.flashMode = FlashMode::TIMED;
whiteFlashing.flashOnMs = 100;
whiteFlashing.flashOffMs = 50;
whiteFlashing.brightnessMode = BrightnessMode::USER;
for (const HwLight& light : supportedLights) {
EXPECT_TRUE(lights->setLightState(light.id, whiteFlashing).isOk());
}
}
/**
* Ensure all reported lights have one of the supported types.
*/
TEST_P(LightsAidl, TestSupportedLightTypes) {
for (const HwLight& light : supportedLights) {
EXPECT_TRUE(kAllTypes.find(light.type) != kAllTypes.end());
}
}
/**
* Ensure all lights have a unique id.
*/
TEST_P(LightsAidl, TestUniqueIds) {
std::set<int> ids;
for (const HwLight& light : supportedLights) {
EXPECT_TRUE(ids.find(light.id) == ids.end());
ids.insert(light.id);
}
}
/**
* Ensure all lights have a unique ordinal for a given type.
*/
TEST_P(LightsAidl, TestUniqueOrdinalsForType) {
std::map<int, std::set<int>> ordinalsByType;
for (const HwLight& light : supportedLights) {
auto& ordinals = ordinalsByType[(int)light.type];
EXPECT_TRUE(ordinals.find(light.ordinal) == ordinals.end());
ordinals.insert(light.ordinal);
}
}
/**
* Ensure EX_UNSUPPORTED_OPERATION is returned if LOW_PERSISTENCE is not supported.
*/
TEST_P(LightsAidl, TestLowPersistence) {
HwLightState lowPersistence;
lowPersistence.color = 0xFF123456;
lowPersistence.flashMode = FlashMode::TIMED;
lowPersistence.flashOnMs = 100;
lowPersistence.flashOffMs = 50;
lowPersistence.brightnessMode = BrightnessMode::LOW_PERSISTENCE;
for (const HwLight& light : supportedLights) {
Status status = lights->setLightState(light.id, lowPersistence);
EXPECT_TRUE(status.isOk() || Status::EX_UNSUPPORTED_OPERATION == status.exceptionCode());
}
}
/**
* Ensure EX_UNSUPPORTED_OPERATION is returns for an invalid light id.
*/
TEST_P(LightsAidl, TestInvalidLightIdUnsupported) {
int maxId = INT_MIN;
for (const HwLight& light : supportedLights) {
maxId = std::max(maxId, light.id);
}
Status status = lights->setLightState(maxId + 1, HwLightState());
EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
}
INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl,
testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)),
android::PrintInstanceNameToString);
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
ProcessState::self()->setThreadPoolMaxThreadCount(1);
ProcessState::self()->startThreadPool();
return RUN_ALL_TESTS();
}

View File

@@ -23,7 +23,11 @@ cc_binary {
shared_libs: [
"android.hardware.light@2.0",
"libbase",
"libbinder",
"libhidlbase",
"libutils",
],
static_libs: [
"android.hardware.light-cpp",
],
}

View File

@@ -19,34 +19,23 @@
#include <android-base/logging.h>
#include <android/hardware/light/2.0/ILight.h>
#include <android/hardware/light/ILights.h>
#include <binder/IServiceManager.h>
using android::sp;
using android::waitForVintfService;
using android::binder::Status;
using android::hardware::hidl_vec;
namespace V2_0 = android::hardware::light::V2_0;
namespace aidl = android::hardware::light;
void error(const std::string& msg) {
LOG(ERROR) << msg;
std::cerr << msg << std::endl;
}
int main(int argc, char* argv[]) {
using ::android::hardware::hidl_vec;
using ::android::hardware::light::V2_0::Brightness;
using ::android::hardware::light::V2_0::Flash;
using ::android::hardware::light::V2_0::ILight;
using ::android::hardware::light::V2_0::LightState;
using ::android::hardware::light::V2_0::Status;
using ::android::hardware::light::V2_0::Type;
using ::android::sp;
sp<ILight> service = ILight::getService();
if (service == nullptr) {
error("Could not retrieve light service.");
return -1;
}
static LightState off = {
.color = 0u,
.flashMode = Flash::NONE,
.brightnessMode = Brightness::USER,
};
int parseArgs(int argc, char* argv[], unsigned int* color) {
if (argc > 2) {
error("Usage: blank_screen [color]");
return -1;
@@ -54,25 +43,78 @@ int main(int argc, char* argv[]) {
if (argc > 1) {
char* col_ptr;
unsigned int col_new;
col_new = strtoul(argv[1], &col_ptr, 0);
*color = strtoul(argv[1], &col_ptr, 0);
if (*col_ptr != '\0') {
error("Failed to convert " + std::string(argv[1]) + " to number");
return -1;
}
off.color = col_new;
return 0;
}
service->getSupportedTypes([&](const hidl_vec<Type>& types) {
for (Type type : types) {
Status ret = service->setLight(type, off);
if (ret != Status::SUCCESS) {
error("Failed to shut off screen for type " +
*color = 0u;
return 0;
}
void setToColorAidl(sp<aidl::ILights> hal, unsigned int color) {
static aidl::HwLightState off;
off.color = color;
off.flashMode = aidl::FlashMode::NONE;
off.brightnessMode = aidl::BrightnessMode::USER;
std::vector<aidl::HwLight> lights;
Status status = hal->getLights(&lights);
if (!status.isOk()) {
error("Failed to list lights");
return;
}
for (auto light : lights) {
Status setStatus = hal->setLightState(light.id, off);
if (!setStatus.isOk()) {
error("Failed to shut off light id " + std::to_string(light.id));
}
}
}
void setToColorHidl(sp<V2_0::ILight> hal, unsigned int color) {
static V2_0::LightState off = {
.color = color,
.flashMode = V2_0::Flash::NONE,
.brightnessMode = V2_0::Brightness::USER,
};
hal->getSupportedTypes([&](const hidl_vec<V2_0::Type>& types) {
for (auto type : types) {
V2_0::Status ret = hal->setLight(type, off);
if (ret != V2_0::Status::SUCCESS) {
error("Failed to shut off light for type " +
std::to_string(static_cast<int>(type)));
}
}
});
return 0;
}
int main(int argc, char* argv[]) {
unsigned int inputColor;
int result = parseArgs(argc, argv, &inputColor);
if (result != 0) {
return result;
}
auto aidlHal = waitForVintfService<aidl::ILights>();
if (aidlHal != nullptr) {
setToColorAidl(aidlHal, inputColor);
return 0;
}
sp<V2_0::ILight> hidlHal = V2_0::ILight::getService();
if (hidlHal != nullptr) {
setToColorHidl(hidlHal, inputColor);
return 0;
}
error("Could not retrieve light service.");
return -1;
}