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