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