No idea what was the point of it, but it prevents us from listening to sensor events when device is asleep. Change-Id: Id7ce79e8cce051256ee15d8b8f342726af085976
162 lines
4.9 KiB
C++
162 lines
4.9 KiB
C++
/*
|
|
* SPDX-FileCopyrightText: 2017 The Android Open Source Project
|
|
* SPDX-FileCopyrightText: 2023-2025 The LineageOS Project
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <android/looper.h>
|
|
#include <android/sensor.h>
|
|
#include <cutils/log.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <linux/input.h>
|
|
#include <linux/uinput.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
// Hall-effect sensor type
|
|
#define SENSOR_TYPE 33171002
|
|
|
|
#define RETRY_LIMIT 120
|
|
#define RETRY_PERIOD 30 // 30 seconds
|
|
#define WARN_PERIOD (time_t)300 // 5 minutes
|
|
|
|
/*
|
|
* This simple daemon listens for events from the Hall-effect sensor and writes
|
|
* the appropriate SW_LID event to a uinput node. This allows the screen to be
|
|
* locked with a magnetic folio case.
|
|
*/
|
|
int main(void) {
|
|
int uinputFd;
|
|
int err;
|
|
struct uinput_user_dev uidev;
|
|
ASensorManager* sensorManager = nullptr;
|
|
ASensorRef hallSensor;
|
|
ALooper* looper;
|
|
ASensorEventQueue* eventQueue = nullptr;
|
|
time_t lastWarn = 0;
|
|
int attemptCount = 0;
|
|
|
|
ALOGI("Started");
|
|
|
|
uinputFd = TEMP_FAILURE_RETRY(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
|
|
if (uinputFd < 0) {
|
|
ALOGE("Unable to open uinput node: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
err = TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_EVBIT, EV_SW)) |
|
|
TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_EVBIT, EV_SYN)) |
|
|
TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_SWBIT, SW_LID));
|
|
if (err != 0) {
|
|
ALOGE("Unable to enable SW_LID events: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
memset(&uidev, 0, sizeof(uidev));
|
|
snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-folio");
|
|
uidev.id.bustype = BUS_VIRTUAL;
|
|
uidev.id.vendor = 0;
|
|
uidev.id.product = 0;
|
|
uidev.id.version = 0;
|
|
|
|
err = TEMP_FAILURE_RETRY(write(uinputFd, &uidev, sizeof(uidev)));
|
|
if (err < 0) {
|
|
ALOGE("Write user device to uinput node failed: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
err = TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_DEV_CREATE));
|
|
if (err < 0) {
|
|
ALOGE("Unable to create uinput device: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
ALOGI("Successfully registered uinput-folio for SW_LID events");
|
|
|
|
// Get Hall-effect sensor events from the NDK
|
|
sensorManager = ASensorManager_getInstanceForPackage(nullptr);
|
|
looper = ALooper_forThread();
|
|
if (looper == nullptr) {
|
|
looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
|
|
}
|
|
|
|
eventQueue = ASensorManager_createEventQueue(sensorManager, looper, 0, NULL, NULL);
|
|
|
|
/*
|
|
* As long as we are unable to get the sensor handle, periodically retry
|
|
* and emit an error message at a low frequency to prevent high CPU usage
|
|
* and log spam. If we simply exited with an error here, we would be
|
|
* immediately restarted and fail in the same way indefinitely.
|
|
*/
|
|
while (true) {
|
|
time_t now = time(NULL);
|
|
hallSensor = ASensorManager_getDefaultSensorEx(sensorManager, SENSOR_TYPE, true);
|
|
if (hallSensor != nullptr) {
|
|
break;
|
|
}
|
|
|
|
if (++attemptCount >= RETRY_LIMIT) {
|
|
ALOGE("Retries exhausted; exiting");
|
|
goto out;
|
|
} else if (now > lastWarn + WARN_PERIOD) {
|
|
ALOGE("Unable to get Hall-effect sensor");
|
|
lastWarn = now;
|
|
}
|
|
|
|
sleep(RETRY_PERIOD);
|
|
}
|
|
|
|
err = ASensorEventQueue_registerSensor(eventQueue, hallSensor, 0, 0);
|
|
if (err < 0) {
|
|
ALOGE("Unable to register for Hall-effect sensor events");
|
|
goto out;
|
|
}
|
|
|
|
ALOGI("Starting polling loop");
|
|
|
|
// Polling loop
|
|
while (ALooper_pollOnce(-1, NULL, NULL, NULL) > ALOOPER_POLL_TIMEOUT) {
|
|
ASensorEvent sensorEvent;
|
|
while (ASensorEventQueue_getEvents(eventQueue, &sensorEvent, 1) > 0) {
|
|
// 0 means closed; 1 means open
|
|
int isClosed = sensorEvent.data[0] > 0.0f ? 0 : 1;
|
|
struct input_event event;
|
|
event.type = EV_SW;
|
|
event.code = SW_LID;
|
|
event.value = isClosed;
|
|
err = TEMP_FAILURE_RETRY(write(uinputFd, &event, sizeof(event)));
|
|
if (err < 0) {
|
|
ALOGE("Write EV_SW to uinput node failed: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
// Force a flush with an EV_SYN
|
|
event.type = EV_SYN;
|
|
event.code = SYN_REPORT;
|
|
event.value = 0;
|
|
err = TEMP_FAILURE_RETRY(write(uinputFd, &event, sizeof(event)));
|
|
if (err < 0) {
|
|
ALOGE("Write EV_SYN to uinput node failed: %s", strerror(errno));
|
|
goto out;
|
|
}
|
|
|
|
ALOGI("Sent lid %s event", isClosed ? "closed" : "open");
|
|
}
|
|
}
|
|
|
|
out:
|
|
// Clean up
|
|
if (sensorManager != nullptr && eventQueue != nullptr) {
|
|
ASensorManager_destroyEventQueue(sensorManager, eventQueue);
|
|
}
|
|
|
|
if (uinputFd >= 0) {
|
|
close(uinputFd);
|
|
}
|
|
|
|
// The loop can only be exited via failure or signal
|
|
return 1;
|
|
}
|