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