Added the ability to read external batteries
Extended InputDevice and EventHub with capabilites to detect and read
external battery status and capacity. This allows devices such as
wireless gamepads to provide battery information to applications.
Bug: 161633432
Test: atest InputDeviceBatteryTest
Change-Id: I3c65166a1f0b055c5b85bad286afd5beb60bb303
Merged-In: I3c65166a1f0b055c5b85bad286afd5beb60bb303
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index ea9b483..69aea84 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -108,6 +108,10 @@
virtual bool isVibrating(int32_t deviceId) = 0;
virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+ /* Get battery level of a particular input device. */
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) = 0;
+ /* Get battery status of a particular input device. */
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) = 0;
/* Return true if the device can send input events to the specified display. */
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index abda4ef..7f979f2 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -30,6 +30,7 @@
"mapper/accumulator/CursorScrollAccumulator.cpp",
"mapper/accumulator/SingleTouchMotionAccumulator.cpp",
"mapper/accumulator/TouchButtonAccumulator.cpp",
+ "mapper/BatteryInputMapper.cpp",
"mapper/CursorInputMapper.cpp",
"mapper/ExternalStylusInputMapper.cpp",
"mapper/InputMapper.cpp",
@@ -60,7 +61,11 @@
"libui",
"libutils",
],
+ static_libs: [
+ "libc++fs",
+ ],
header_libs: [
+ "libbatteryservice_headers",
"libinputreader_headers",
],
}
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index b97ff90..8f8c051 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -29,11 +29,14 @@
#include <sys/inotify.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
#include <unistd.h>
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <input/KeyCharacterMap.h>
@@ -64,6 +67,23 @@
static constexpr int32_t FF_STRONG_MAGNITUDE_CHANNEL_IDX = 0;
static constexpr int32_t FF_WEAK_MAGNITUDE_CHANNEL_IDX = 1;
+// must be kept in sync with definitions in kernel /drivers/power/supply/power_supply_sysfs.c
+static const std::unordered_map<std::string, int32_t> BATTERY_STATUS =
+ {{"Unknown", BATTERY_STATUS_UNKNOWN},
+ {"Charging", BATTERY_STATUS_CHARGING},
+ {"Discharging", BATTERY_STATUS_DISCHARGING},
+ {"Not charging", BATTERY_STATUS_NOT_CHARGING},
+ {"Full", BATTERY_STATUS_FULL}};
+
+// Mapping taken from
+// https://gitlab.freedesktop.org/upower/upower/-/blob/master/src/linux/up-device-supply.c#L484
+static const std::unordered_map<std::string, int32_t> BATTERY_LEVEL = {{"Critical", 5},
+ {"Low", 10},
+ {"Normal", 55},
+ {"High", 70},
+ {"Full", 100},
+ {"Unknown", 50}};
+
static inline const char* toString(bool value) {
return value ? "true" : "false";
}
@@ -127,6 +147,73 @@
return inputEventTime;
}
+/**
+ * Returns the sysfs root path of the input device
+ *
+ */
+static std::filesystem::path getSysfsRootPath(const char* devicePath) {
+ std::error_code errorCode;
+
+ // Stat the device path to get the major and minor number of the character file
+ struct stat statbuf;
+ if (stat(devicePath, &statbuf) == -1) {
+ ALOGE("Could not stat device %s due to error: %s.", devicePath, std::strerror(errno));
+ return std::filesystem::path();
+ }
+
+ unsigned int major_num = major(statbuf.st_rdev);
+ unsigned int minor_num = minor(statbuf.st_rdev);
+
+ // Realpath "/sys/dev/char/{major}:{minor}" to get the sysfs path to the input event
+ auto sysfsPath = std::filesystem::path("/sys/dev/char/");
+ sysfsPath /= std::to_string(major_num) + ":" + std::to_string(minor_num);
+ sysfsPath = std::filesystem::canonical(sysfsPath, errorCode);
+
+ // Make sure nothing went wrong in call to canonical()
+ if (errorCode) {
+ ALOGW("Could not run filesystem::canonical() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ return std::filesystem::path();
+ }
+
+ // Continue to go up a directory until we reach a directory named "input"
+ while (sysfsPath != "/" && sysfsPath.filename() != "input") {
+ sysfsPath = sysfsPath.parent_path();
+ }
+
+ // Then go up one more and you will be at the sysfs root of the device
+ sysfsPath = sysfsPath.parent_path();
+
+ // Make sure we didn't reach root path and that directory actually exists
+ if (sysfsPath == "/" || !std::filesystem::exists(sysfsPath, errorCode)) {
+ if (errorCode) {
+ ALOGW("Could not run filesystem::exists() due to error %d : %s.", errorCode.value(),
+ errorCode.message().c_str());
+ }
+
+ // Not found
+ return std::filesystem::path();
+ }
+
+ return sysfsPath;
+}
+
+/**
+ * Returns the power supply node in sys fs
+ *
+ */
+static std::filesystem::path findPowerSupplyNode(const std::filesystem::path& sysfsRootPath) {
+ for (auto path = sysfsRootPath; path != "/"; path = path.parent_path()) {
+ std::error_code errorCode;
+ auto iter = std::filesystem::directory_iterator(path / "power_supply", errorCode);
+ if (!errorCode && iter != std::filesystem::directory_iterator()) {
+ return iter->path();
+ }
+ }
+ // Not found
+ return std::filesystem::path();
+}
+
// --- Global Functions ---
Flags<InputDeviceClass> getAbsAxisUsage(int32_t axis, Flags<InputDeviceClass> deviceClasses) {
@@ -976,6 +1063,56 @@
return nullptr;
}
+std::optional<int32_t> EventHub::getBatteryCapacity(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ std::string buffer;
+
+ if (!device || (device->sysfsBatteryPath.empty())) {
+ return std::nullopt;
+ }
+
+ // Some devices report battery capacity as an integer through the "capacity" file
+ if (base::ReadFileToString(device->sysfsBatteryPath / "capacity", &buffer)) {
+ return std::stoi(buffer);
+ }
+
+ // Other devices report capacity as an enum value POWER_SUPPLY_CAPACITY_LEVEL_XXX
+ // These values are taken from kernel source code include/linux/power_supply.h
+ if (base::ReadFileToString(device->sysfsBatteryPath / "capacity_level", &buffer)) {
+ const auto it = BATTERY_LEVEL.find(buffer);
+ if (it != BATTERY_LEVEL.end()) {
+ return it->second;
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional<int32_t> EventHub::getBatteryStatus(int32_t deviceId) const {
+ std::scoped_lock _l(mLock);
+ Device* device = getDeviceLocked(deviceId);
+ std::string buffer;
+
+ if (!device || (device->sysfsBatteryPath.empty())) {
+ return std::nullopt;
+ }
+
+ if (!base::ReadFileToString(device->sysfsBatteryPath / "status", &buffer)) {
+ ALOGE("Failed to read sysfs battery info: %s", strerror(errno));
+ return std::nullopt;
+ }
+
+ // Remove trailing new line
+ buffer.erase(std::remove(buffer.begin(), buffer.end(), '\n'), buffer.end());
+ const auto it = BATTERY_STATUS.find(buffer);
+
+ if (it != BATTERY_STATUS.end()) {
+ return it->second;
+ }
+
+ return std::nullopt;
+}
+
size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) {
ALOG_ASSERT(bufferSize >= 1);
@@ -1584,6 +1721,18 @@
return -1;
}
+ // Grab the device's sysfs path
+ device->sysfsRootPath = getSysfsRootPath(devicePath.c_str());
+
+ if (!device->sysfsRootPath.empty()) {
+ device->sysfsBatteryPath = findPowerSupplyNode(device->sysfsRootPath);
+
+ // Check if a battery exists
+ if (!device->sysfsBatteryPath.empty()) {
+ device->classes |= InputDeviceClass::BATTERY;
+ }
+ }
+
// Determine whether the device has a mic.
if (device->deviceHasMicLocked()) {
device->classes |= InputDeviceClass::MIC;
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 3e6910d..8fc6f4a 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -21,6 +21,7 @@
#include <input/Flags.h>
#include <algorithm>
+#include "BatteryInputMapper.h"
#include "CursorInputMapper.h"
#include "ExternalStylusInputMapper.h"
#include "InputReaderContext.h"
@@ -160,6 +161,11 @@
mappers.push_back(std::make_unique<VibratorInputMapper>(*contextPtr));
}
+ // Battery-like devices.
+ if (classes.test(InputDeviceClass::BATTERY)) {
+ mappers.push_back(std::make_unique<BatteryInputMapper>(*contextPtr));
+ }
+
// Keyboard-like devices.
uint32_t keyboardSource = 0;
int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
@@ -490,6 +496,15 @@
for_each_mapper([when](InputMapper& mapper) { mapper.cancelTouch(when); });
}
+std::optional<int32_t> InputDevice::getBatteryCapacity() {
+ return first_in_mappers<int32_t>(
+ [](InputMapper& mapper) { return mapper.getBatteryCapacity(); });
+}
+
+std::optional<int32_t> InputDevice::getBatteryStatus() {
+ return first_in_mappers<int32_t>([](InputMapper& mapper) { return mapper.getBatteryStatus(); });
+}
+
int32_t InputDevice::getMetaState() {
int32_t result = 0;
for_each_mapper([&result](InputMapper& mapper) { result |= mapper.getMetaState(); });
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 7c448e4..de5d0e6 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -673,6 +673,26 @@
}
}
+std::optional<int32_t> InputReader::getBatteryCapacity(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBatteryCapacity();
+ }
+ return std::nullopt;
+}
+
+std::optional<int32_t> InputReader::getBatteryStatus(int32_t deviceId) {
+ std::scoped_lock _l(mLock);
+
+ InputDevice* device = findInputDeviceLocked(deviceId);
+ if (device) {
+ return device->getBatteryStatus();
+ }
+ return std::nullopt;
+}
+
bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 2cea017..30967df 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -23,6 +23,9 @@
#include <vector>
#include <input/Flags.h>
+#include <filesystem>
+
+#include <batteryservice/BatteryService.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
@@ -121,6 +124,9 @@
/* The input device has a sensor like accelerometer, gyro, etc */
SENSOR = 0x00002000,
+ /* The input device has a battery */
+ BATTERY = 0x00004000,
+
/* The input device is virtual (not a real device, not part of UI configuration). */
VIRTUAL = 0x40000000,
@@ -242,6 +248,12 @@
virtual void cancelVibrate(int32_t deviceId) = 0;
virtual std::vector<int32_t> getVibratorIds(int32_t deviceId) = 0;
+ /* Query battery level. */
+ virtual std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const = 0;
+
+ /* Query battery status. */
+ virtual std::optional<int32_t> getBatteryStatus(int32_t deviceId) const = 0;
+
/* Requests the EventHub to reopen all input devices on the next call to getEvents(). */
virtual void requestReopenDevices() = 0;
@@ -404,6 +416,10 @@
void monitor() override final;
+ std::optional<int32_t> getBatteryCapacity(int32_t deviceId) const override final;
+
+ std::optional<int32_t> getBatteryStatus(int32_t deviceId) const override final;
+
bool isDeviceEnabled(int32_t deviceId) override final;
status_t enableDevice(int32_t deviceId) override final;
@@ -442,6 +458,10 @@
bool ffEffectPlaying;
int16_t ffEffectId; // initially -1
+ // The paths are invalid when .empty() returns true
+ std::filesystem::path sysfsRootPath;
+ std::filesystem::path sysfsBatteryPath;
+
int32_t controllerNumber;
Device(int fd, int32_t id, const std::string& path,
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 5af76b7..e4186c8 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -92,6 +92,9 @@
void disableSensor(InputDeviceSensorType sensorType);
void flushSensor(InputDeviceSensorType sensorType);
+ std::optional<int32_t> getBatteryCapacity();
+ std::optional<int32_t> getBatteryStatus();
+
int32_t getMetaState();
void updateMetaState(int32_t keyCode);
@@ -287,6 +290,12 @@
inline std::vector<int32_t> getVibratorIds() { return mEventHub->getVibratorIds(mId); }
+ inline std::optional<int32_t> getBatteryCapacity() {
+ return mEventHub->getBatteryCapacity(mId);
+ }
+
+ inline std::optional<int32_t> getBatteryStatus() { return mEventHub->getBatteryStatus(mId); }
+
inline bool hasAbsoluteAxis(int32_t code) const {
RawAbsoluteAxisInfo info;
mEventHub->getAbsoluteAxisInfo(mId, code, &info);
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 7be932a..e2558bc 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -95,6 +95,10 @@
void flushSensor(int32_t deviceId, InputDeviceSensorType sensorType) override;
+ std::optional<int32_t> getBatteryCapacity(int32_t deviceId) override;
+
+ std::optional<int32_t> getBatteryStatus(int32_t deviceId) override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(int32_t deviceId,
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.cpp b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
new file mode 100644
index 0000000..afdc5ab
--- /dev/null
+++ b/services/inputflinger/reader/mapper/BatteryInputMapper.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2021 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 "../Macros.h"
+
+#include "BatteryInputMapper.h"
+
+namespace android {
+
+BatteryInputMapper::BatteryInputMapper(InputDeviceContext& deviceContext)
+ : InputMapper(deviceContext) {}
+
+uint32_t BatteryInputMapper::getSources() {
+ return 0;
+}
+
+void BatteryInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
+ InputMapper::populateDeviceInfo(info);
+
+ info->setHasBattery(true);
+}
+
+void BatteryInputMapper::process(const RawEvent* rawEvent) {}
+
+std::optional<int32_t> BatteryInputMapper::getBatteryCapacity() {
+ return getDeviceContext().getBatteryCapacity();
+}
+
+std::optional<int32_t> BatteryInputMapper::getBatteryStatus() {
+ return getDeviceContext().getBatteryStatus();
+}
+
+void BatteryInputMapper::dump(std::string& dump) {
+ dump += INDENT2 "Battery Input Mapper:\n";
+ dump += getBatteryCapacity().has_value()
+ ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity().value())
+ : StringPrintf(INDENT3 "Capacity: Unknown");
+
+ std::string status;
+ switch (getBatteryStatus().value_or(BATTERY_STATUS_UNKNOWN)) {
+ case BATTERY_STATUS_CHARGING:
+ status = "Charging";
+ break;
+ case BATTERY_STATUS_DISCHARGING:
+ status = "Discharging";
+ break;
+ case BATTERY_STATUS_NOT_CHARGING:
+ status = "Not charging";
+ break;
+ case BATTERY_STATUS_FULL:
+ status = "Full";
+ break;
+ default:
+ status = "Unknown";
+ }
+ dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
+}
+
+} // namespace android
diff --git a/services/inputflinger/reader/mapper/BatteryInputMapper.h b/services/inputflinger/reader/mapper/BatteryInputMapper.h
new file mode 100644
index 0000000..4fe373e
--- /dev/null
+++ b/services/inputflinger/reader/mapper/BatteryInputMapper.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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 _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
+#define _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
+
+#include "InputMapper.h"
+
+namespace android {
+
+class BatteryInputMapper : public InputMapper {
+public:
+ explicit BatteryInputMapper(InputDeviceContext& deviceContext);
+ virtual ~BatteryInputMapper(){};
+
+ uint32_t getSources() override;
+ void populateDeviceInfo(InputDeviceInfo* deviceInfo) override;
+ void process(const RawEvent* rawEvent) override;
+
+ std::optional<int32_t> getBatteryCapacity() override;
+ std::optional<int32_t> getBatteryStatus() override;
+
+ void dump(std::string& dump) override;
+};
+
+} // namespace android
+
+#endif // _UI_INPUTREADER_BATTERY_INPUT_MAPPER_H
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 6ca6ec9..44af998 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -72,6 +72,9 @@
virtual void disableSensor(InputDeviceSensorType sensorType);
virtual void flushSensor(InputDeviceSensorType sensorType);
+ virtual std::optional<int32_t> getBatteryCapacity() { return std::nullopt; }
+ virtual std::optional<int32_t> getBatteryStatus() { return std::nullopt; }
+
virtual int32_t getMetaState();
virtual void updateMetaState(int32_t keyCode);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 36da8dd..0e88312 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <BatteryInputMapper.h>
#include <CursorInputMapper.h>
#include <InputDevice.h>
#include <InputMapper.h>
@@ -67,6 +68,8 @@
static constexpr int32_t FIRST_TRACKING_ID = 0;
static constexpr int32_t SECOND_TRACKING_ID = 1;
static constexpr int32_t THIRD_TRACKING_ID = 2;
+static constexpr int32_t BATTERY_STATUS = 4;
+static constexpr int32_t BATTERY_CAPACITY = 66;
// Error tolerance for floating point assertions.
static const float EPSILON = 0.001f;
@@ -863,6 +866,10 @@
std::vector<int32_t> getVibratorIds(int32_t deviceId) override { return mVibrators; };
+ std::optional<int32_t> getBatteryCapacity(int32_t) const override { return BATTERY_CAPACITY; }
+
+ std::optional<int32_t> getBatteryStatus(int32_t) const override { return BATTERY_STATUS; }
+
virtual bool isExternal(int32_t) const {
return false;
}
@@ -1924,6 +1931,52 @@
ASSERT_EQ(mReader->getVibratorIds(deviceId).size(), 2U);
}
+class FakeBatteryInputMapper : public FakeInputMapper {
+public:
+ FakeBatteryInputMapper(InputDeviceContext& deviceContext, uint32_t sources)
+ : FakeInputMapper(deviceContext, sources) {}
+
+ std::optional<int32_t> getBatteryCapacity() override {
+ return getDeviceContext().getBatteryCapacity();
+ }
+
+ std::optional<int32_t> getBatteryStatus() override {
+ return getDeviceContext().getBatteryStatus();
+ }
+};
+
+TEST_F(InputReaderTest, BatteryGetCapacity) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakeBatteryInputMapper& mapper =
+ device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+ ASSERT_EQ(mReader->getBatteryCapacity(deviceId), BATTERY_CAPACITY);
+}
+
+TEST_F(InputReaderTest, BatteryGetStatus) {
+ constexpr int32_t deviceId = END_RESERVED_ID + 1000;
+ Flags<InputDeviceClass> deviceClass = InputDeviceClass::KEYBOARD | InputDeviceClass::BATTERY;
+ constexpr int32_t eventHubId = 1;
+ const char* DEVICE_LOCATION = "BLUETOOTH";
+ std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake", DEVICE_LOCATION);
+ FakeBatteryInputMapper& mapper =
+ device->addMapper<FakeBatteryInputMapper>(eventHubId, AINPUT_SOURCE_KEYBOARD);
+ mReader->pushNextDevice(device);
+
+ ASSERT_NO_FATAL_FAILURE(addDevice(eventHubId, "fake", deviceClass, nullptr));
+ ASSERT_NO_FATAL_FAILURE(mapper.assertConfigureWasCalled());
+
+ ASSERT_EQ(mReader->getBatteryStatus(deviceId), BATTERY_STATUS);
+}
+
// --- InputReaderIntegrationTest ---
// These tests create and interact with the InputReader only through its interface.
@@ -2799,6 +2852,32 @@
mapper.flushSensor(InputDeviceSensorType::GYROSCOPE);
}
+// --- BatteryInputMapperTest ---
+class BatteryInputMapperTest : public InputMapperTest {
+protected:
+ void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::BATTERY); }
+};
+
+TEST_F(BatteryInputMapperTest, GetSources) {
+ BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
+
+ ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mapper.getSources());
+}
+
+TEST_F(BatteryInputMapperTest, GetBatteryCapacity) {
+ BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
+
+ ASSERT_TRUE(mapper.getBatteryCapacity());
+ ASSERT_EQ(*mapper.getBatteryCapacity(), BATTERY_CAPACITY);
+}
+
+TEST_F(BatteryInputMapperTest, GetBatteryStatus) {
+ BatteryInputMapper& mapper = addMapperAndConfigure<BatteryInputMapper>();
+
+ ASSERT_TRUE(mapper.getBatteryStatus());
+ ASSERT_EQ(*mapper.getBatteryStatus(), BATTERY_STATUS);
+}
+
// --- KeyboardInputMapperTest ---
class KeyboardInputMapperTest : public InputMapperTest {