Home | History | Annotate | Download | only in folio_daemon
      1 /*
      2  * Copyright 2017 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 <fcntl.h>
     18 #include <time.h>
     19 #include <unistd.h>
     20 #include <linux/input.h>
     21 #include <linux/uinput.h>
     22 #include <android/looper.h>
     23 #include <android/sensor.h>
     24 #include <cutils/log.h>
     25 
     26 // Hall-effect sensor type
     27 #define SENSOR_TYPE 33171016
     28 
     29 #define RETRY_LIMIT     120
     30 #define RETRY_PERIOD    30          // 30 seconds
     31 #define WARN_PERIOD     (time_t)300 // 5 minutes
     32 
     33 /*
     34  * This simple daemon listens for events from the Hall-effect sensor and writes
     35  * the appropriate SW_LID event to a uinput node. This allows the screen to be
     36  * locked with a magnetic folio case.
     37  */
     38 int main(void) {
     39     int uinputFd;
     40     int err;
     41     struct uinput_user_dev uidev;
     42     ASensorManager *sensorManager = nullptr;
     43     ASensorRef hallSensor;
     44     ALooper *looper;
     45     ASensorEventQueue *eventQueue = nullptr;
     46     time_t lastWarn = 0;
     47     int attemptCount = 0;
     48 
     49     ALOGI("Started");
     50 
     51     uinputFd = TEMP_FAILURE_RETRY(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
     52     if (uinputFd < 0) {
     53         ALOGE("Unable to open uinput node: %s", strerror(errno));
     54         goto out;
     55     }
     56 
     57     err = TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_EVBIT, EV_SW))
     58         | TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_EVBIT, EV_SYN))
     59         | TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_SET_SWBIT, SW_LID));
     60     if (err != 0) {
     61         ALOGE("Unable to enable SW_LID events: %s", strerror(errno));
     62         goto out;
     63     }
     64 
     65     memset(&uidev, 0, sizeof (uidev));
     66     snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-folio");
     67     uidev.id.bustype = BUS_VIRTUAL;
     68     uidev.id.vendor = 0;
     69     uidev.id.product = 0;
     70     uidev.id.version = 0;
     71 
     72     err = TEMP_FAILURE_RETRY(write(uinputFd, &uidev, sizeof (uidev)));
     73     if (err < 0) {
     74         ALOGE("Write user device to uinput node failed: %s", strerror(errno));
     75         goto out;
     76     }
     77 
     78     err = TEMP_FAILURE_RETRY(ioctl(uinputFd, UI_DEV_CREATE));
     79     if (err < 0) {
     80         ALOGE("Unable to create uinput device: %s", strerror(errno));
     81         goto out;
     82     }
     83 
     84     ALOGI("Successfully registered uinput-folio for SW_LID events");
     85 
     86     // Get Hall-effect sensor events from the NDK
     87     sensorManager = ASensorManager_getInstanceForPackage(nullptr);
     88     /*
     89      * As long as we are unable to get the sensor handle, periodically retry
     90      * and emit an error message at a low frequency to prevent high CPU usage
     91      * and log spam. If we simply exited with an error here, we would be
     92      * immediately restarted and fail in the same way indefinitely.
     93      */
     94     while (true) {
     95         time_t now = time(NULL);
     96         hallSensor = ASensorManager_getDefaultSensor(sensorManager,
     97                                                      SENSOR_TYPE);
     98         if (hallSensor != nullptr) {
     99             break;
    100         }
    101 
    102         if (++attemptCount >= RETRY_LIMIT) {
    103             ALOGE("Retries exhausted; exiting");
    104             goto out;
    105         } else if (now > lastWarn + WARN_PERIOD) {
    106             ALOGE("Unable to get Hall-effect sensor");
    107             lastWarn = now;
    108         }
    109 
    110         sleep(RETRY_PERIOD);
    111     }
    112 
    113     looper = ALooper_forThread();
    114     if (looper == nullptr) {
    115         looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    116     }
    117 
    118     eventQueue = ASensorManager_createEventQueue(sensorManager, looper, 0, NULL,
    119                                                  NULL);
    120     err = ASensorEventQueue_registerSensor(eventQueue, hallSensor,
    121                                            ASensor_getMinDelay(hallSensor),
    122                                            10000);
    123     if (err < 0) {
    124         ALOGE("Unable to register for Hall-effect sensor events");
    125         goto out;
    126     }
    127 
    128     ALOGI("Starting polling loop");
    129 
    130     // Polling loop
    131     while (ALooper_pollAll(-1, NULL, NULL, NULL) == 0) {
    132         int eventCount = 0;
    133         ASensorEvent sensorEvent;
    134         while (ASensorEventQueue_getEvents(eventQueue, &sensorEvent, 1) > 0) {
    135             // 1 means closed; 0 means open
    136             int isClosed = sensorEvent.data[0] > 0.0f ? 1 : 0;
    137             struct input_event event;
    138             event.type = EV_SW;
    139             event.code = SW_LID;
    140             event.value = isClosed;
    141             err = TEMP_FAILURE_RETRY(write(uinputFd, &event, sizeof (event)));
    142             if (err < 0) {
    143                 ALOGE("Write EV_SW to uinput node failed: %s", strerror(errno));
    144                 goto out;
    145             }
    146 
    147             // Force a flush with an EV_SYN
    148             event.type = EV_SYN;
    149             event.code = SYN_REPORT;
    150             event.value = 0;
    151             err = TEMP_FAILURE_RETRY(write(uinputFd, &event, sizeof (event)));
    152             if (err < 0) {
    153                 ALOGE("Write EV_SYN to uinput node failed: %s",
    154                       strerror(errno));
    155                 goto out;
    156             }
    157 
    158             ALOGI("Sent lid %s event", isClosed ? "closed" : "open");
    159             eventCount++;
    160         }
    161 
    162         if (eventCount == 0) {
    163             ALOGE("Poll returned with zero events: %s", strerror(errno));
    164             break;
    165         }
    166     }
    167 
    168 out:
    169     // Clean up
    170     if (sensorManager != nullptr && eventQueue != nullptr) {
    171         ASensorManager_destroyEventQueue(sensorManager, eventQueue);
    172     }
    173 
    174     if (uinputFd >= 0) {
    175         close(uinputFd);
    176     }
    177 
    178     // The loop can only be exited via failure or signal
    179     return 1;
    180 }
    181