1 /*
2  * Copyright (C) 2016 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 "androidcontexthub.h"
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <poll.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <sys/stat.h>
25 
26 #include <chrono>
27 #include <cstdint>
28 #include <cstdio>
29 #include <cstring>
30 #include <thread>
31 #include <vector>
32 
33 #include "calibrationfile.h"
34 #include "log.h"
35 
36 namespace android {
37 
38 constexpr char kSensorDeviceFile[] = "/dev/nanohub";
39 constexpr char kCommsDeviceFile[] = "/dev/nanohub_comms";
40 constexpr char kLockDirectory[] = "/data/vendor/sensor/nanohub_lock";
41 constexpr char kLockFile[] = "/data/vendor/sensor/nanohub_lock/lock";
42 
43 constexpr mode_t kLockDirPermissions = (S_IRUSR | S_IWUSR | S_IXUSR);
44 
45 constexpr auto kLockDelay = std::chrono::milliseconds(100);
46 
47 constexpr int kDeviceFileCount = 2;
48 constexpr int kPollNoTimeout = -1;
49 
50 static const std::vector<std::tuple<const char *, SensorType>> kCalibrationKeys = {
51     std::make_tuple("accel",     SensorType::Accel),
52     std::make_tuple("gyro",      SensorType::Gyro),
53     std::make_tuple("mag",       SensorType::Magnetometer),
54     std::make_tuple("proximity", SensorType::Proximity),
55     std::make_tuple("barometer", SensorType::Barometer),
56     std::make_tuple("light",     SensorType::AmbientLightSensor),
57 };
58 
AppendBytes(const void * data,size_t length,std::vector<uint8_t> & buffer)59 static void AppendBytes(const void *data, size_t length, std::vector<uint8_t>& buffer) {
60     const uint8_t *bytes = (const uint8_t *) data;
61     for (size_t i = 0; i < length; i++) {
62         buffer.push_back(bytes[i]);
63     }
64 }
65 
CopyInt32Array(const char * key,sp<JSONObject> json,std::vector<uint8_t> & bytes)66 static bool CopyInt32Array(const char *key,
67         sp<JSONObject> json, std::vector<uint8_t>& bytes) {
68     sp<JSONArray> array;
69     if (json->getArray(key, &array)) {
70         for (size_t i = 0; i < array->size(); i++) {
71             int32_t val = 0;
72             array->getInt32(i, &val);
73             AppendBytes(&val, sizeof(uint32_t), bytes);
74         }
75 
76         return true;
77     }
78     return false;
79 }
80 
CopyFloatArray(const char * key,sp<JSONObject> json,std::vector<uint8_t> & bytes)81 static bool CopyFloatArray(const char *key,
82         sp<JSONObject> json, std::vector<uint8_t>& bytes) {
83     sp<JSONArray> array;
84     if (json->getArray(key, &array)) {
85         for (size_t i = 0; i < array->size(); i++) {
86             float val = 0;
87             array->getFloat(i, &val);
88             AppendBytes(&val, sizeof(float), bytes);
89         }
90 
91         return true;
92     }
93     return false;
94 }
95 
GetCalibrationBytes(const char * key,SensorType sensor_type,std::vector<uint8_t> & bytes)96 static bool GetCalibrationBytes(const char *key, SensorType sensor_type,
97         std::vector<uint8_t>& bytes) {
98     bool success = true;
99     std::shared_ptr<CalibrationFile> cal_file = CalibrationFile::Instance();
100     if (!cal_file) {
101         return false;
102     }
103     auto json = cal_file->GetJSONObject();
104 
105     switch (sensor_type) {
106       case SensorType::Accel:
107       case SensorType::Gyro:
108         success = CopyInt32Array(key, json, bytes);
109         break;
110 
111       case SensorType::Magnetometer:
112         success = CopyFloatArray(key, json, bytes);
113         break;
114 
115       case SensorType::AmbientLightSensor:
116       case SensorType::Barometer: {
117         float value = 0;
118         success = json->getFloat(key, &value);
119         if (success) {
120             AppendBytes(&value, sizeof(float), bytes);
121         }
122         break;
123       }
124 
125       case SensorType::Proximity: {
126         // Proximity might be an int32 array with 4 values (CRGB) or a single
127         // int32 value - try both
128         success = CopyInt32Array(key, json, bytes);
129         if (!success) {
130             int32_t value = 0;
131             success = json->getInt32(key, &value);
132             if (success) {
133                 AppendBytes(&value, sizeof(int32_t), bytes);
134             }
135         }
136         break;
137       }
138 
139       default:
140         // If this log message gets printed, code needs to be added in this
141         // switch statement
142         LOGE("Missing sensor type to calibration data mapping sensor %d",
143              static_cast<int>(sensor_type));
144         success = false;
145     }
146 
147     return success;
148 }
149 
~AndroidContextHub()150 AndroidContextHub::~AndroidContextHub() {
151     if (unlink(kLockFile) < 0) {
152         LOGE("Couldn't remove lock file: %s", strerror(errno));
153     }
154     if (sensor_fd_ >= 0) {
155         DisableActiveSensors();
156         (void) close(sensor_fd_);
157     }
158     if (comms_fd_ >= 0) {
159         (void) close(comms_fd_);
160     }
161 }
162 
TerminateHandler()163 void AndroidContextHub::TerminateHandler() {
164     (void) unlink(kLockFile);
165 }
166 
Initialize()167 bool AndroidContextHub::Initialize() {
168     // Acquire a lock on nanohub, so the HAL read threads won't take our events.
169     // We need to delay after creating the file to have good confidence that
170     // the HALs noticed the lock file creation.
171     if (access(kLockDirectory, F_OK) < 0) {
172         if (mkdir(kLockDirectory, kLockDirPermissions) < 0 && errno != EEXIST) {
173             LOGE("Couldn't create lock directory: %s", strerror(errno));
174         }
175     }
176     int lock_fd = open(kLockFile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
177     if (lock_fd < 0) {
178         LOGE("Couldn't create lock file: %s", strerror(errno));
179         if (errno != EEXIST) {
180             return false;
181         }
182     } else {
183         close(lock_fd);
184         std::this_thread::sleep_for(kLockDelay);
185         LOGD("Lock sleep complete");
186     }
187 
188     // Sensor device file is used for sensor requests, e.g. configure, etc., and
189     // returns sensor events
190     sensor_fd_ = open(kSensorDeviceFile, O_RDWR);
191     if (sensor_fd_ < 0) {
192         LOGE("Couldn't open device file: %s", strerror(errno));
193         return false;
194     }
195 
196     // The comms device file is used for more generic communication with
197     // nanoapps. Calibration results are returned through this channel.
198     comms_fd_ = open(kCommsDeviceFile, O_RDONLY);
199     if (comms_fd_ < 0) {
200         // TODO(bduddie): Currently informational only, as the kernel change
201         // that adds this device file is not available/propagated yet.
202         // Eventually this should be an error.
203         LOGI("Couldn't open comms device file: %s", strerror(errno));
204     }
205 
206     return true;
207 }
208 
SetLoggingEnabled(bool logging_enabled)209 void AndroidContextHub::SetLoggingEnabled(bool logging_enabled) {
210     if (logging_enabled) {
211         LOGE("Logging is not supported on this platform");
212     }
213 }
214 
WriteEvent(const std::vector<uint8_t> & message)215 ContextHub::TransportResult AndroidContextHub::WriteEvent(
216         const std::vector<uint8_t>& message) {
217     ContextHub::TransportResult result;
218 
219     LOGD("Writing %zu bytes", message.size());
220     LOGD_BUF(message.data(), message.size());
221     int ret = write(sensor_fd_, message.data(), message.size());
222     if (ret == -1) {
223         LOGE("Couldn't write %zu bytes to device file: %s", message.size(),
224              strerror(errno));
225         result = TransportResult::GeneralFailure;
226     } else if (ret != (int) message.size()) {
227         LOGW("Write returned %d, expected %zu", ret, message.size());
228         result = TransportResult::GeneralFailure;
229     } else {
230         LOGD("Successfully sent event");
231         result = TransportResult::Success;
232     }
233 
234     return result;
235 }
236 
ReadEvent(std::vector<uint8_t> & message,int timeout_ms)237 ContextHub::TransportResult AndroidContextHub::ReadEvent(
238         std::vector<uint8_t>& message, int timeout_ms) {
239     ContextHub::TransportResult result = TransportResult::GeneralFailure;
240 
241     struct pollfd pollfds[kDeviceFileCount];
242     int fd_count = ResetPollFds(pollfds, kDeviceFileCount);
243 
244     int timeout = timeout_ms > 0 ? timeout_ms : kPollNoTimeout;
245     int ret = poll(pollfds, fd_count, timeout);
246     if (ret < 0) {
247         LOGE("Polling failed: %s", strerror(errno));
248         if (errno == EINTR) {
249             result = TransportResult::Canceled;
250         }
251     } else if (ret == 0) {
252         LOGD("Poll timed out");
253         result = TransportResult::Timeout;
254     } else {
255         int read_fd = -1;
256         for (int i = 0; i < kDeviceFileCount; i++) {
257             if (pollfds[i].revents & POLLIN) {
258                 read_fd = pollfds[i].fd;
259                 break;
260             }
261         }
262 
263         if (read_fd == sensor_fd_) {
264             LOGD("Data ready on sensors device file");
265         } else if (read_fd == comms_fd_) {
266             LOGD("Data ready on comms device file");
267         }
268 
269         if (read_fd >= 0) {
270             result = ReadEventFromFd(read_fd, message);
271         } else {
272             LOGE("Poll returned but none of expected files are ready");
273         }
274     }
275 
276     return result;
277 }
278 
FlashSensorHub(const std::vector<uint8_t> & bytes)279 bool AndroidContextHub::FlashSensorHub(const std::vector<uint8_t>& bytes) {
280     (void)bytes;
281     LOGE("Flashing is not supported on this platform");
282     return false;
283 }
284 
LoadCalibration()285 bool AndroidContextHub::LoadCalibration() {
286     std::vector<uint8_t> cal_data;
287     bool success = true;
288 
289     for (size_t i = 0; success && i < kCalibrationKeys.size(); i++) {
290         std::string key;
291         SensorType sensor_type;
292 
293         std::tie(key, sensor_type) = kCalibrationKeys[i];
294         if (GetCalibrationBytes(key.c_str(), sensor_type, cal_data)) {
295             success = SendCalibrationData(sensor_type, cal_data);
296         }
297 
298         cal_data.clear();
299     }
300 
301     return success;
302 }
303 
SetCalibration(SensorType sensor_type,int32_t data)304 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t data) {
305     LOGI("Setting calibration for sensor %d (%s) to %d",
306          static_cast<int>(sensor_type),
307          ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
308     auto cal_file = CalibrationFile::Instance();
309     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
310     if (cal_file && key) {
311         return cal_file->SetSingleAxis(key, data);
312     }
313     return false;
314 }
315 
SetCalibration(SensorType sensor_type,float data)316 bool AndroidContextHub::SetCalibration(SensorType sensor_type, float data) {
317     LOGI("Setting calibration for sensor %d (%s) to %f",
318          static_cast<int>(sensor_type),
319          ContextHub::SensorTypeToAbbrevName(sensor_type).c_str(), data);
320     auto cal_file = CalibrationFile::Instance();
321     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
322     if (cal_file && key) {
323         return cal_file->SetSingleAxis(key, data);
324     }
325     return false;
326 }
327 
SetCalibration(SensorType sensor_type,int32_t x,int32_t y,int32_t z)328 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
329         int32_t y, int32_t z) {
330     LOGI("Setting calibration for %d to %d %d %d", static_cast<int>(sensor_type),
331          x, y, z);
332     auto cal_file = CalibrationFile::Instance();
333     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
334     if (cal_file && key) {
335         return cal_file->SetTripleAxis(key, x, y, z);
336     }
337     return false;
338 }
339 
SetCalibration(SensorType sensor_type,int32_t x,int32_t y,int32_t z,int32_t w)340 bool AndroidContextHub::SetCalibration(SensorType sensor_type, int32_t x,
341         int32_t y, int32_t z, int32_t w) {
342     LOGI("Setting calibration for %d to %d %d %d %d", static_cast<int>(sensor_type),
343          x, y, z, w);
344     auto cal_file = CalibrationFile::Instance();
345     const char *key = AndroidContextHub::SensorTypeToCalibrationKey(sensor_type);
346     if (cal_file && key) {
347         return cal_file->SetFourAxis(key, x, y, z, w);
348     }
349     return false;
350 }
351 
SaveCalibration()352 bool AndroidContextHub::SaveCalibration() {
353     LOGI("Saving calibration data");
354     auto cal_file = CalibrationFile::Instance();
355     if (cal_file) {
356         return cal_file->Save();
357     }
358     return false;
359 }
360 
ReadEventFromFd(int fd,std::vector<uint8_t> & message)361 ContextHub::TransportResult AndroidContextHub::ReadEventFromFd(
362         int fd, std::vector<uint8_t>& message) {
363     ContextHub::TransportResult result = TransportResult::GeneralFailure;
364 
365     // Set the size to the maximum, so when we resize later, it's always a
366     // shrink (otherwise it will end up clearing the bytes)
367     message.resize(message.capacity());
368 
369     LOGD("Calling into read()");
370     int ret = read(fd, message.data(), message.capacity());
371     if (ret < 0) {
372         LOGE("Couldn't read from device file: %s", strerror(errno));
373         if (errno == EINTR) {
374             result = TransportResult::Canceled;
375         }
376     } else if (ret == 0) {
377         // We might need to handle this specially, if the driver implements this
378         // to mean something specific
379         LOGE("Read unexpectedly returned 0 bytes");
380     } else {
381         message.resize(ret);
382         LOGD_VEC(message);
383         result = TransportResult::Success;
384     }
385 
386     return result;
387 }
388 
ResetPollFds(struct pollfd * pfds,size_t count)389 int AndroidContextHub::ResetPollFds(struct pollfd *pfds, size_t count) {
390     memset(pfds, 0, sizeof(struct pollfd) * count);
391     pfds[0].fd = sensor_fd_;
392     pfds[0].events = POLLIN;
393 
394     int nfds = 1;
395     if (count > 1 && comms_fd_ >= 0) {
396         pfds[1].fd = comms_fd_;
397         pfds[1].events = POLLIN;
398         nfds++;
399     }
400     return nfds;
401 }
402 
SensorTypeToCalibrationKey(SensorType sensor_type)403 const char *AndroidContextHub::SensorTypeToCalibrationKey(SensorType sensor_type) {
404     for (size_t i = 0; i < kCalibrationKeys.size(); i++) {
405         const char *key;
406         SensorType sensor_type_for_key;
407 
408         std::tie(key, sensor_type_for_key) = kCalibrationKeys[i];
409         if (sensor_type == sensor_type_for_key) {
410             return key;
411         }
412     }
413 
414     LOGE("No calibration key mapping for sensor type %d",
415          static_cast<int>(sensor_type));
416     return nullptr;
417 }
418 
419 }  // namespace android
420