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
36namespace android {
37
38constexpr char kSensorDeviceFile[] = "/dev/nanohub";
39constexpr char kCommsDeviceFile[] = "/dev/nanohub_comms";
40constexpr char kLockDirectory[] = "/data/vendor/sensor/nanohub_lock";
41constexpr char kLockFile[] = "/data/vendor/sensor/nanohub_lock/lock";
42
43constexpr mode_t kLockDirPermissions = (S_IRUSR | S_IWUSR | S_IXUSR);
44
45constexpr auto kLockDelay = std::chrono::milliseconds(100);
46
47constexpr int kDeviceFileCount = 2;
48constexpr int kPollNoTimeout = -1;
49
50static 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
59static 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
66static 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
81static 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
96static 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
150AndroidContextHub::~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
163void AndroidContextHub::TerminateHandler() {
164    (void) unlink(kLockFile);
165}
166
167bool 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
209void AndroidContextHub::SetLoggingEnabled(bool logging_enabled) {
210    if (logging_enabled) {
211        LOGE("Logging is not supported on this platform");
212    }
213}
214
215ContextHub::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
237ContextHub::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
279bool 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
285bool 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
304bool 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
316bool 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
328bool 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
340bool 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
352bool 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
361ContextHub::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
389int 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
403const 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