1 /*
2  * Copyright (C) 2022 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 <aidl/android/hardware/light/BnLights.h>
18 #include <aidl/com/google/hardware/pixel/display/IDisplay.h>
19 #include <android-base/logging.h>
20 #include <android/binder_manager.h>
21 #include <android/binder_process.h>
22 #include <fcntl.h>
23 
24 #include "led_lut_calibrator.h"
25 
26 using ::aidl::android::hardware::light::BnLights;
27 using ::aidl::android::hardware::light::HwLight;
28 using ::aidl::android::hardware::light::HwLightState;
29 using ::aidl::android::hardware::light::ILights;
30 using ::aidl::android::hardware::light::LightType;
31 using ::ndk::ScopedAStatus;
32 using ::ndk::SharedRefBase;
33 using ::ndk::SpAIBinder;
34 
35 using aidl::com::google::hardware::pixel::display::IDisplay;
36 using PanelCalibrationStatus = aidl::com::google::hardware::pixel::display::PanelCalibrationStatus;
37 
38 static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
39 
40 char const *const GREEN_LED_FILE = "/sys/class/leds/green/brightness";
41 
42 enum {
43     ARGB_DAY   = 0x0000ff00,
44     ARGB_DUSK  = 0x0000c000,
45     ARGB_NIGHT = 0x00008000,
46     ARGB_OFF   = 0x00000000
47 };
48 
49 enum {
50     PWM_DAY_DEF   = 0x300,
51     PWM_DUSK_DEF  = 0x080,
52     PWM_NIGHT_DEF = 0x040,
53     PWM_OFF_DEF   = 0x000
54 };
55 
56 enum {
57     MODE_DAY   = 0xfff,
58     MODE_NIGHT = 0x000
59 };
60 
61 struct ARGBToPWM {
62     int argb;
63     int pwm;
64 };
65 
66 static struct ARGBToPWM pwmIds[] = {
67     {ARGB_DAY,   PWM_DAY_DEF},
68     {ARGB_DUSK,  PWM_DUSK_DEF},
69     {ARGB_NIGHT, PWM_NIGHT_DEF},
70     {ARGB_OFF,   PWM_OFF_DEF}
71 };
72 
sys_write_int(int fd,int value)73 static int sys_write_int(int fd, int value) {
74     char buffer[16];
75     size_t bytes;
76     ssize_t amount;
77 
78     bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);
79     if (bytes >= sizeof(buffer)) {
80         return -EINVAL;
81     }
82     amount = write(fd, buffer, bytes);
83 
84     return amount == -1 ? -errno : 0;
85 }
86 
WaitForDisplayService()87 static SpAIBinder WaitForDisplayService() {
88     LOG(INFO) << "waiting for display service to appear";
89     static std::mutex svc_mutex;
90     static SpAIBinder svc;
91     std::unique_lock<std::mutex> l(svc_mutex);
92     uint8_t retrycount = 0;
93 
94     if (svc != nullptr && AIBinder_isAlive(svc.get())) return svc;
95 
96     while (true && retrycount < 20) {
97         svc = SpAIBinder(AServiceManager_waitForService(
98             "com.google.hardware.pixel.display.IDisplay/default"));
99         if (svc != nullptr) {
100             LOG(INFO) << "A wild display service has appeared!";
101             return svc;
102         }
103         retrycount++;
104         sleep(2);
105     }
106 
107     LOG(ERROR) << "Failed to get the display service";
108     return NULL;
109 }
110 
111 class Lights : public BnLights {
112   private:
113     std::vector<HwLight> availableLights;
114     LedLutCalibrator *calibrator;
115 
addLight(LightType const type,int const ordinal)116     void addLight(LightType const type, int const ordinal) {
117         HwLight light{};
118         light.id = availableLights.size();
119         light.type = type;
120         light.ordinal = ordinal;
121         availableLights.emplace_back(light);
122     }
123 
writeLed(const char * path,int color)124     void writeLed(const char *path, int color) {
125         int fd = open(path, O_WRONLY);
126         if (fd < 0) {
127             LOG(ERROR) << "Failed to open LED device " << path << strerror(errno);
128             return;
129         }
130         sys_write_int(fd, color);
131         close(fd);
132     }
133 
134   public:
Lights()135     Lights() : BnLights() {
136         pthread_mutex_init(&g_lock, NULL);
137 
138         addLight(LightType::BACKLIGHT, 0);
139         addLight(LightType::KEYBOARD, 0);
140         addLight(LightType::BUTTONS, 0);
141         addLight(LightType::BATTERY, 0);
142         addLight(LightType::NOTIFICATIONS, 0);
143         addLight(LightType::ATTENTION, 0);
144         addLight(LightType::BLUETOOTH, 0);
145         addLight(LightType::WIFI, 0);
146         addLight(LightType::MICROPHONE, 0);
147         addLight(LightType::CAMERA, 0);
148 
149         SpAIBinder display_service = WaitForDisplayService();
150         auto ser = IDisplay::fromBinder(display_service);
151         PanelCalibrationStatus cal;
152         ser->getPanelCalibrationStatus(&cal);
153         calibrator =  new LedLutCalibrator(cal);
154 
155         int cal_color = 0;
156         cal_color = calibrator->GetByColorIntensity("green", MODE_DAY);
157         if (cal_color >= 0) {
158             pwmIds[0].pwm  = cal_color;
159         }
160         cal_color = calibrator->GetByColorIntensity("green", MODE_NIGHT);
161         if (cal_color >= 0) {
162             pwmIds[1].pwm  = cal_color << 1;
163             pwmIds[2].pwm  = cal_color;
164         }
165     }
166 
setLightState(int id,const HwLightState & state)167     ScopedAStatus setLightState(int id, const HwLightState &state) override {
168         if (!(0 <= id && id < availableLights.size())) {
169             LOG(ERROR) << "Light id " << (int32_t)id << " does not exist.";
170             return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
171         }
172 
173         HwLight const &light = availableLights[id];
174         switch (light.type) {
175             case LightType::CAMERA:
176                 for (size_t i = 0; i < sizeof(pwmIds) / sizeof(pwmIds[0]); i++) {
177                     if (state.color == pwmIds[i].argb) {
178                         pthread_mutex_lock(&g_lock);
179                         writeLed(GREEN_LED_FILE, pwmIds[i].pwm);
180                         pthread_mutex_unlock(&g_lock);
181                         break;
182                     }
183                 }
184                 break;
185             default:
186                 break;
187         }
188 
189         return ScopedAStatus::ok();
190     }
191 
getLights(std::vector<HwLight> * lights)192     ScopedAStatus getLights(std::vector<HwLight> *lights) override {
193         for (auto i = availableLights.begin(); i != availableLights.end(); i++) {
194             lights->push_back(*i);
195         }
196 
197         return ScopedAStatus::ok();
198     }
199 };
200 
main()201 int main() {
202     ABinderProcess_setThreadPoolMaxThreadCount(0);
203 
204     SpAIBinder display_service = WaitForDisplayService();
205     if (display_service == nullptr) {
206         return -1;
207     }
208 
209     std::shared_ptr<Lights> light = SharedRefBase::make<Lights>();
210 
211     const std::string instance = std::string() + ILights::descriptor + "/default";
212     binder_status_t status = AServiceManager_addService(light->asBinder().get(), instance.c_str());
213 
214     if (status != STATUS_OK) {
215         LOG(ERROR) << "Failed to register" << instance;
216     }
217 
218     ABinderProcess_joinThreadPool();
219 
220     return -1;  // should not reach
221 }
222