• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 #include <sstream>
20 #include <string>
21 
22 #include <android/sysprop/InputProperties.sysprop.h>
23 #include <ftl/enum.h>
24 
25 #include "../Macros.h"
26 #include "PeripheralController.h"
27 
28 namespace android {
29 
getAlpha(int32_t color)30 static inline int32_t getAlpha(int32_t color) {
31     return (color >> 24) & 0xff;
32 }
33 
getRed(int32_t color)34 static inline int32_t getRed(int32_t color) {
35     return (color >> 16) & 0xff;
36 }
37 
getGreen(int32_t color)38 static inline int32_t getGreen(int32_t color) {
39     return (color >> 8) & 0xff;
40 }
41 
getBlue(int32_t color)42 static inline int32_t getBlue(int32_t color) {
43     return color & 0xff;
44 }
45 
toArgb(int32_t brightness,int32_t red,int32_t green,int32_t blue)46 static 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  * Input controller 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  */
PeripheralController(InputDeviceContext & deviceContext)55 PeripheralController::PeripheralController(InputDeviceContext& deviceContext)
56       : mDeviceContext(deviceContext) {
57     configureBattries();
58     configureLights();
59 }
60 
~PeripheralController()61 PeripheralController::~PeripheralController() {}
62 
getRawLightBrightness(int32_t rawLightId)63 std::optional<std::int32_t> PeripheralController::Light::getRawLightBrightness(int32_t rawLightId) {
64     std::optional<RawLightInfo> rawInfoOpt = context.getRawLightInfo(rawLightId);
65     if (!rawInfoOpt.has_value()) {
66         return std::nullopt;
67     }
68     std::optional<int32_t> brightnessOpt = context.getLightBrightness(rawLightId);
69     if (!brightnessOpt.has_value()) {
70         return std::nullopt;
71     }
72     int brightness = brightnessOpt.value();
73 
74     // If the light node doesn't have max brightness, use the default max brightness.
75     int rawMaxBrightness = rawInfoOpt->maxBrightness.value_or(MAX_BRIGHTNESS);
76     float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
77     // Scale the returned brightness in [0, rawMaxBrightness] to [0, 255]
78     if (rawMaxBrightness != MAX_BRIGHTNESS) {
79         brightness = brightness * ratio;
80     }
81     ALOGD_IF(DEBUG_LIGHT_DETAILS, "getRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
82              rawLightId, brightness, ratio);
83     return brightness;
84 }
85 
setRawLightBrightness(int32_t rawLightId,int32_t brightness)86 void PeripheralController::Light::setRawLightBrightness(int32_t rawLightId, int32_t brightness) {
87     std::optional<RawLightInfo> rawInfo = context.getRawLightInfo(rawLightId);
88     if (!rawInfo.has_value()) {
89         return;
90     }
91     // If the light node doesn't have max brightness, use the default max brightness.
92     int rawMaxBrightness = rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS);
93     float ratio = static_cast<float>(MAX_BRIGHTNESS) / rawMaxBrightness;
94     // Scale the requested brightness in [0, 255] to [0, rawMaxBrightness]
95     if (rawMaxBrightness != MAX_BRIGHTNESS) {
96         brightness = ceil(brightness / ratio);
97     }
98     ALOGD_IF(DEBUG_LIGHT_DETAILS, "setRawLightBrightness rawLightId %d brightness 0x%x ratio %.2f",
99              rawLightId, brightness, ratio);
100     context.setLightBrightness(rawLightId, brightness);
101 }
102 
setLightColor(int32_t color)103 bool PeripheralController::MonoLight::setLightColor(int32_t color) {
104     int32_t brightness = getAlpha(color);
105     setRawLightBrightness(rawId, brightness);
106 
107     return true;
108 }
109 
setLightColor(int32_t color)110 bool PeripheralController::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 
setLightColor(int32_t color)136 bool PeripheralController::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 
getLightColor()147 std::optional<int32_t> PeripheralController::MonoLight::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(), /*red=*/0, /*green=*/0, /*blue=*/0);
154 }
155 
getLightColor()156 std::optional<int32_t> PeripheralController::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 
getLightColor()191 std::optional<int32_t> PeripheralController::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(/*brightness=*/0, intensities.at(LightColor::RED),
199                            intensities.at(LightColor::GREEN), intensities.at(LightColor::BLUE));
200     // Get brightness
201     std::optional<int32_t> brightness = getRawLightBrightness(rawId);
202     if (brightness.has_value()) {
203         return toArgb(/*brightness=*/brightness.value(), 0, 0, 0) | color;
204     }
205     return std::nullopt;
206 }
207 
setLightPlayerId(int32_t playerId)208 bool PeripheralController::PlayerIdLight::setLightPlayerId(int32_t playerId) {
209     if (rawLightIds.find(playerId) == rawLightIds.end()) {
210         return false;
211     }
212     for (const auto& [id, rawId] : rawLightIds) {
213         if (playerId == id) {
214             setRawLightBrightness(rawId, MAX_BRIGHTNESS);
215         } else {
216             setRawLightBrightness(rawId, 0);
217         }
218     }
219     return true;
220 }
221 
getLightPlayerId()222 std::optional<int32_t> PeripheralController::PlayerIdLight::getLightPlayerId() {
223     for (const auto& [id, rawId] : rawLightIds) {
224         std::optional<int32_t> brightness = getRawLightBrightness(rawId);
225         if (brightness.has_value() && brightness.value() > 0) {
226             return id;
227         }
228     }
229     return std::nullopt;
230 }
231 
dump(std::string & dump)232 void PeripheralController::MonoLight::dump(std::string& dump) {
233     dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
234 }
235 
dump(std::string & dump)236 void PeripheralController::PlayerIdLight::dump(std::string& dump) {
237     dump += StringPrintf(INDENT4 "PlayerId: %d\n", getLightPlayerId().value_or(-1));
238     dump += StringPrintf(INDENT4 "Raw Player ID LEDs:");
239     for (const auto& [id, rawId] : rawLightIds) {
240         dump += StringPrintf("id %d -> %d ", id, rawId);
241     }
242     dump += "\n";
243 }
244 
dump(std::string & dump)245 void PeripheralController::RgbLight::dump(std::string& dump) {
246     dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
247     dump += StringPrintf(INDENT4 "Raw RGB LEDs: [%d, %d, %d] ", rawRgbIds.at(LightColor::RED),
248                          rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
249     if (rawGlobalId.has_value()) {
250         dump += StringPrintf(INDENT4 "Raw Global LED: [%d] ", rawGlobalId.value());
251     }
252     dump += "\n";
253 }
254 
dump(std::string & dump)255 void PeripheralController::MultiColorLight::dump(std::string& dump) {
256     dump += StringPrintf(INDENT4 "Color: 0x%x\n", getLightColor().value_or(0));
257 }
258 
populateDeviceInfo(InputDeviceInfo * deviceInfo)259 void PeripheralController::populateDeviceInfo(InputDeviceInfo* deviceInfo) {
260     // TODO: b/180733860 Remove this after enabling multi-battery
261     if (!mBatteries.empty()) {
262         deviceInfo->setHasBattery(true);
263     }
264 
265     for (const auto& [batteryId, battery] : mBatteries) {
266         InputDeviceBatteryInfo batteryInfo(battery->name, battery->id);
267         deviceInfo->addBatteryInfo(batteryInfo);
268     }
269 
270     for (const auto& [lightId, light] : mLights) {
271         // Input device light doesn't support ordinal, always pass 1.
272         InputDeviceLightInfo lightInfo(light->name, light->id, light->type, light->capabilityFlags,
273                                        /*ordinal=*/1, getPreferredBrightnessLevels(light.get()));
274         deviceInfo->addLightInfo(lightInfo);
275     }
276 }
277 
278 // TODO(b/281822656): Move to constructor and add as a parameter to avoid parsing repeatedly.
279 // Need to change lifecycle of Peripheral controller so that Input device configuration map is
280 // available at construction time before moving this logic to constructor.
getPreferredBrightnessLevels(const Light * light) const281 std::set<BrightnessLevel> PeripheralController::getPreferredBrightnessLevels(
282         const Light* light) const {
283     std::set<BrightnessLevel> levels;
284     if (light->type != InputDeviceLightType::KEYBOARD_BACKLIGHT) {
285         return levels;
286     }
287     std::optional<std::string> keyboardBacklightLevels =
288             mDeviceContext.getConfiguration().getString("keyboard.backlight.brightnessLevels");
289     if (!keyboardBacklightLevels) {
290         return levels;
291     }
292     std::stringstream ss(*keyboardBacklightLevels);
293     while (ss.good()) {
294         std::string substr;
295         std::getline(ss, substr, ',');
296         char* end;
297         int32_t value = static_cast<int32_t>(strtol(substr.c_str(), &end, 10));
298         if (*end != '\0' || value < 0 || value > 255) {
299             ALOGE("Error parsing keyboard backlight brightness levels, provided levels = %s",
300                   keyboardBacklightLevels->c_str());
301             levels.clear();
302             break;
303         }
304         levels.insert(BrightnessLevel(value));
305     }
306     return levels;
307 }
308 
dump(std::string & dump)309 void PeripheralController::dump(std::string& dump) {
310     dump += INDENT2 "Input Controller:\n";
311     if (!mLights.empty()) {
312         dump += INDENT3 "Lights:\n";
313         for (const auto& [lightId, light] : mLights) {
314             dump += StringPrintf(INDENT4 "Id: %d", lightId);
315             dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str());
316             dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str());
317             dump += StringPrintf(INDENT4 "Capability flags: %s",
318                                  light->capabilityFlags.string().c_str());
319             light->dump(dump);
320         }
321     }
322     // Dump raw lights
323     dump += INDENT3 "RawLights:\n";
324     dump += INDENT4 "Id:\t Name:\t Flags:\t Max brightness:\t Brightness\n";
325     const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
326     // Map from raw light id to raw light info
327     std::unordered_map<int32_t, RawLightInfo> rawInfos;
328     for (const auto& rawId : rawLightIds) {
329         std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
330         if (!rawInfo.has_value()) {
331             continue;
332         }
333         dump += StringPrintf(INDENT4 "%d", rawId);
334         dump += StringPrintf(INDENT4 "%s", rawInfo->name.c_str());
335         dump += StringPrintf(INDENT4 "%s", rawInfo->flags.string().c_str());
336         dump += StringPrintf(INDENT4 "%d", rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS));
337         dump += StringPrintf(INDENT4 "%d\n",
338                              getDeviceContext().getLightBrightness(rawId).value_or(-1));
339     }
340 
341     if (!mBatteries.empty()) {
342         dump += INDENT3 "Batteries:\n";
343         for (const auto& [batteryId, battery] : mBatteries) {
344             dump += StringPrintf(INDENT4 "Id: %d", batteryId);
345             dump += StringPrintf(INDENT4 "Name: %s", battery->name.c_str());
346             dump += getBatteryCapacity(batteryId).has_value()
347                     ? StringPrintf(INDENT3 "Capacity: %d\n", getBatteryCapacity(batteryId).value())
348                     : StringPrintf(INDENT3 "Capacity: Unknown");
349 
350             std::string status;
351             switch (getBatteryStatus(batteryId).value_or(BATTERY_STATUS_UNKNOWN)) {
352                 case BATTERY_STATUS_CHARGING:
353                     status = "Charging";
354                     break;
355                 case BATTERY_STATUS_DISCHARGING:
356                     status = "Discharging";
357                     break;
358                 case BATTERY_STATUS_NOT_CHARGING:
359                     status = "Not charging";
360                     break;
361                 case BATTERY_STATUS_FULL:
362                     status = "Full";
363                     break;
364                 default:
365                     status = "Unknown";
366             }
367             dump += StringPrintf(INDENT3 "Status: %s\n", status.c_str());
368         }
369     }
370 }
371 
configureBattries()372 void PeripheralController::configureBattries() {
373     // Check raw batteries
374     const std::vector<int32_t> rawBatteryIds = getDeviceContext().getRawBatteryIds();
375 
376     for (const auto& rawId : rawBatteryIds) {
377         std::optional<RawBatteryInfo> rawInfo = getDeviceContext().getRawBatteryInfo(rawId);
378         if (!rawInfo.has_value()) {
379             continue;
380         }
381         std::unique_ptr<Battery> battery =
382                 std::make_unique<Battery>(getDeviceContext(), rawInfo->name, rawInfo->id);
383         mBatteries.insert_or_assign(rawId, std::move(battery));
384     }
385 }
386 
configureLights()387 void PeripheralController::configureLights() {
388     bool hasRedLed = false;
389     bool hasGreenLed = false;
390     bool hasBlueLed = false;
391     std::optional<int32_t> rawGlobalId = std::nullopt;
392     // Player ID light common name string
393     std::string playerIdName;
394     // Raw RGB color to raw light ID
395     std::unordered_map<LightColor, int32_t /* rawLightId */> rawRgbIds;
396     // Map from player Id to raw light Id
397     std::unordered_map<int32_t, int32_t> playerIdLightIds;
398     // Set of Keyboard backlights
399     std::set<int32_t> keyboardBacklightIds;
400 
401     // Check raw lights
402     const std::vector<int32_t> rawLightIds = getDeviceContext().getRawLightIds();
403     // Map from raw light id to raw light info
404     std::unordered_map<int32_t, RawLightInfo> rawInfos;
405     for (const auto& rawId : rawLightIds) {
406         std::optional<RawLightInfo> rawInfo = getDeviceContext().getRawLightInfo(rawId);
407         if (!rawInfo.has_value()) {
408             continue;
409         }
410         rawInfos.insert_or_assign(rawId, rawInfo.value());
411         // Check if this is a group LEDs for player ID
412         // The name for the light has already been parsed and is the `function`
413         // value; for player ID lights the function is expected to be `player-#`.
414         // However, the Sony driver will use `sony#` instead on SIXAXIS
415         // gamepads.
416         std::regex lightPattern("(player|sony)-?([0-9]+)");
417         std::smatch results;
418         if (std::regex_match(rawInfo->name, results, lightPattern)) {
419             std::string commonName = results[1].str();
420             int32_t playerId = std::stoi(results[2]);
421             if (playerIdLightIds.empty()) {
422                 playerIdName = commonName;
423                 playerIdLightIds.insert_or_assign(playerId, rawId);
424             } else {
425                 // Make sure the player ID leds have common string name
426                 if (playerIdName.compare(commonName) == 0 &&
427                     playerIdLightIds.find(playerId) == playerIdLightIds.end()) {
428                     playerIdLightIds.insert_or_assign(playerId, rawId);
429                 }
430             }
431         }
432         // Check if this is a Keyboard backlight
433         if (rawInfo->flags.test(InputLightClass::KEYBOARD_BACKLIGHT)) {
434             keyboardBacklightIds.insert(rawId);
435         }
436         // Check if this is an LED of RGB light
437         if (rawInfo->flags.test(InputLightClass::RED)) {
438             hasRedLed = true;
439             rawRgbIds.emplace(LightColor::RED, rawId);
440         }
441         if (rawInfo->flags.test(InputLightClass::GREEN)) {
442             hasGreenLed = true;
443             rawRgbIds.emplace(LightColor::GREEN, rawId);
444         }
445         if (rawInfo->flags.test(InputLightClass::BLUE)) {
446             hasBlueLed = true;
447             rawRgbIds.emplace(LightColor::BLUE, rawId);
448         }
449         if (rawInfo->flags.test(InputLightClass::GLOBAL)) {
450             rawGlobalId = rawId;
451         }
452         ALOGD_IF(DEBUG_LIGHT_DETAILS, "Light rawId %d name %s max %d flags %s\n", rawInfo->id,
453                  rawInfo->name.c_str(), rawInfo->maxBrightness.value_or(MAX_BRIGHTNESS),
454                  rawInfo->flags.string().c_str());
455     }
456 
457     // Construct a player ID light
458     if (playerIdLightIds.size() > 1) {
459         std::unique_ptr<Light> light =
460                 std::make_unique<PlayerIdLight>(getDeviceContext(), playerIdName, ++mNextId,
461                                                 playerIdLightIds);
462         mLights.insert_or_assign(light->id, std::move(light));
463         // Remove these raw lights from raw light info as they've been used to compose a
464         // Player ID light, so we do not expose these raw lights as mono lights.
465         for (const auto& [playerId, rawId] : playerIdLightIds) {
466             rawInfos.erase(rawId);
467         }
468     }
469     // Construct a RGB light for composed RGB light
470     if (hasRedLed && hasGreenLed && hasBlueLed) {
471         ALOGD_IF(DEBUG_LIGHT_DETAILS, "Rgb light ids [%d, %d, %d]\n", rawRgbIds.at(LightColor::RED),
472                  rawRgbIds.at(LightColor::GREEN), rawRgbIds.at(LightColor::BLUE));
473         bool isKeyboardBacklight = keyboardBacklightIds.find(rawRgbIds.at(LightColor::RED)) !=
474                         keyboardBacklightIds.end() &&
475                 keyboardBacklightIds.find(rawRgbIds.at(LightColor::GREEN)) !=
476                         keyboardBacklightIds.end() &&
477                 keyboardBacklightIds.find(rawRgbIds.at(LightColor::BLUE)) !=
478                         keyboardBacklightIds.end() &&
479                 (!rawGlobalId.has_value() ||
480                  keyboardBacklightIds.find(rawGlobalId.value()) != keyboardBacklightIds.end());
481 
482         std::unique_ptr<Light> light =
483                 std::make_unique<RgbLight>(getDeviceContext(), ++mNextId,
484                                            isKeyboardBacklight
485                                                    ? InputDeviceLightType::KEYBOARD_BACKLIGHT
486                                                    : InputDeviceLightType::INPUT,
487                                            rawRgbIds, rawGlobalId);
488         mLights.insert_or_assign(light->id, std::move(light));
489         // Remove from raw light info as they've been composed a RBG light.
490         rawInfos.erase(rawRgbIds.at(LightColor::RED));
491         rawInfos.erase(rawRgbIds.at(LightColor::GREEN));
492         rawInfos.erase(rawRgbIds.at(LightColor::BLUE));
493         if (rawGlobalId.has_value()) {
494             rawInfos.erase(rawGlobalId.value());
495         }
496     }
497 
498     // Check the rest of raw light infos
499     for (const auto& [rawId, rawInfo] : rawInfos) {
500         InputDeviceLightType type;
501         if (keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()) {
502             type = InputDeviceLightType::KEYBOARD_BACKLIGHT;
503         } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) {
504             type = InputDeviceLightType::KEYBOARD_MIC_MUTE;
505         } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_VOLUME_MUTE)) {
506             type = InputDeviceLightType::KEYBOARD_VOLUME_MUTE;
507         } else {
508             type = InputDeviceLightType::INPUT;
509         }
510 
511         // If the node is multi-color led, construct a MULTI_COLOR light
512         if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
513             rawInfo.flags.test(InputLightClass::MULTI_INTENSITY)) {
514             ALOGD_IF(DEBUG_LIGHT_DETAILS, "Multicolor light Id %d name %s\n", rawInfo.id,
515                      rawInfo.name.c_str());
516             std::unique_ptr<Light> light =
517                     std::make_unique<MultiColorLight>(getDeviceContext(), rawInfo.name, ++mNextId,
518                                                       type, rawInfo.id);
519             mLights.insert_or_assign(light->id, std::move(light));
520             continue;
521         }
522         // Construct a Mono LED light
523         ALOGD_IF(DEBUG_LIGHT_DETAILS, "Mono light Id %d name %s\n", rawInfo.id,
524                  rawInfo.name.c_str());
525         std::unique_ptr<Light> light = std::make_unique<MonoLight>(getDeviceContext(), rawInfo.name,
526                                                                    ++mNextId, type, rawInfo.id);
527 
528         mLights.insert_or_assign(light->id, std::move(light));
529     }
530 }
531 
getBatteryCapacity(int batteryId)532 std::optional<int32_t> PeripheralController::getBatteryCapacity(int batteryId) {
533     return getDeviceContext().getBatteryCapacity(batteryId);
534 }
535 
getBatteryStatus(int batteryId)536 std::optional<int32_t> PeripheralController::getBatteryStatus(int batteryId) {
537     return getDeviceContext().getBatteryStatus(batteryId);
538 }
539 
setLightColor(int32_t lightId,int32_t color)540 bool PeripheralController::setLightColor(int32_t lightId, int32_t color) {
541     auto it = mLights.find(lightId);
542     if (it == mLights.end()) {
543         return false;
544     }
545     auto& light = it->second;
546     ALOGD_IF(DEBUG_LIGHT_DETAILS, "setLightColor lightId %d type %s color 0x%x", lightId,
547              ftl::enum_string(light->type).c_str(), color);
548     return light->setLightColor(color);
549 }
550 
getLightColor(int32_t lightId)551 std::optional<int32_t> PeripheralController::getLightColor(int32_t lightId) {
552     auto it = mLights.find(lightId);
553     if (it == mLights.end()) {
554         return std::nullopt;
555     }
556     auto& light = it->second;
557     std::optional<int32_t> color = light->getLightColor();
558     ALOGD_IF(DEBUG_LIGHT_DETAILS, "getLightColor lightId %d type %s color 0x%x", lightId,
559              ftl::enum_string(light->type).c_str(), color.value_or(0));
560     return color;
561 }
562 
setLightPlayerId(int32_t lightId,int32_t playerId)563 bool PeripheralController::setLightPlayerId(int32_t lightId, int32_t playerId) {
564     auto it = mLights.find(lightId);
565     if (it == mLights.end()) {
566         return false;
567     }
568     auto& light = it->second;
569     return light->setLightPlayerId(playerId);
570 }
571 
getLightPlayerId(int32_t lightId)572 std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) {
573     auto it = mLights.find(lightId);
574     if (it == mLights.end()) {
575         return std::nullopt;
576     }
577     auto& light = it->second;
578     return light->getLightPlayerId();
579 }
580 
getEventHubId() const581 int32_t PeripheralController::getEventHubId() const {
582     return getDeviceContext().getEventHubId();
583 }
584 } // namespace android
585