blob: be1f722e2ec9b062fe343e7d8c18dcd2f4ae11ab [file] [log] [blame]
Chris Ye3fdbfef2021-01-06 18:45:18 -08001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <locale>
18#include <regex>
19
20#include "../Macros.h"
21
22#include "LightInputMapper.h"
23#include "input/NamedEnum.h"
24
25// Log detailed debug messages about input device lights.
26static constexpr bool DEBUG_LIGHT_DETAILS = false;
27
28namespace android {
29
30static inline int32_t getAlpha(int32_t color) {
31 return (color >> 24) & 0xff;
32}
33
34static inline int32_t getRed(int32_t color) {
35 return (color >> 16) & 0xff;
36}
37
38static inline int32_t getGreen(int32_t color) {
39 return (color >> 8) & 0xff;
40}
41
42static inline int32_t getBlue(int32_t color) {
43 return color & 0xff;
44}
45
46static inline int32_t toArgb(int32_t brightness, int32_t red, int32_t green, int32_t blue) {
47 return (brightness & 0xff) << 24 | (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff);
48}
49
50/**
51 * Light input mapper owned by InputReader device, implements the native API for querying input
52 * lights, getting and setting the lights brightness and color, by interacting with EventHub
53 * devices.
54 * TODO b/180342233: Reconsider the inputflinger design to accommodate the device class
55 * like lights and battery.
56 */
57LightInputMapper::LightInputMapper(InputDeviceContext& deviceContext)
58 : InputMapper(deviceContext) {}
59
60LightInputMapper::~LightInputMapper() {}
61
62std::optional<std::int32_t> LightInputMapper::Light::getRawLightBrightness(int32_t rawLightId) {
63 std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
64 std::optional<int32_t> ret = context.getLightBrightness(rawLightId);
65 if (!rawInfo.has_value() || !ret.has_value()) {
66 return std::nullopt;
67 }
68 int brightness = ret.value();
69
70 // If the light node doesn't have max brightness, use the default max brightness.
71 int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
72 float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
73 // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
74 if (rawMaxBrightness != MAX_BRIGHTNESS) {
75 brightness = brightness * ratio;
76 }
77 if (DEBUG_LIGHT_DETAILS) {
78 ALOGD("getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
79 brightness, ratio);
80 }
81 return brightness;
82}
83
84void LightInputMapper::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
85 std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
86 if (!rawInfo.has_value()) {
87 return;
88 }
89 // If the light node doesn't have max brightness, use the default max brightness.
90 int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
91 float ratio = MAX_BRIGHTNESS / rawMaxBrightness;
92 // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
93 if (rawMaxBrightness != MAX_BRIGHTNESS) {
94 brightness = ceil(brightness / ratio);
95 }
96 if (DEBUG_LIGHT_DETAILS) {
97 ALOGD("setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f", rawLightId,
98 brightness, ratio);
99 }
100 context.setLightBrightness(rawLightId, brightness);
101}
102
103bool LightInputMapper::SingleLight::setLightColor(int32_t color) {
104 int32_t brightness = getAlpha(color);
105 setRawLightBrightness(rawId, brightness);
106
107 return true;
108}
109
110bool LightInputMapper::RgbLight::setLightColor(int32_t color) {
111 // Compose color value as per:
112 // https://developer.android.com/reference/android/graphics/Color?hl=en
113 // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
114 // The alpha component is used to scale the R,G,B leds brightness, with the ratio to
115 // MAX_BRIGHTNESS.
116 brightness = getAlpha(color);
117 int32_t red = 0;
118 int32_t green = 0;
119 int32_t blue = 0;
120 if (brightness > 0) {
121 float ratio = MAX_BRIGHTNESS / brightness;
122 red = ceil(getRed(color) / ratio);
123 green = ceil(getGreen(color) / ratio);
124 blue = ceil(getBlue(color) / ratio);
125 }
126 setRawLightBrightness(rawRgbIds.at(LightColor::RED), red);
127 setRawLightBrightness(rawRgbIds.at(LightColor::GREEN), green);
128 setRawLightBrightness(rawRgbIds.at(LightColor::BLUE), blue);
129 if (rawGlobalId.has_value()) {
130 setRawLightBrightness(rawGlobalId.value(), brightness);
131 }
132
133 return true;
134}
135
136bool LightInputMapper::MultiColorLight::setLightColor(int32_t color) {
137 std::unordered_map<LightColor, int32_t> intensities;
138 intensities.emplace(LightColor::RED, getRed(color));
139 intensities.emplace(LightColor::GREEN, getGreen(color));
140 intensities.emplace(LightColor::BLUE, getBlue(color));
141
142 context.setLightIntensities(rawId, intensities);
143 setRawLightBrightness(rawId, getAlpha(color));
144 return true;
145}
146
147std::optional<int32_t> LightInputMapper::SingleLight::getLightColor() {
148 std::optional<int32_t> brightness = getRawLightBrightness(rawId);
149 if (!brightness.has_value()) {
150 return std::nullopt;
151 }
152
153 return toArgb(brightness.value(), 0 /* red */, 0 /* green */, 0 /* blue */);
154}
155
156std::optional<int32_t> LightInputMapper::RgbLight::getLightColor() {
157 // If the Alpha component is zero, then return color 0.
158 if (brightness == 0) {
159 return 0;
160 }
161 // Compose color value as per:
162 // https://developer.android.com/reference/android/graphics/Color?hl=en
163 // int color = (A & 0xff) << 24 | (R & 0xff) << 16 | (G & 0xff) << 8 | (B & 0xff);
164 std::optional<int32_t> redOr = getRawLightBrightness(rawRgbIds.at(LightColor::RED));
165 std::optional<int32_t> greenOr = getRawLightBrightness(rawRgbIds.at(LightColor::GREEN));
166 std::optional<int32_t> blueOr = getRawLightBrightness(rawRgbIds.at(LightColor::BLUE));
167 // If we can't get brightness for any of the RGB light
168 if (!redOr.has_value() || !greenOr.has_value() || !blueOr.has_value()) {
169 return std::nullopt;
170 }
171
172 // Compose the ARGB format color. As the R,G,B color led brightness is scaled by Alpha
173 // value, scale it back to return the nominal color value.
174 float ratio = MAX_BRIGHTNESS / brightness;
175 int32_t red = round(redOr.value() * ratio);
176 int32_t green = round(greenOr.value() * ratio);
177 int32_t blue = round(blueOr.value() * ratio);
178
179 if (red > MAX_BRIGHTNESS || green > MAX_BRIGHTNESS || blue > MAX_BRIGHTNESS) {
180 // Previously stored brightness isn't valid for current LED values, so just reset to max
181 // brightness since an app couldn't have provided these values in the first place.
182 red = redOr.value();
183 green = greenOr.value();
184 blue = blueOr.value();
185 brightness = MAX_BRIGHTNESS;
186 }
187
188 return toArgb(brightness, red, green, blue);
189}
190
191std::optional<int32_t> LightInputMapper::MultiColorLight::getLightColor() {
192 auto ret = context.getLightIntensities(rawId);
193 if (!ret.has_value()) {
194 return std::nullopt;
195 }
196 std::unordered_map<LightColor, int32_t> intensities = ret.value();
197 // Get red, green, blue colors
198 int32_t color = toArgb(0 /* brightness */, intensities.at(LightColor::RED) /* red */,
199 intensities.at(LightColor::GREEN) /* green */,
200 intensities.at(LightColor::BLUE) /* blue */);
201 // Get brightness
202 std::optional<int32_t> brightness = getRawLightBrightness(rawId);
203 if (brightness.has_value()) {
204 return toArgb(brightness.value() /* A */, 0, 0, 0) | color;
205 }
206 return std::nullopt;
207}
208
209bool LightInputMapper::PlayerIdLight::setLightPlayerId(int32_t playerId) {
210 if (rawLightIds.find(playerId) == rawLightIds.end()) {
211 return false;
212 }
213 for (const auto& [id, rawId] : rawLightIds) {
214 if (playerId == id) {
215 setRawLightBrightness(rawId, MAX_BRIGHTNESS);
216 } else {
217 setRawLightBrightness(rawId, 0);
218 }
219 }
220 return true;
221}
222
223std::optional<int32_t> LightInputMapper::PlayerIdLight::getLightPlayerId() {
224 for (const auto& [id, rawId] : rawLightIds) {
225 std::optional<int32_t> brightness = getRawLightBrightness(rawId);
226 if (brightness.has_value() && brightness.value() > 0) {
227 return id;
228 }
229 }
230 return std::nullopt;
231}
232
233void LightInputMapper::SingleLight::dump(std::string& dump) {
234 dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
235}
236
237void LightInputMapper::PlayerIdLight::dump(std::string& dump) {
238 dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
239 dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
240 for (const auto& [id, rawId] : rawLightIds) {
241 dump += StringPrintf("id %d -> %d ", id, rawId);
242 }
243 dump += "\n";
244}
245
246void LightInputMapper::RgbLight::dump(std::string& dump) {
247 dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
248 dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
249 rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
250 if (rawGlobalId.has_value()) {
251 dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
252 }
253 dump += "\n";
254}
255
256void LightInputMapper::MultiColorLight::dump(std::string& dump) {
257 dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
258}
259
260uint32_t LightInputMapper::getSources() {
261 return AINPUT_SOURCE_UNKNOWN;
262}
263
264void LightInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
265 InputMapper::populateDeviceInfo(info);
266
267 for (const auto& [lightId, light] : mLights) {
268 // Input device light doesn't support ordinal, always pass 1.
269 InputDeviceLightInfo lightInfo(light->name, light->id, light->type, 1 /* ordinal */);
270 info->addLightInfo(lightInfo);
271 }
272}
273
274void LightInputMapper::dump(std::string& dump) {
275 dump += INDENT2 "Light Input Mapper:\n";
276 dump += INDENT3 "Lights:\n";
277 for (const auto& [lightId, light] : mLights) {
278 dump += StringPrintf(INDENT4 "Id: %d", lightId);
279 dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
280 dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str());
281 light->dump(dump);
282 }
283 // Dump raw lights
284 dump += INDENT3 "RawLights:\n";
285 dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
286 const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
287 // Map from raw light id to raw light info
288 std::unordered_map<int32_t, RawLightInfo> rawInfos;
289 for (const auto& rawId : rawLightIds) {
290 std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
291 if (!rawInfo.has_value()) {
292 continue;
293 }
294 dump += StringPrintf(INDENT4 "%d", rawId);
295 dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
296 dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
297 dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
298 dump += StringPrintf(INDENT4 "%d\n",
299 getDeviceContext().getLightBrightness(rawId).value_or(-1));
300 }
301}
302
303void LightInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
304 uint32_t changes) {
305 InputMapper::configure(when, config, changes);
306
307 if (!changes) { // first time only
308 bool hasRedLed = false;
309 bool hasGreenLed = false;
310 bool hasBlueLed = false;
311 std::optional<int32_t> rawGlobalId = std::nullopt;
312 // Player ID light common name string
313 std::string playerIdName;
314 // Raw RGB color to raw light ID
315 std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
316 // Map from player Id to raw light Id
317 std::unordered_map<int32_t, int32_t> playerIdLightIds;
318 mLights.clear();
319
320 // Check raw lights
321 const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
322 // Map from raw light id to raw light info
323 std::unordered_map<int32_t, RawLightInfo> rawInfos;
324 for (const auto& rawId : rawLightIds) {
325 std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
326 if (!rawInfo.has_value()) {
327 continue;
328 }
329 rawInfos.insert_or_assign(rawId, rawInfo.value());
330 // Check if this is a group LEDs for player ID
331 std::regex lightPattern("([a-z]+)([0-9]+)");
332 std::smatch results;
333 if (std::regex_match(rawInfo->name, results, lightPattern)) {
334 std::string commonName = results[1].str();
335 int32_t playerId = std::stoi(results[2]);
336 if (playerIdLightIds.empty()) {
337 playerIdName = commonName;
338 playerIdLightIds.insert_or_assign(playerId, rawId);
339 } else {
340 // Make sure the player ID leds have common string name
341 if (playerIdName.compare(commonName) == 0 &&
342 playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
343 playerIdLightIds.insert_or_assign(playerId, rawId);
344 }
345 }
346 }
347 // Check if this is an LED of RGB light
348 if (rawInfo->flags.test(InputLightClass::RED)) {
349 hasRedLed = true;
350 rawRgbIds.emplace(LightColor::RED, rawId);
351 }
352 if (rawInfo->flags.test(InputLightClass::GREEN)) {
353 hasGreenLed = true;
354 rawRgbIds.emplace(LightColor::GREEN, rawId);
355 }
356 if (rawInfo->flags.test(InputLightClass::BLUE)) {
357 hasBlueLed = true;
358 rawRgbIds.emplace(LightColor::BLUE, rawId);
359 }
360 if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
361 rawGlobalId = rawId;
362 }
363 if (DEBUG_LIGHT_DETAILS) {
364 ALOGD("Light rawId %d name %s max %d flags %s \n", rawInfo->id,
365 rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
366 rawInfo->flags.string().c_str());
367 }
368 }
369
370 // Construct a player ID light
371 if (playerIdLightIds.size() > 1) {
372 std::unique_ptr<Light> light =
373 std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
374 playerIdLightIds);
375 mLights.insert_or_assign(light->id, std::move(light));
376 // Remove these raw lights from raw light info as they've been used to compose a
377 // Player ID light, so we do not expose these raw lights as single lights.
378 for (const auto& [playerId, rawId] : playerIdLightIds) {
379 rawInfos.erase(rawId);
380 }
381 }
382 // Construct a RGB light for composed RGB light
383 if (hasRedLed && hasGreenLed && hasBlueLed) {
384 if (DEBUG_LIGHT_DETAILS) {
385 ALOGD("Rgb light ids [%d, %d, %d] \n", rawRgbIds.at(LightColor::RED),
386 rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
387 }
388 std::unique_ptr<Light> light = std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
389 rawRgbIds, rawGlobalId);
390 mLights.insert_or_assign(light->id, std::move(light));
391 // Remove from raw light info as they've been composed a RBG light.
392 rawInfos.erase(rawRgbIds.at(LightColor::RED));
393 rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
394 rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
395 if (rawGlobalId.has_value()) {
396 rawInfos.erase(rawGlobalId.value());
397 }
398 }
399
400 // Check the rest of raw light infos
401 for (const auto& [rawId, rawInfo] : rawInfos) {
402 // If the node is multi-color led, construct a MULTI_COLOR light
403 if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
404 rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
405 if (DEBUG_LIGHT_DETAILS) {
406 ALOGD("Multicolor light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
407 }
408 std::unique_ptr<Light> light =
409 std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name,
410 ++mNextId, rawInfo.id);
411 mLights.insert_or_assign(light->id, std::move(light));
412 continue;
413 }
414 // Construct a single LED light
415 if (DEBUG_LIGHT_DETAILS) {
416 ALOGD("Single light Id %d name %s \n", rawInfo.id, rawInfo.name.c_str());
417 }
418 std::unique_ptr<Light> light =
419 std::make_unique<SingleLight>(getDeviceContext(), rawInfo.name, ++mNextId,
420 rawInfo.id);
421
422 mLights.insert_or_assign(light->id, std::move(light));
423 }
424 }
425}
426
427void LightInputMapper::reset(nsecs_t when) {
428 InputMapper::reset(when);
429}
430
431void LightInputMapper::process(const RawEvent* rawEvent) {}
432
433bool LightInputMapper::setLightColor(int32_t lightId, int32_t color) {
434 auto it = mLights.find(lightId);
435 if (it == mLights.end()) {
436 return false;
437 }
438 auto& light = it->second;
439 if (DEBUG_LIGHT_DETAILS) {
440 ALOGD("setLightColor lightId %d type %s color 0x%x", lightId,
441 NamedEnum::string(light->type).c_str(), color);
442 }
443 return light->setLightColor(color);
444}
445
446std::optional<int32_t> LightInputMapper::getLightColor(int32_t lightId) {
447 auto it = mLights.find(lightId);
448 if (it == mLights.end()) {
449 return std::nullopt;
450 }
451 auto& light = it->second;
452 std::optional<int32_t> color = light->getLightColor();
453 if (DEBUG_LIGHT_DETAILS) {
454 ALOGD("getLightColor lightId %d type %s color 0x%x", lightId,
455 NamedEnum::string(light->type).c_str(), color.value_or(0));
456 }
457 return color;
458}
459
460bool LightInputMapper::setLightPlayerId(int32_t lightId, int32_t playerId) {
461 auto it = mLights.find(lightId);
462 if (it == mLights.end()) {
463 return false;
464 }
465 auto& light = it->second;
466 return light->setLightPlayerId(playerId);
467}
468
469std::optional<int32_t> LightInputMapper::getLightPlayerId(int32_t lightId) {
470 auto it = mLights.find(lightId);
471 if (it == mLights.end()) {
472 return std::nullopt;
473 }
474 auto& light = it->second;
475 return light->getLightPlayerId();
476}
477
478} // namespace android