/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "nanohub" #include "hubconnection.h" #include "file.h" #include "JSONObject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define APP_ID_GET_VENDOR(appid) ((appid) >> 24) #define APP_ID_MAKE(vendor, app) ((((uint64_t)(vendor)) << 24) | ((app) & 0x00FFFFFF)) #define APP_ID_VENDOR_GOOGLE 0x476f6f676cULL // "Googl" #define APP_ID_APP_BMI160 2 #define APP_ID_APP_WRIST_TILT_DETECT 0x1005 #define APP_ID_APP_GAZE_DETECT 0x1009 #define APP_ID_APP_UNGAZE_DETECT 0x100a #define SENS_TYPE_TO_EVENT(_sensorType) (EVT_NO_FIRST_SENSOR_EVENT + (_sensorType)) #define NANOHUB_FILE_PATH "/dev/nanohub" #define NANOHUB_LOCK_DIR "/data/vendor/sensor/nanohub_lock" #define NANOHUB_LOCK_FILE NANOHUB_LOCK_DIR "/lock" #define MAG_BIAS_FILE_PATH "/sys/class/power_supply/battery/compass_compensation" #define DOUBLE_TOUCH_FILE_PATH "/sys/android_touch/synaptics_rmi4_dsx/wake_event" #define NANOHUB_LOCK_DIR_PERMS (S_IRUSR | S_IWUSR | S_IXUSR) #define SENSOR_RATE_ONCHANGE 0xFFFFFF01UL #define SENSOR_RATE_ONESHOT 0xFFFFFF02UL #define MIN_MAG_SQ (10.0f * 10.0f) #define MAX_MAG_SQ (80.0f * 80.0f) #define OS_LOG_EVENT 0x474F4C41 // ascii: ALOG #define MAX_RETRY_CNT 5 #ifdef LID_STATE_REPORTING_ENABLED const char LID_STATE_PROPERTY[] = "sensors.contexthub.lid_state"; const char LID_STATE_UNKNOWN[] = "unknown"; const char LID_STATE_OPEN[] = "open"; const char LID_STATE_CLOSED[] = "closed"; #endif // LID_STATE_REPORTING_ENABLED constexpr int HUBCONNECTION_SCHED_FIFO_PRIORITY = 3; static const uint32_t delta_time_encoded = 1; static const uint32_t delta_time_shift_table[2] = {9, 0}; namespace android { // static Mutex HubConnection::sInstanceLock; // static HubConnection *HubConnection::sInstance = NULL; HubConnection *HubConnection::getInstance() { Mutex::Autolock autoLock(sInstanceLock); if (sInstance == NULL) { sInstance = new HubConnection; } return sInstance; } static bool isWakeEvent(int32_t sensor) { switch (sensor) { case COMMS_SENSOR_DOUBLE_TOUCH: case COMMS_SENSOR_DOUBLE_TWIST: case COMMS_SENSOR_GESTURE: case COMMS_SENSOR_PROXIMITY: case COMMS_SENSOR_SIGNIFICANT_MOTION: case COMMS_SENSOR_TILT: return true; default: return false; } } HubConnection::HubConnection() : Thread(false /* canCallJava */), mRing(10 *1024), mScaleAccel(1.0f), mScaleMag(1.0f), mStepCounterOffset(0ull), mLastStepCount(0ull) { mMagBias[0] = mMagBias[1] = mMagBias[2] = 0.0f; mMagAccuracy = SENSOR_STATUS_UNRELIABLE; mMagAccuracyRestore = SENSOR_STATUS_UNRELIABLE; mGyroBias[0] = mGyroBias[1] = mGyroBias[2] = 0.0f; mAccelBias[0] = mAccelBias[1] = mAccelBias[2] = 0.0f; mAccelEnabledBias[0] = mAccelEnabledBias[1] = mAccelEnabledBias[2] = 0.0f; mAccelEnabledBiasStored = true; memset(&mGyroOtcData, 0, sizeof(mGyroOtcData)); mLefty.accel = false; mLefty.gyro = false; mLefty.hub = false; memset(&mSensorState, 0x00, sizeof(mSensorState)); mFd = open(NANOHUB_FILE_PATH, O_RDWR); mPollFds[0].fd = mFd; mPollFds[0].events = POLLIN; mPollFds[0].revents = 0; mNumPollFds = 1; mWakelockHeld = false; mWakeEventCount = 0; mWriteFailures = 0; initNanohubLock(); #ifdef USB_MAG_BIAS_REPORTING_ENABLED mUsbMagBias = 0; mMagBiasPollIndex = -1; int magBiasFd = open(MAG_BIAS_FILE_PATH, O_RDONLY); if (magBiasFd < 0) { ALOGW("Mag bias file open failed: %s", strerror(errno)); } else { mPollFds[mNumPollFds].fd = magBiasFd; mPollFds[mNumPollFds].events = 0; mPollFds[mNumPollFds].revents = 0; mMagBiasPollIndex = mNumPollFds; mNumPollFds++; } #endif // USB_MAG_BIAS_REPORTING_ENABLED #ifdef DOUBLE_TOUCH_ENABLED mDoubleTouchPollIndex = -1; int doubleTouchFd = open(DOUBLE_TOUCH_FILE_PATH, O_RDONLY); if (doubleTouchFd < 0) { ALOGW("Double touch file open failed: %s", strerror(errno)); } else { mPollFds[mNumPollFds].fd = doubleTouchFd; mPollFds[mNumPollFds].events = 0; mPollFds[mNumPollFds].revents = 0; mDoubleTouchPollIndex = mNumPollFds; mNumPollFds++; } #endif // DOUBLE_TOUCH_ENABLED mSensorState[COMMS_SENSOR_ACCEL].sensorType = SENS_TYPE_ACCEL; mSensorState[COMMS_SENSOR_ACCEL].alt[0] = COMMS_SENSOR_ACCEL_UNCALIBRATED; mSensorState[COMMS_SENSOR_ACCEL].alt[1] = COMMS_SENSOR_ACCEL_WRIST_AWARE; mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].sensorType = SENS_TYPE_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].primary = COMMS_SENSOR_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].alt[0] = COMMS_SENSOR_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].alt[1] = COMMS_SENSOR_ACCEL_WRIST_AWARE; mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].sensorType = SENS_TYPE_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].primary = COMMS_SENSOR_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].alt[0] = COMMS_SENSOR_ACCEL; mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].alt[1] = COMMS_SENSOR_ACCEL_UNCALIBRATED; mSensorState[COMMS_SENSOR_GYRO].sensorType = SENS_TYPE_GYRO; mSensorState[COMMS_SENSOR_GYRO].alt[0] = COMMS_SENSOR_GYRO_UNCALIBRATED; mSensorState[COMMS_SENSOR_GYRO].alt[1] = COMMS_SENSOR_GYRO_WRIST_AWARE; mSensorState[COMMS_SENSOR_GYRO_UNCALIBRATED].sensorType = SENS_TYPE_GYRO; mSensorState[COMMS_SENSOR_GYRO_UNCALIBRATED].primary = COMMS_SENSOR_GYRO; mSensorState[COMMS_SENSOR_GYRO_UNCALIBRATED].alt[0] = COMMS_SENSOR_GYRO; mSensorState[COMMS_SENSOR_GYRO_UNCALIBRATED].alt[1] = COMMS_SENSOR_GYRO_WRIST_AWARE; mSensorState[COMMS_SENSOR_GYRO_WRIST_AWARE].sensorType = SENS_TYPE_GYRO; mSensorState[COMMS_SENSOR_GYRO_WRIST_AWARE].primary = COMMS_SENSOR_GYRO; mSensorState[COMMS_SENSOR_GYRO_WRIST_AWARE].alt[0] = COMMS_SENSOR_GYRO; mSensorState[COMMS_SENSOR_GYRO_WRIST_AWARE].alt[1] = COMMS_SENSOR_GYRO_UNCALIBRATED; mSensorState[COMMS_SENSOR_MAG].sensorType = SENS_TYPE_MAG; mSensorState[COMMS_SENSOR_MAG].alt[0] = COMMS_SENSOR_MAG_UNCALIBRATED; mSensorState[COMMS_SENSOR_MAG_UNCALIBRATED].sensorType = SENS_TYPE_MAG; mSensorState[COMMS_SENSOR_MAG_UNCALIBRATED].primary = COMMS_SENSOR_MAG; mSensorState[COMMS_SENSOR_MAG_UNCALIBRATED].alt[0] = COMMS_SENSOR_MAG; mSensorState[COMMS_SENSOR_LIGHT].sensorType = SENS_TYPE_ALS; mSensorState[COMMS_SENSOR_PROXIMITY].sensorType = SENS_TYPE_PROX; mSensorState[COMMS_SENSOR_PRESSURE].sensorType = SENS_TYPE_BARO; mSensorState[COMMS_SENSOR_TEMPERATURE].sensorType = SENS_TYPE_TEMP; mSensorState[COMMS_SENSOR_AMBIENT_TEMPERATURE].sensorType = SENS_TYPE_AMBIENT_TEMP; mSensorState[COMMS_SENSOR_ORIENTATION].sensorType = SENS_TYPE_ORIENTATION; mSensorState[COMMS_SENSOR_WINDOW_ORIENTATION].sensorType = SENS_TYPE_WIN_ORIENTATION; mSensorState[COMMS_SENSOR_WINDOW_ORIENTATION].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_STEP_DETECTOR].sensorType = SENS_TYPE_STEP_DETECT; mSensorState[COMMS_SENSOR_STEP_DETECTOR].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_STEP_COUNTER].sensorType = SENS_TYPE_STEP_COUNT; mSensorState[COMMS_SENSOR_SIGNIFICANT_MOTION].sensorType = SENS_TYPE_SIG_MOTION; mSensorState[COMMS_SENSOR_SIGNIFICANT_MOTION].rate = SENSOR_RATE_ONESHOT; mSensorState[COMMS_SENSOR_GRAVITY].sensorType = SENS_TYPE_GRAVITY; mSensorState[COMMS_SENSOR_LINEAR_ACCEL].sensorType = SENS_TYPE_LINEAR_ACCEL; mSensorState[COMMS_SENSOR_ROTATION_VECTOR].sensorType = SENS_TYPE_ROTATION_VECTOR; mSensorState[COMMS_SENSOR_GEO_MAG].sensorType = SENS_TYPE_GEO_MAG_ROT_VEC; mSensorState[COMMS_SENSOR_GAME_ROTATION_VECTOR].sensorType = SENS_TYPE_GAME_ROT_VECTOR; mSensorState[COMMS_SENSOR_HALL].sensorType = SENS_TYPE_HALL; mSensorState[COMMS_SENSOR_HALL].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_SYNC].sensorType = SENS_TYPE_VSYNC; mSensorState[COMMS_SENSOR_SYNC].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_TILT].sensorType = SENS_TYPE_TILT; mSensorState[COMMS_SENSOR_TILT].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_GESTURE].sensorType = SENS_TYPE_GESTURE; mSensorState[COMMS_SENSOR_GESTURE].rate = SENSOR_RATE_ONESHOT; mSensorState[COMMS_SENSOR_DOUBLE_TWIST].sensorType = SENS_TYPE_DOUBLE_TWIST; mSensorState[COMMS_SENSOR_DOUBLE_TWIST].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_DOUBLE_TAP].sensorType = SENS_TYPE_DOUBLE_TAP; mSensorState[COMMS_SENSOR_DOUBLE_TAP].rate = SENSOR_RATE_ONCHANGE; mSensorState[COMMS_SENSOR_WRIST_TILT].sensorType = SENS_TYPE_WRIST_TILT; mSensorState[COMMS_SENSOR_DOUBLE_TOUCH].sensorType = SENS_TYPE_DOUBLE_TOUCH; mSensorState[COMMS_SENSOR_DOUBLE_TOUCH].rate = SENSOR_RATE_ONESHOT; mSensorState[COMMS_SENSOR_GAZE].sensorType = SENS_TYPE_GAZE; mSensorState[COMMS_SENSOR_GAZE].rate = SENSOR_RATE_ONESHOT; mSensorState[COMMS_SENSOR_UNGAZE].sensorType = SENS_TYPE_UNGAZE; mSensorState[COMMS_SENSOR_UNGAZE].rate = SENSOR_RATE_ONESHOT; mSensorState[COMMS_SENSOR_HUMIDITY].sensorType = SENS_TYPE_HUMIDITY; #ifdef LID_STATE_REPORTING_ENABLED initializeUinputNode(); // set initial lid state if (property_set(LID_STATE_PROPERTY, LID_STATE_UNKNOWN) < 0) { ALOGW("could not set lid_state property"); } // enable hall sensor for folio if (mFd >= 0) { queueActivate(COMMS_SENSOR_HALL, true /* enable */); } #endif // LID_STATE_REPORTING_ENABLED #ifdef DIRECT_REPORT_ENABLED mDirectChannelHandle = 1; mSensorToChannel.emplace(COMMS_SENSOR_ACCEL, std::unordered_map()); mSensorToChannel.emplace(COMMS_SENSOR_GYRO, std::unordered_map()); mSensorToChannel.emplace(COMMS_SENSOR_MAG, std::unordered_map()); mSensorToChannel.emplace(COMMS_SENSOR_ACCEL_UNCALIBRATED, std::unordered_map()); mSensorToChannel.emplace(COMMS_SENSOR_GYRO_UNCALIBRATED, std::unordered_map()); mSensorToChannel.emplace(COMMS_SENSOR_MAG_UNCALIBRATED, std::unordered_map()); #endif // DIRECT_REPORT_ENABLED } HubConnection::~HubConnection() { close(mFd); } void HubConnection::onFirstRef() { run("HubConnection", PRIORITY_URGENT_DISPLAY); enableSchedFifoMode(); } // Set main thread to SCHED_FIFO to lower sensor event latency when system is under load void HubConnection::enableSchedFifoMode() { struct sched_param param = {0}; param.sched_priority = HUBCONNECTION_SCHED_FIFO_PRIORITY; if (sched_setscheduler(getTid(), SCHED_FIFO | SCHED_RESET_ON_FORK, ¶m) != 0) { ALOGW("Couldn't set SCHED_FIFO for HubConnection thread"); } } status_t HubConnection::initCheck() const { return mFd < 0 ? UNKNOWN_ERROR : OK; } status_t HubConnection::getAliveCheck() { return OK; } static sp readSettings(File *file) { off64_t size = file->seekTo(0, SEEK_END); file->seekTo(0, SEEK_SET); sp root; if (size > 0) { char *buf = (char *)malloc(size); CHECK_EQ(file->read(buf, size), (ssize_t)size); file->seekTo(0, SEEK_SET); sp in = JSONCompound::Parse(buf, size); free(buf); buf = NULL; if (in != NULL && in->isObject()) { root = (JSONObject *)in.get(); } } if (root == NULL) { root = new JSONObject; } return root; } static bool getCalibrationInt32( const sp &settings, const char *key, int32_t *out, size_t numArgs) { sp array; for (size_t i = 0; i < numArgs; i++) { out[i] = 0; } if (!settings->getArray(key, &array)) { return false; } else { for (size_t i = 0; i < numArgs; i++) { if (!array->getInt32(i, &out[i])) { return false; } } } return true; } static bool getCalibrationFloat( const sp &settings, const char *key, float out[3]) { sp array; for (size_t i = 0; i < 3; i++) { out[i] = 0.0f; } if (!settings->getArray(key, &array)) { return false; } else { for (size_t i = 0; i < 3; i++) { if (!array->getFloat(i, &out[i])) { return false; } } } return true; } static std::vector getInt32Setting(const sp &settings, const char *key) { std::vector ret; sp array; if (settings->getArray(key, &array)) { ret.resize(array->size()); for (size_t i = 0; i < array->size(); ++i) { array->getInt32(i, &ret[i]); } } return ret; } static std::vector getFloatSetting(const sp &settings, const char *key) { std::vector ret; sp array; if (settings->getArray(key, &array)) { ret.resize(array->size()); for (size_t i = 0; i < array->size(); ++i) { array->getFloat(i, &ret[i]); } } return ret; } static void loadSensorSettings(sp* settings, sp* saved_settings) { File settings_file(CONTEXTHUB_SETTINGS_PATH, "r"); File saved_settings_file(CONTEXTHUB_SAVED_SETTINGS_PATH, "r"); status_t err; if ((err = settings_file.initCheck()) != OK) { ALOGW("settings file open failed: %d (%s)", err, strerror(-err)); *settings = new JSONObject; } else { *settings = readSettings(&settings_file); } if ((err = saved_settings_file.initCheck()) != OK) { ALOGW("saved settings file open failed: %d (%s)", err, strerror(-err)); *saved_settings = new JSONObject; } else { *saved_settings = readSettings(&saved_settings_file); } } void HubConnection::saveSensorSettings() const { File saved_settings_file(CONTEXTHUB_SAVED_SETTINGS_PATH, "w"); sp settingsObject = new JSONObject; status_t err; if ((err = saved_settings_file.initCheck()) != OK) { ALOGW("saved settings file open failed %d (%s)", err, strerror(-err)); return; } // Build a settings object. sp magArray = new JSONArray; #ifdef USB_MAG_BIAS_REPORTING_ENABLED magArray->addFloat(mMagBias[0] + mUsbMagBias); #else magArray->addFloat(mMagBias[0]); #endif // USB_MAG_BIAS_REPORTING_ENABLED magArray->addFloat(mMagBias[1]); magArray->addFloat(mMagBias[2]); settingsObject->setArray(MAG_BIAS_TAG, magArray); // Add gyro settings sp gyroArray = new JSONArray; gyroArray->addFloat(mGyroBias[0]); gyroArray->addFloat(mGyroBias[1]); gyroArray->addFloat(mGyroBias[2]); settingsObject->setArray(GYRO_SW_BIAS_TAG, gyroArray); // Add accel settings sp accelArray = new JSONArray; accelArray->addFloat(mAccelBias[0]); accelArray->addFloat(mAccelBias[1]); accelArray->addFloat(mAccelBias[2]); settingsObject->setArray(ACCEL_SW_BIAS_TAG, accelArray); // Add overtemp calibration values for gyro sp gyroOtcDataArray = new JSONArray; const float *f; size_t i; for (f = reinterpret_cast(&mGyroOtcData), i = 0; i < sizeof(mGyroOtcData)/sizeof(float); ++i, ++f) { gyroOtcDataArray->addFloat(*f); } settingsObject->setArray(GYRO_OTC_DATA_TAG, gyroOtcDataArray); // Write the JSON string to disk. AString serializedSettings = settingsObject->toString(); size_t size = serializedSettings.size(); if ((err = saved_settings_file.write(serializedSettings.c_str(), size)) != (ssize_t)size) { ALOGW("saved settings file write failed %d (%s)", err, strerror(-err)); } } ssize_t HubConnection::sendCmd(const void *buf, size_t count) { ssize_t ret; int retryCnt = 0; do { ret = TEMP_FAILURE_RETRY(::write(mFd, buf, count)); } while (ret == 0 && retryCnt++ < MAX_RETRY_CNT); if (retryCnt > 0) ALOGW("sendCmd: retry: count=%zu, ret=%zd, retryCnt=%d", count, ret, retryCnt); else if (ret < 0 || static_cast(ret) != count) ALOGW("sendCmd: failed: count=%zu, ret=%zd, errno=%d", count, ret, errno); return ret; } void HubConnection::setLeftyMode(bool enable) { struct MsgCmd *cmd; size_t ret; Mutex::Autolock autoLock(mLock); if (enable == mLefty.hub) return; cmd = (struct MsgCmd *)malloc(sizeof(struct MsgCmd) + sizeof(bool)); if (cmd) { cmd->evtType = EVT_APP_FROM_HOST; cmd->msg.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, APP_ID_APP_GAZE_DETECT); cmd->msg.dataLen = sizeof(bool); memcpy((bool *)(cmd+1), &enable, sizeof(bool)); ret = sendCmd(cmd, sizeof(*cmd) + sizeof(bool)); if (ret == sizeof(*cmd) + sizeof(bool)) ALOGV("setLeftyMode: lefty (gaze) = %s\n", (enable ? "true" : "false")); else ALOGE("setLeftyMode: failed to send command lefty (gaze) = %s\n", (enable ? "true" : "false")); cmd->msg.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, APP_ID_APP_UNGAZE_DETECT); ret = sendCmd(cmd, sizeof(*cmd) + sizeof(bool)); if (ret == sizeof(*cmd) + sizeof(bool)) ALOGV("setLeftyMode: lefty (ungaze) = %s\n", (enable ? "true" : "false")); else ALOGE("setLeftyMode: failed to send command lefty (ungaze) = %s\n", (enable ? "true" : "false")); cmd->msg.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, APP_ID_APP_WRIST_TILT_DETECT); ret = sendCmd(cmd, sizeof(*cmd) + sizeof(bool)); if (ret == sizeof(*cmd) + sizeof(bool)) ALOGV("setLeftyMode: lefty (tilt) = %s\n", (enable ? "true" : "false")); else ALOGE("setLeftyMode: failed to send command lefty (tilt) = %s\n", (enable ? "true" : "false")); free(cmd); } else { ALOGE("setLeftyMode: failed to allocate command\n"); return; } queueFlushInternal(COMMS_SENSOR_ACCEL_WRIST_AWARE, true); queueFlushInternal(COMMS_SENSOR_GYRO_WRIST_AWARE, true); mLefty.hub = enable; } sensors_event_t *HubConnection::initEv(sensors_event_t *ev, uint64_t timestamp, uint32_t type, uint32_t sensor) { memset(ev, 0x00, sizeof(sensors_event_t)); ev->version = sizeof(sensors_event_t); ev->timestamp = timestamp; ev->type = type; ev->sensor = sensor; return ev; } ssize_t HubConnection::decrementIfWakeEventLocked(int32_t sensor) { if (isWakeEvent(sensor)) { if (mWakeEventCount > 0) mWakeEventCount--; else ALOGW("%s: sensor=%d, unexpected count=%d, no-op", __FUNCTION__, sensor, mWakeEventCount); } return mWakeEventCount; } void HubConnection::protectIfWakeEventLocked(int32_t sensor) { if (isWakeEvent(sensor)) { if (mWakelockHeld == false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKELOCK_NAME); mWakelockHeld = true; } mWakeEventCount++; } } void HubConnection::releaseWakeLockIfAppropriate() { Mutex::Autolock autoLock(mLock); if (mWakelockHeld && (mWakeEventCount == 0)) { mWakelockHeld = false; release_wake_lock(WAKELOCK_NAME); } } void HubConnection::processSample(uint64_t timestamp, uint32_t type, uint32_t sensor, struct OneAxisSample *sample, __attribute__((unused)) bool highAccuracy) { sensors_event_t nev[1]; int cnt = 0; switch (sensor) { case COMMS_SENSOR_PRESSURE: initEv(&nev[cnt++], timestamp, type, sensor)->pressure = sample->fdata; break; case COMMS_SENSOR_HUMIDITY: initEv(&nev[cnt++], timestamp, type, sensor)->relative_humidity = sample->fdata; break; case COMMS_SENSOR_TEMPERATURE: initEv(&nev[cnt++], timestamp, type, sensor)->temperature = sample->fdata; break; case COMMS_SENSOR_AMBIENT_TEMPERATURE: initEv(&nev[cnt++], timestamp, type, sensor)->temperature = sample->fdata; break; case COMMS_SENSOR_PROXIMITY: initEv(&nev[cnt++], timestamp, type, sensor)->distance = sample->fdata; break; case COMMS_SENSOR_LIGHT: initEv(&nev[cnt++], timestamp, type, sensor)->light = sample->fdata; break; case COMMS_SENSOR_STEP_COUNTER: // We'll stash away the last step count in case we need to reset // the hub. This last step count would then become the new offset. mLastStepCount = mStepCounterOffset + sample->idata; initEv(&nev[cnt++], timestamp, type, sensor)->u64.step_counter = mLastStepCount; break; case COMMS_SENSOR_STEP_DETECTOR: case COMMS_SENSOR_SIGNIFICANT_MOTION: case COMMS_SENSOR_TILT: case COMMS_SENSOR_DOUBLE_TWIST: case COMMS_SENSOR_WRIST_TILT: initEv(&nev[cnt++], timestamp, type, sensor)->data[0] = 1.0f; break; case COMMS_SENSOR_GAZE: case COMMS_SENSOR_UNGAZE: case COMMS_SENSOR_GESTURE: case COMMS_SENSOR_SYNC: case COMMS_SENSOR_DOUBLE_TOUCH: initEv(&nev[cnt++], timestamp, type, sensor)->data[0] = sample->idata; break; case COMMS_SENSOR_HALL: #ifdef LID_STATE_REPORTING_ENABLED sendFolioEvent(sample->idata); #endif // LID_STATE_REPORTING_ENABLED break; case COMMS_SENSOR_WINDOW_ORIENTATION: initEv(&nev[cnt++], timestamp, type, sensor)->data[0] = sample->idata; break; default: break; } if (cnt > 0) write(nev, cnt); } uint8_t HubConnection::magAccuracyUpdate(sensors_vec_t *sv) { float magSq = sv->x * sv->x + sv->y * sv->y + sv->z * sv->z; if (magSq < MIN_MAG_SQ || magSq > MAX_MAG_SQ) { // save last good accuracy (either MEDIUM or HIGH) if (mMagAccuracy != SENSOR_STATUS_UNRELIABLE) mMagAccuracyRestore = mMagAccuracy; mMagAccuracy = SENSOR_STATUS_UNRELIABLE; } else if (mMagAccuracy == SENSOR_STATUS_UNRELIABLE) { // restore mMagAccuracy = mMagAccuracyRestore; } return mMagAccuracy; } void HubConnection::processSample(uint64_t timestamp, uint32_t type, uint32_t sensor, struct RawThreeAxisSample *sample, __attribute__((unused)) bool highAccuracy) { sensors_vec_t *sv; uncalibrated_event_t *ue; sensors_event_t nev[3]; int cnt = 0; switch (sensor) { case COMMS_SENSOR_ACCEL: sv = &initEv(&nev[cnt], timestamp, type, sensor)->acceleration; sv->x = sample->ix * mScaleAccel; sv->y = sample->iy * mScaleAccel; sv->z = sample->iz * mScaleAccel; sv->status = SENSOR_STATUS_ACCURACY_HIGH; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[sensor].enable && isSampleIntervalSatisfied(sensor, timestamp)) { if (!mAccelEnabledBiasStored) { // accel is enabled, but no enabled bias. Store latest bias and use // for accel and uncalibrated accel due to: // https://source.android.com/devices/sensors/sensor-types.html // "The bias and scale calibration must only be updated while the sensor is deactivated, // so as to avoid causing jumps in values during streaming." mAccelEnabledBiasStored = true; mAccelEnabledBias[0] = mAccelBias[0]; mAccelEnabledBias[1] = mAccelBias[1]; mAccelEnabledBias[2] = mAccelBias[2]; } // samples arrive using latest bias // adjust for enabled bias being different from lastest bias sv->x += mAccelBias[0] - mAccelEnabledBias[0]; sv->y += mAccelBias[1] - mAccelEnabledBias[1]; sv->z += mAccelBias[2] - mAccelEnabledBias[2]; ++cnt; } ue = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED, COMMS_SENSOR_ACCEL_UNCALIBRATED)->uncalibrated_accelerometer; ue->x_uncalib = sample->ix * mScaleAccel + mAccelBias[0]; ue->y_uncalib = sample->iy * mScaleAccel + mAccelBias[1]; ue->z_uncalib = sample->iz * mScaleAccel + mAccelBias[2]; if (!mAccelEnabledBiasStored) { // No enabled bias (which means accel is disabled). Use latest bias. ue->x_bias = mAccelBias[0]; ue->y_bias = mAccelBias[1]; ue->z_bias = mAccelBias[2]; } else { // enabled bias is valid, so use it ue->x_bias = mAccelEnabledBias[0]; ue->y_bias = mAccelEnabledBias[1]; ue->z_bias = mAccelEnabledBias[2]; } sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].enable && isSampleIntervalSatisfied(COMMS_SENSOR_ACCEL_UNCALIBRATED, timestamp)) { ++cnt; } if (mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].enable && isSampleIntervalSatisfied(COMMS_SENSOR_ACCEL_WRIST_AWARE, timestamp)) { sv = &initEv(&nev[cnt++], timestamp, SENSOR_TYPE_ACCELEROMETER_WRIST_AWARE, COMMS_SENSOR_ACCEL_WRIST_AWARE)->acceleration; sv->x = sample->ix * mScaleAccel; sv->y = (mLefty.accel ? -sample->iy : sample->iy) * mScaleAccel; sv->z = sample->iz * mScaleAccel; sv->status = SENSOR_STATUS_ACCURACY_HIGH; } break; case COMMS_SENSOR_MAG: sv = &initEv(&nev[cnt], timestamp, type, sensor)->magnetic; sv->x = sample->ix * mScaleMag; sv->y = sample->iy * mScaleMag; sv->z = sample->iz * mScaleMag; sv->status = magAccuracyUpdate(sv); sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[sensor].enable && isSampleIntervalSatisfied(sensor, timestamp)) { ++cnt; } ue = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, COMMS_SENSOR_MAG_UNCALIBRATED)->uncalibrated_magnetic; ue->x_uncalib = sample->ix * mScaleMag + mMagBias[0]; ue->y_uncalib = sample->iy * mScaleMag + mMagBias[1]; ue->z_uncalib = sample->iz * mScaleMag + mMagBias[2]; ue->x_bias = mMagBias[0]; ue->y_bias = mMagBias[1]; ue->z_bias = mMagBias[2]; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[COMMS_SENSOR_MAG_UNCALIBRATED].enable && isSampleIntervalSatisfied(COMMS_SENSOR_MAG_UNCALIBRATED, timestamp)) { ++cnt; } break; default: break; } if (cnt > 0) write(nev, cnt); } void HubConnection::processSample(uint64_t timestamp, uint32_t type, uint32_t sensor, struct ThreeAxisSample *sample, bool highAccuracy) { sensors_vec_t *sv; uncalibrated_event_t *ue; sensors_event_t *ev; sensors_event_t nev[3]; static const float heading_accuracy = M_PI / 6.0f; float w; int cnt = 0; switch (sensor) { case COMMS_SENSOR_ACCEL: sv = &initEv(&nev[cnt], timestamp, type, sensor)->acceleration; sv->x = sample->x; sv->y = sample->y; sv->z = sample->z; sv->status = SENSOR_STATUS_ACCURACY_HIGH; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[sensor].enable && isSampleIntervalSatisfied(sensor, timestamp)) { ++cnt; } ue = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED, COMMS_SENSOR_ACCEL_UNCALIBRATED)->uncalibrated_accelerometer; ue->x_uncalib = sample->x + mAccelBias[0]; ue->y_uncalib = sample->y + mAccelBias[1]; ue->z_uncalib = sample->z + mAccelBias[2]; ue->x_bias = mAccelBias[0]; ue->y_bias = mAccelBias[1]; ue->z_bias = mAccelBias[2]; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[COMMS_SENSOR_ACCEL_UNCALIBRATED].enable && isSampleIntervalSatisfied(COMMS_SENSOR_ACCEL_UNCALIBRATED, timestamp)) { ++cnt; } if (mSensorState[COMMS_SENSOR_ACCEL_WRIST_AWARE].enable && isSampleIntervalSatisfied(COMMS_SENSOR_ACCEL_WRIST_AWARE, timestamp)) { sv = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_ACCELEROMETER_WRIST_AWARE, COMMS_SENSOR_ACCEL_WRIST_AWARE)->acceleration; sv->x = sample->x; sv->y = (mLefty.accel ? -sample->y : sample->y); sv->z = sample->z; sv->status = SENSOR_STATUS_ACCURACY_HIGH; ++cnt; } break; case COMMS_SENSOR_GYRO: sv = &initEv(&nev[cnt], timestamp, type, sensor)->gyro; sv->x = sample->x; sv->y = sample->y; sv->z = sample->z; sv->status = SENSOR_STATUS_ACCURACY_HIGH; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[sensor].enable && isSampleIntervalSatisfied(sensor, timestamp)) { ++cnt; } ue = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_GYROSCOPE_UNCALIBRATED, COMMS_SENSOR_GYRO_UNCALIBRATED)->uncalibrated_gyro; ue->x_uncalib = sample->x + mGyroBias[0]; ue->y_uncalib = sample->y + mGyroBias[1]; ue->z_uncalib = sample->z + mGyroBias[2]; ue->x_bias = mGyroBias[0]; ue->y_bias = mGyroBias[1]; ue->z_bias = mGyroBias[2]; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[COMMS_SENSOR_GYRO_UNCALIBRATED].enable && isSampleIntervalSatisfied(COMMS_SENSOR_GYRO_UNCALIBRATED, timestamp)) { ++cnt; } if (mSensorState[COMMS_SENSOR_GYRO_WRIST_AWARE].enable && isSampleIntervalSatisfied(COMMS_SENSOR_GYRO_WRIST_AWARE, timestamp)) { sv = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_GYROSCOPE_WRIST_AWARE, COMMS_SENSOR_GYRO_WRIST_AWARE)->gyro; sv->x = (mLefty.gyro ? -sample->x : sample->x); sv->y = sample->y; sv->z = (mLefty.gyro ? -sample->z : sample->z); sv->status = SENSOR_STATUS_ACCURACY_HIGH; ++cnt; } break; case COMMS_SENSOR_ACCEL_BIAS: mAccelBias[0] = sample->x; mAccelBias[1] = sample->y; mAccelBias[2] = sample->z; saveSensorSettings(); break; case COMMS_SENSOR_GYRO_BIAS: mGyroBias[0] = sample->x; mGyroBias[1] = sample->y; mGyroBias[2] = sample->z; saveSensorSettings(); break; case COMMS_SENSOR_MAG: sv = &initEv(&nev[cnt], timestamp, type, sensor)->magnetic; sv->x = sample->x; sv->y = sample->y; sv->z = sample->z; sv->status = magAccuracyUpdate(sv); sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[sensor].enable && isSampleIntervalSatisfied(sensor, timestamp)) { ++cnt; } ue = &initEv(&nev[cnt], timestamp, SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, COMMS_SENSOR_MAG_UNCALIBRATED)->uncalibrated_magnetic; ue->x_uncalib = sample->x + mMagBias[0]; ue->y_uncalib = sample->y + mMagBias[1]; ue->z_uncalib = sample->z + mMagBias[2]; ue->x_bias = mMagBias[0]; ue->y_bias = mMagBias[1]; ue->z_bias = mMagBias[2]; sendDirectReportEvent(&nev[cnt], 1); if (mSensorState[COMMS_SENSOR_MAG_UNCALIBRATED].enable && isSampleIntervalSatisfied(COMMS_SENSOR_MAG_UNCALIBRATED, timestamp)) { ++cnt; } break; case COMMS_SENSOR_MAG_BIAS: mMagAccuracy = highAccuracy ? SENSOR_STATUS_ACCURACY_HIGH : SENSOR_STATUS_ACCURACY_MEDIUM; mMagBias[0] = sample->x; mMagBias[1] = sample->y; mMagBias[2] = sample->z; saveSensorSettings(); break; case COMMS_SENSOR_ORIENTATION: case COMMS_SENSOR_LINEAR_ACCEL: case COMMS_SENSOR_GRAVITY: sv = &initEv(&nev[cnt++], timestamp, type, sensor)->orientation; sv->x = sample->x; sv->y = sample->y; sv->z = sample->z; sv->status = mMagAccuracy; break; case COMMS_SENSOR_DOUBLE_TAP: ev = initEv(&nev[cnt++], timestamp, type, sensor); ev->data[0] = sample->x; ev->data[1] = sample->y; ev->data[2] = sample->z; break; case COMMS_SENSOR_ROTATION_VECTOR: ev = initEv(&nev[cnt++], timestamp, type, sensor); w = sample->x * sample->x + sample->y * sample->y + sample->z * sample->z; if (w < 1.0f) w = sqrt(1.0f - w); else w = 0.0f; ev->data[0] = sample->x; ev->data[1] = sample->y; ev->data[2] = sample->z; ev->data[3] = w; ev->data[4] = (4 - mMagAccuracy) * heading_accuracy; break; case COMMS_SENSOR_GEO_MAG: case COMMS_SENSOR_GAME_ROTATION_VECTOR: ev = initEv(&nev[cnt++], timestamp, type, sensor); w = sample->x * sample->x + sample->y * sample->y + sample->z * sample->z; if (w < 1.0f) w = sqrt(1.0f - w); else w = 0.0f; ev->data[0] = sample->x; ev->data[1] = sample->y; ev->data[2] = sample->z; ev->data[3] = w; break; default: break; } if (cnt > 0) write(nev, cnt); } void HubConnection::discardInotifyEvent() { // Read & discard an inotify event. We only use the presence of an event as // a trigger to perform the file existence check (for simplicity) if (mInotifyPollIndex >= 0) { char buf[sizeof(struct inotify_event) + NAME_MAX + 1]; int ret = ::read(mPollFds[mInotifyPollIndex].fd, buf, sizeof(buf)); ALOGV("Discarded %d bytes of inotify data", ret); } } void HubConnection::waitOnNanohubLock() { if (mInotifyPollIndex < 0) { return; } struct pollfd *pfd = &mPollFds[mInotifyPollIndex]; // While the lock file exists, poll on the inotify fd (with timeout) while (access(NANOHUB_LOCK_FILE, F_OK) == 0) { ALOGW("Nanohub is locked; blocking read thread"); int ret = poll(pfd, 1, 5000); if ((ret > 0) && (pfd->revents & POLLIN)) { discardInotifyEvent(); } } } void HubConnection::restoreSensorState() { Mutex::Autolock autoLock(mLock); sendCalibrationOffsets(); for (int i = 0; i < NUM_COMMS_SENSORS_PLUS_1; i++) { if (mSensorState[i].sensorType && mSensorState[i].enable) { struct ConfigCmd cmd; initConfigCmd(&cmd, i); ALOGV("restoring: sensor=%d, handle=%d, enable=%d, period=%" PRId64 ", latency=%" PRId64, cmd.sensorType, i, mSensorState[i].enable, frequency_q10_to_period_ns(mSensorState[i].rate), mSensorState[i].latency); int ret = sendCmd(&cmd, sizeof(cmd)); if (ret != sizeof(cmd)) { ALOGW("failed to send config command to restore sensor %d\n", cmd.sensorType); } cmd.cmd = CONFIG_CMD_FLUSH; for (auto iter = mFlushesPending[i].cbegin(); iter != mFlushesPending[i].cend(); ++iter) { for (int j = 0; j < iter->count; j++) { int ret = sendCmd(&cmd, sizeof(cmd)); if (ret != sizeof(cmd)) { ALOGW("failed to send flush command to sensor %d\n", cmd.sensorType); } } } } } mStepCounterOffset = mLastStepCount; } void HubConnection::postOsLog(uint8_t *buf, ssize_t len) { // if len is less than 6, it's either an invalid or an empty log message. if (len < 6) return; buf[len] = 0x00; switch (buf[4]) { case 'E': ALOGE("osLog: %s", &buf[5]); break; case 'W': ALOGW("osLog: %s", &buf[5]); break; case 'I': ALOGI("osLog: %s", &buf[5]); break; case 'D': ALOGD("osLog: %s", &buf[5]); break; case 'V': ALOGV("osLog: %s", &buf[5]); break; default: break; } } void HubConnection::processAppData(uint8_t *buf, ssize_t len) { if (len < static_cast(sizeof(AppToSensorHalDataBuffer))) return; AppToSensorHalDataPayload *data = &(reinterpret_cast(buf)->payload); if (data->size + sizeof(AppToSensorHalDataBuffer) != len) { ALOGW("Received corrupted data update packet, len %zd, size %u", len, data->size); return; } switch (data->type & APP_TO_SENSOR_HAL_TYPE_MASK) { case HALINTF_TYPE_GYRO_OTC_DATA: if (data->size != sizeof(GyroOtcData)) { ALOGW("Corrupted HALINTF_TYPE_GYRO_OTC_DATA with size %u", data->size); return; } mGyroOtcData = data->gyroOtcData[0]; saveSensorSettings(); break; default: ALOGW("Unknown app to hal data type 0x%04x", data->type); break; } } ssize_t HubConnection::processBuf(uint8_t *buf, size_t len) { struct nAxisEvent *data = (struct nAxisEvent *)buf; uint32_t type, sensor, bias, currSensor; int i, numSamples; bool one, rawThree, three; sensors_event_t ev; uint64_t timestamp; ssize_t ret = 0; uint32_t primary; if (len >= sizeof(data->evtType)) { ret = sizeof(data->evtType); one = three = rawThree = false; bias = 0; switch (data->evtType) { case OS_LOG_EVENT: postOsLog(buf, len); return 0; case EVT_APP_TO_SENSOR_HAL_DATA: processAppData(buf, len); return 0; case SENS_TYPE_TO_EVENT(SENS_TYPE_ACCEL): type = SENSOR_TYPE_ACCELEROMETER; sensor = COMMS_SENSOR_ACCEL; bias = COMMS_SENSOR_ACCEL_BIAS; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_ACCEL_RAW): type = SENSOR_TYPE_ACCELEROMETER; sensor = COMMS_SENSOR_ACCEL; rawThree = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GYRO): type = SENSOR_TYPE_GYROSCOPE; sensor = COMMS_SENSOR_GYRO; bias = COMMS_SENSOR_GYRO_BIAS; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_MAG): type = SENSOR_TYPE_MAGNETIC_FIELD; sensor = COMMS_SENSOR_MAG; bias = COMMS_SENSOR_MAG_BIAS; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_MAG_RAW): type = SENSOR_TYPE_MAGNETIC_FIELD; sensor = COMMS_SENSOR_MAG; rawThree = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_ALS): type = SENSOR_TYPE_LIGHT; sensor = COMMS_SENSOR_LIGHT; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_PROX): type = SENSOR_TYPE_PROXIMITY; sensor = COMMS_SENSOR_PROXIMITY; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_BARO): type = SENSOR_TYPE_PRESSURE; sensor = COMMS_SENSOR_PRESSURE; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_HUMIDITY): type = SENSOR_TYPE_RELATIVE_HUMIDITY; sensor = COMMS_SENSOR_HUMIDITY; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_TEMP): // nanohub only has one temperature sensor type, which is mapped to // internal temp because we currently don't have ambient temp type = SENSOR_TYPE_INTERNAL_TEMPERATURE; sensor = COMMS_SENSOR_TEMPERATURE; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_AMBIENT_TEMP): type = SENSOR_TYPE_AMBIENT_TEMPERATURE; sensor = COMMS_SENSOR_AMBIENT_TEMPERATURE; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_ORIENTATION): type = SENSOR_TYPE_ORIENTATION; sensor = COMMS_SENSOR_ORIENTATION; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_WIN_ORIENTATION): type = SENSOR_TYPE_DEVICE_ORIENTATION; sensor = COMMS_SENSOR_WINDOW_ORIENTATION; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_STEP_DETECT): type = SENSOR_TYPE_STEP_DETECTOR; sensor = COMMS_SENSOR_STEP_DETECTOR; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_STEP_COUNT): type = SENSOR_TYPE_STEP_COUNTER; sensor = COMMS_SENSOR_STEP_COUNTER; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_SIG_MOTION): type = SENSOR_TYPE_SIGNIFICANT_MOTION; sensor = COMMS_SENSOR_SIGNIFICANT_MOTION; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GRAVITY): type = SENSOR_TYPE_GRAVITY; sensor = COMMS_SENSOR_GRAVITY; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_LINEAR_ACCEL): type = SENSOR_TYPE_LINEAR_ACCELERATION; sensor = COMMS_SENSOR_LINEAR_ACCEL; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_ROTATION_VECTOR): type = SENSOR_TYPE_ROTATION_VECTOR; sensor = COMMS_SENSOR_ROTATION_VECTOR; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GEO_MAG_ROT_VEC): type = SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR; sensor = COMMS_SENSOR_GEO_MAG; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GAME_ROT_VECTOR): type = SENSOR_TYPE_GAME_ROTATION_VECTOR; sensor = COMMS_SENSOR_GAME_ROTATION_VECTOR; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_HALL): type = 0; sensor = COMMS_SENSOR_HALL; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_VSYNC): type = SENSOR_TYPE_SYNC; sensor = COMMS_SENSOR_SYNC; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_TILT): type = SENSOR_TYPE_TILT_DETECTOR; sensor = COMMS_SENSOR_TILT; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GESTURE): type = SENSOR_TYPE_PICK_UP_GESTURE; sensor = COMMS_SENSOR_GESTURE; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_DOUBLE_TWIST): type = SENSOR_TYPE_DOUBLE_TWIST; sensor = COMMS_SENSOR_DOUBLE_TWIST; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_DOUBLE_TAP): type = SENSOR_TYPE_DOUBLE_TAP; sensor = COMMS_SENSOR_DOUBLE_TAP; three = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_WRIST_TILT): type = SENSOR_TYPE_WRIST_TILT_GESTURE; sensor = COMMS_SENSOR_WRIST_TILT; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_DOUBLE_TOUCH): type = SENSOR_TYPE_DOUBLE_TOUCH; sensor = COMMS_SENSOR_DOUBLE_TOUCH; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_GAZE): type = SENSOR_TYPE_GAZE; sensor = COMMS_SENSOR_GAZE; one = true; break; case SENS_TYPE_TO_EVENT(SENS_TYPE_UNGAZE): type = SENSOR_TYPE_UNGAZE; sensor = COMMS_SENSOR_UNGAZE; one = true; break; case EVT_RESET_REASON: uint32_t resetReason; memcpy(&resetReason, data->buffer, sizeof(resetReason)); ALOGI("Observed hub reset: 0x%08" PRIx32, resetReason); restoreSensorState(); return 0; default: ALOGW("unknown evtType: 0x%08x len: %zu\n", data->evtType, len); return -1; } } else { ALOGW("too little data: len=%zu\n", len); return -1; } if (len >= sizeof(data->evtType) + sizeof(data->referenceTime) + sizeof(data->firstSample)) { ret += sizeof(data->referenceTime); timestamp = data->referenceTime; numSamples = data->firstSample.numSamples; for (i=0; ifirstSample.biasPresent && data->firstSample.biasSample == i) currSensor = bias; else currSensor = sensor; if (one) { if (ret + sizeof(data->oneSamples[i]) > len) { ALOGW("sensor %d (one): ret=%zd, numSamples=%d, i=%d\n", currSensor, ret, numSamples, i); return -1; } if (i > 0) timestamp += ((uint64_t)data->oneSamples[i].deltaTime) << delta_time_shift_table[data->oneSamples[i].deltaTime & delta_time_encoded]; processSample(timestamp, type, currSensor, &data->oneSamples[i], data->firstSample.highAccuracy); ret += sizeof(data->oneSamples[i]); } else if (rawThree) { if (ret + sizeof(data->rawThreeSamples[i]) > len) { ALOGW("sensor %d (rawThree): ret=%zd, numSamples=%d, i=%d\n", currSensor, ret, numSamples, i); return -1; } if (i > 0) timestamp += ((uint64_t)data->rawThreeSamples[i].deltaTime) << delta_time_shift_table[data->rawThreeSamples[i].deltaTime & delta_time_encoded]; processSample(timestamp, type, currSensor, &data->rawThreeSamples[i], data->firstSample.highAccuracy); ret += sizeof(data->rawThreeSamples[i]); } else if (three) { if (ret + sizeof(data->threeSamples[i]) > len) { ALOGW("sensor %d (three): ret=%zd, numSamples=%d, i=%d\n", currSensor, ret, numSamples, i); return -1; } if (i > 0) timestamp += ((uint64_t)data->threeSamples[i].deltaTime) << delta_time_shift_table[data->threeSamples[i].deltaTime & delta_time_encoded]; processSample(timestamp, type, currSensor, &data->threeSamples[i], data->firstSample.highAccuracy); ret += sizeof(data->threeSamples[i]); } else { ALOGW("sensor %d (unknown): cannot processSample\n", currSensor); return -1; } } if (!numSamples) ret += sizeof(data->firstSample); // If no primary sensor type is specified, // then 'sensor' is the primary sensor type. primary = mSensorState[sensor].primary; primary = (primary ? primary : sensor); for (i=0; ifirstSample.numFlushes; i++) { bool internal = false; { Mutex::Autolock autoLock(mLock); struct Flush& flush = mFlushesPending[primary].front(); memset(&ev, 0x00, sizeof(sensors_event_t)); ev.version = META_DATA_VERSION; ev.timestamp = 0; ev.type = SENSOR_TYPE_META_DATA; ev.sensor = 0; ev.meta_data.what = META_DATA_FLUSH_COMPLETE; ev.meta_data.sensor = flush.handle; if (flush.internal) { internal = true; if (flush.handle == COMMS_SENSOR_ACCEL_WRIST_AWARE) mLefty.accel = !mLefty.accel; else if (flush.handle == COMMS_SENSOR_GYRO_WRIST_AWARE) mLefty.gyro = !mLefty.gyro; } if (--flush.count == 0) mFlushesPending[primary].pop_front(); } if (!internal) write(&ev, 1); ALOGV("flushing %d", ev.meta_data.sensor); } } else { ALOGW("too little data for sensor %d: len=%zu\n", sensor, len); return -1; } return ret; } void HubConnection::sendCalibrationOffsets() { sp settings; sp saved_settings; struct { int32_t hw[3]; float sw[3]; } accel; int32_t proximity, proximity_array[4]; float barometer, humidity, light; bool accel_hw_cal_exists, accel_sw_cal_exists; loadSensorSettings(&settings, &saved_settings); accel_hw_cal_exists = getCalibrationInt32(settings, ACCEL_BIAS_TAG, accel.hw, 3); accel_sw_cal_exists = getCalibrationFloat(saved_settings, ACCEL_SW_BIAS_TAG, accel.sw); if (accel_hw_cal_exists || accel_sw_cal_exists) { // Store SW bias so we can remove bias for uncal data mAccelBias[0] = accel.sw[0]; mAccelBias[1] = accel.sw[1]; mAccelBias[2] = accel.sw[2]; queueDataInternal(COMMS_SENSOR_ACCEL, &accel, sizeof(accel)); } ALOGV("Use new configuration format"); std::vector hardwareGyroBias = getInt32Setting(settings, GYRO_BIAS_TAG); std::vector softwareGyroBias = getFloatSetting(saved_settings, GYRO_SW_BIAS_TAG); if (hardwareGyroBias.size() == 3 || softwareGyroBias.size() == 3) { struct { AppToSensorHalDataPayload header; GyroCalBias data; } packet = { .header = { .size = sizeof(GyroCalBias), .type = HALINTF_TYPE_GYRO_CAL_BIAS } }; if (hardwareGyroBias.size() == 3) { std::copy(hardwareGyroBias.begin(), hardwareGyroBias.end(), packet.data.hardwareBias); } if (softwareGyroBias.size() == 3) { // Store SW bias so we can remove bias for uncal data std::copy(softwareGyroBias.begin(), softwareGyroBias.end(), mGyroBias); std::copy(softwareGyroBias.begin(), softwareGyroBias.end(), packet.data.softwareBias); } // send packet to hub queueDataInternal(COMMS_SENSOR_GYRO, &packet, sizeof(packet)); } // over temp cal std::vector gyroOtcData = getFloatSetting(saved_settings, GYRO_OTC_DATA_TAG); if (gyroOtcData.size() == sizeof(GyroOtcData) / sizeof(float)) { std::copy(gyroOtcData.begin(), gyroOtcData.end(), reinterpret_cast(&mGyroOtcData)); struct { AppToSensorHalDataPayload header; GyroOtcData data; } packet = { .header = { .size = sizeof(GyroOtcData), .type = HALINTF_TYPE_GYRO_OTC_DATA }, .data = mGyroOtcData }; // send it to hub queueDataInternal(COMMS_SENSOR_GYRO, &packet, sizeof(packet)); } else { ALOGW("Illegal otc_gyro data size = %zu", gyroOtcData.size()); } std::vector magBiasData = getFloatSetting(saved_settings, MAG_BIAS_TAG); if (magBiasData.size() == 3) { // Store SW bias so we can remove bias for uncal data std::copy(magBiasData.begin(), magBiasData.end(), mMagBias); struct { AppToSensorHalDataPayload header; MagCalBias mag; } packet = { .header = { .size = sizeof(MagCalBias), .type = HALINTF_TYPE_MAG_CAL_BIAS } }; std::copy(magBiasData.begin(), magBiasData.end(), packet.mag.bias); queueDataInternal(COMMS_SENSOR_MAG, &packet, sizeof(packet)); } if (settings->getFloat("barometer", &barometer)) queueDataInternal(COMMS_SENSOR_PRESSURE, &barometer, sizeof(barometer)); if (settings->getFloat("humidity", &humidity)) queueDataInternal(COMMS_SENSOR_HUMIDITY, &humidity, sizeof(humidity)); if (settings->getInt32("proximity", &proximity)) queueDataInternal(COMMS_SENSOR_PROXIMITY, &proximity, sizeof(proximity)); if (getCalibrationInt32(settings, "proximity", proximity_array, 4)) queueDataInternal(COMMS_SENSOR_PROXIMITY, proximity_array, sizeof(proximity_array)); if (settings->getFloat("light", &light)) queueDataInternal(COMMS_SENSOR_LIGHT, &light, sizeof(light)); } bool HubConnection::threadLoop() { ALOGV("threadLoop: starting"); if (mFd < 0) { ALOGW("threadLoop: exiting prematurely: nanohub is unavailable"); return false; } waitOnNanohubLock(); sendCalibrationOffsets(); while (!Thread::exitPending()) { ssize_t ret; do { ret = poll(mPollFds, mNumPollFds, -1); } while (ret < 0 && errno == EINTR); if (mInotifyPollIndex >= 0 && mPollFds[mInotifyPollIndex].revents & POLLIN) { discardInotifyEvent(); waitOnNanohubLock(); } #ifdef USB_MAG_BIAS_REPORTING_ENABLED if (mMagBiasPollIndex >= 0 && mPollFds[mMagBiasPollIndex].revents & POLLERR) { // Read from mag bias file char buf[16]; lseek(mPollFds[mMagBiasPollIndex].fd, 0, SEEK_SET); ::read(mPollFds[mMagBiasPollIndex].fd, buf, 16); float bias = atof(buf); mUsbMagBias = bias; queueUsbMagBias(); } #endif // USB_MAG_BIAS_REPORTING_ENABLED #ifdef DOUBLE_TOUCH_ENABLED if (mDoubleTouchPollIndex >= 0 && mPollFds[mDoubleTouchPollIndex].revents & POLLERR) { // Read from double touch file char buf[16]; lseek(mPollFds[mDoubleTouchPollIndex].fd, 0, SEEK_SET); ::read(mPollFds[mDoubleTouchPollIndex].fd, buf, 16); sensors_event_t gestureEvent; initEv(&gestureEvent, elapsedRealtimeNano(), SENSOR_TYPE_PICK_UP_GESTURE, COMMS_SENSOR_GESTURE)->data[0] = 8; write(&gestureEvent, 1); } #endif // DOUBLE_TOUCH_ENABLED if (mPollFds[0].revents & POLLIN) { uint8_t recv[256]; ssize_t len = ::read(mFd, recv, sizeof(recv)); if (len >= 0) { for (ssize_t offset = 0; offset < len;) { ret = processBuf(recv + offset, len - offset); if (ret > 0) offset += ret; else break; } } else { ALOGW("read -1: errno=%d\n", errno); } } } return false; } void HubConnection::initConfigCmd(struct ConfigCmd *cmd, int handle) { memset(cmd, 0x00, sizeof(*cmd)); cmd->evtType = EVT_NO_SENSOR_CONFIG_EVENT; cmd->sensorType = mSensorState[handle].sensorType; if (mSensorState[handle].enable) { cmd->cmd = CONFIG_CMD_ENABLE; cmd->rate = mSensorState[handle].rate; cmd->latency = mSensorState[handle].latency; } else { cmd->cmd = CONFIG_CMD_DISABLE; // set rate and latency to values that will always be overwritten by the // first enabled alt sensor cmd->rate = UINT32_C(0); cmd->latency = UINT64_MAX; } for (int i=0; icmd = CONFIG_CMD_ENABLE; if (mSensorState[alt].rate > cmd->rate) { cmd->rate = mSensorState[alt].rate; } if (mSensorState[alt].latency < cmd->latency) { cmd->latency = mSensorState[alt].latency; } } // will be a nop if direct report mode is not enabled mergeDirectReportRequest(cmd, handle); } void HubConnection::queueActivate(int handle, bool enable) { struct ConfigCmd cmd; int ret; Mutex::Autolock autoLock(mLock); if (isValidHandle(handle)) { // disabling accel, so no longer need to use the bias from when // accel was first enabled if (handle == COMMS_SENSOR_ACCEL && !enable) mAccelEnabledBiasStored = false; mSensorState[handle].enable = enable; initConfigCmd(&cmd, handle); ret = sendCmd(&cmd, sizeof(cmd)); if (ret == sizeof(cmd)) { updateSampleRate(handle, enable ? CONFIG_CMD_ENABLE : CONFIG_CMD_DISABLE); ALOGV("queueActivate: sensor=%d, handle=%d, enable=%d", cmd.sensorType, handle, enable); } else ALOGW("queueActivate: failed to send command: sensor=%d, handle=%d, enable=%d", cmd.sensorType, handle, enable); } else { ALOGV("queueActivate: unhandled handle=%d, enable=%d", handle, enable); } } void HubConnection::queueSetDelay(int handle, nsecs_t sampling_period_ns) { struct ConfigCmd cmd; int ret; Mutex::Autolock autoLock(mLock); if (isValidHandle(handle)) { if (sampling_period_ns > 0 && mSensorState[handle].rate != SENSOR_RATE_ONCHANGE && mSensorState[handle].rate != SENSOR_RATE_ONESHOT) { mSensorState[handle].rate = period_ns_to_frequency_q10(sampling_period_ns); } initConfigCmd(&cmd, handle); ret = sendCmd(&cmd, sizeof(cmd)); if (ret == sizeof(cmd)) ALOGV("queueSetDelay: sensor=%d, handle=%d, period=%" PRId64, cmd.sensorType, handle, sampling_period_ns); else ALOGW("queueSetDelay: failed to send command: sensor=%d, handle=%d, period=%" PRId64, cmd.sensorType, handle, sampling_period_ns); } else { ALOGV("queueSetDelay: unhandled handle=%d, period=%" PRId64, handle, sampling_period_ns); } } void HubConnection::queueBatch( int handle, nsecs_t sampling_period_ns, nsecs_t max_report_latency_ns) { struct ConfigCmd cmd; int ret; Mutex::Autolock autoLock(mLock); if (isValidHandle(handle)) { if (sampling_period_ns > 0 && mSensorState[handle].rate != SENSOR_RATE_ONCHANGE && mSensorState[handle].rate != SENSOR_RATE_ONESHOT) { mSensorState[handle].rate = period_ns_to_frequency_q10(sampling_period_ns); } mSensorState[handle].latency = max_report_latency_ns; initConfigCmd(&cmd, handle); ret = sendCmd(&cmd, sizeof(cmd)); if (ret == sizeof(cmd)) { updateSampleRate(handle, CONFIG_CMD_ENABLE); // batch uses CONFIG_CMD_ENABLE command ALOGV("queueBatch: sensor=%d, handle=%d, period=%" PRId64 ", latency=%" PRId64, cmd.sensorType, handle, sampling_period_ns, max_report_latency_ns); } else { ALOGW("queueBatch: failed to send command: sensor=%d, handle=%d, period=%" PRId64 ", latency=%" PRId64, cmd.sensorType, handle, sampling_period_ns, max_report_latency_ns); } } else { ALOGV("queueBatch: unhandled handle=%d, period=%" PRId64 ", latency=%" PRId64, handle, sampling_period_ns, max_report_latency_ns); } } void HubConnection::queueFlush(int handle) { Mutex::Autolock autoLock(mLock); queueFlushInternal(handle, false); } void HubConnection::queueFlushInternal(int handle, bool internal) { struct ConfigCmd cmd; uint32_t primary; int ret; if (isValidHandle(handle)) { // If no primary sensor type is specified, // then 'handle' is the primary sensor type. primary = mSensorState[handle].primary; primary = (primary ? primary : handle); std::list& flushList = mFlushesPending[primary]; if (!flushList.empty() && flushList.back().internal == internal && flushList.back().handle == handle) { ++flushList.back().count; } else { flushList.push_back((struct Flush){handle, 1, internal}); } initConfigCmd(&cmd, handle); cmd.cmd = CONFIG_CMD_FLUSH; ret = sendCmd(&cmd, sizeof(cmd)); if (ret == sizeof(cmd)) { ALOGV("queueFlush: sensor=%d, handle=%d", cmd.sensorType, handle); } else { ALOGW("queueFlush: failed to send command: sensor=%d, handle=%d" " with error %s", cmd.sensorType, handle, strerror(errno)); } } else { ALOGV("queueFlush: unhandled handle=%d", handle); } } void HubConnection::queueDataInternal(int handle, void *data, size_t length) { struct ConfigCmd *cmd = (struct ConfigCmd *)malloc(sizeof(struct ConfigCmd) + length); size_t ret; if (cmd && isValidHandle(handle)) { initConfigCmd(cmd, handle); memcpy(cmd->data, data, length); cmd->cmd = CONFIG_CMD_CFG_DATA; ret = sendCmd(cmd, sizeof(*cmd) + length); if (ret == sizeof(*cmd) + length) ALOGV("queueData: sensor=%d, length=%zu", cmd->sensorType, length); else ALOGW("queueData: failed to send command: sensor=%d, length=%zu", cmd->sensorType, length); } else { ALOGV("queueData: unhandled handle=%d", handle); } free(cmd); } void HubConnection::queueData(int handle, void *data, size_t length) { Mutex::Autolock autoLock(mLock); queueDataInternal(handle, data, length); } void HubConnection::setOperationParameter(const additional_info_event_t &info) { switch (info.type) { case AINFO_LOCAL_GEOMAGNETIC_FIELD: { ALOGV("local geomag field update: strength %fuT, dec %fdeg, inc %fdeg", static_cast(info.data_float[0]), info.data_float[1] * 180 / M_PI, info.data_float[2] * 180 / M_PI); struct { AppToSensorHalDataPayload header; MagLocalField magLocalField; } packet = { .header = { .size = sizeof(MagLocalField), .type = HALINTF_TYPE_MAG_LOCAL_FIELD }, .magLocalField = { .strength = info.data_float[0], .declination = info.data_float[1], .inclination = info.data_float[2]} }; queueDataInternal(COMMS_SENSOR_MAG, &packet, sizeof(packet)); break; } default: break; } } void HubConnection::initNanohubLock() { // Create the lock directory (if it doesn't already exist) if (mkdir(NANOHUB_LOCK_DIR, NANOHUB_LOCK_DIR_PERMS) < 0 && errno != EEXIST) { ALOGW("Couldn't create Nanohub lock directory: %s", strerror(errno)); return; } mInotifyPollIndex = -1; int inotifyFd = inotify_init1(IN_NONBLOCK); if (inotifyFd < 0) { ALOGW("Couldn't initialize inotify: %s", strerror(errno)); } else if (inotify_add_watch(inotifyFd, NANOHUB_LOCK_DIR, IN_CREATE | IN_DELETE) < 0) { ALOGW("Couldn't add inotify watch: %s", strerror(errno)); close(inotifyFd); } else { mPollFds[mNumPollFds].fd = inotifyFd; mPollFds[mNumPollFds].events = POLLIN; mPollFds[mNumPollFds].revents = 0; mInotifyPollIndex = mNumPollFds; mNumPollFds++; } } ssize_t HubConnection::read(sensors_event_t *ev, size_t size) { ssize_t n = mRing.read(ev, size); Mutex::Autolock autoLock(mLock); // We log the first failure in write, so only log 2+ errors if (mWriteFailures > 1) { ALOGW("%s: mRing.write failed %d times", __FUNCTION__, mWriteFailures); mWriteFailures = 0; } for (ssize_t i = 0; i < n; i++) decrementIfWakeEventLocked(ev[i].sensor); return n; } ssize_t HubConnection::write(const sensors_event_t *ev, size_t n) { ssize_t ret = 0; Mutex::Autolock autoLock(mLock); for (size_t i=0; ievtType = EVT_APP_FROM_HOST; cmd->msg.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, APP_ID_APP_BMI160); cmd->msg.dataLen = sizeof(float); memcpy((float *)(cmd+1), &mUsbMagBias, sizeof(float)); ret = sendCmd(cmd, sizeof(*cmd) + sizeof(float)); if (ret == sizeof(*cmd) + sizeof(float)) ALOGV("queueUsbMagBias: bias=%f\n", mUsbMagBias); else ALOGW("queueUsbMagBias: failed to send command: bias=%f\n", mUsbMagBias); free(cmd); } } #endif // USB_MAG_BIAS_REPORTING_ENABLED #ifdef LID_STATE_REPORTING_ENABLED status_t HubConnection::initializeUinputNode() { int ret = 0; // Open uinput dev node mUinputFd = TEMP_FAILURE_RETRY(open("/dev/uinput", O_WRONLY | O_NONBLOCK)); if (mUinputFd < 0) { ALOGW("could not open uinput node: %s", strerror(errno)); return UNKNOWN_ERROR; } // Enable SW_LID events ret = TEMP_FAILURE_RETRY(ioctl(mUinputFd, UI_SET_EVBIT, EV_SW)); ret |= TEMP_FAILURE_RETRY(ioctl(mUinputFd, UI_SET_EVBIT, EV_SYN)); ret |= TEMP_FAILURE_RETRY(ioctl(mUinputFd, UI_SET_SWBIT, SW_LID)); if (ret < 0) { ALOGW("could not send ioctl to uinput node: %s", strerror(errno)); return UNKNOWN_ERROR; } // Create uinput node for SW_LID struct uinput_user_dev uidev; memset(&uidev, 0, sizeof(uidev)); snprintf(uidev.name, UINPUT_MAX_NAME_SIZE, "uinput-folio"); uidev.id.bustype = BUS_SPI; uidev.id.vendor = 0; uidev.id.product = 0; uidev.id.version = 0; ret = TEMP_FAILURE_RETRY(::write(mUinputFd, &uidev, sizeof(uidev))); if (ret < 0) { ALOGW("write to uinput node failed: %s", strerror(errno)); return UNKNOWN_ERROR; } ret = TEMP_FAILURE_RETRY(ioctl(mUinputFd, UI_DEV_CREATE)); if (ret < 0) { ALOGW("could not send ioctl to uinput node: %s", strerror(errno)); return UNKNOWN_ERROR; } return OK; } void HubConnection::sendFolioEvent(int32_t data) { ssize_t ret = 0; struct input_event ev; memset(&ev, 0, sizeof(ev)); ev.type = EV_SW; ev.code = SW_LID; ev.value = data; ret = TEMP_FAILURE_RETRY(::write(mUinputFd, &ev, sizeof(ev))); if (ret < 0) { ALOGW("write to uinput node failed: %s", strerror(errno)); return; } // Force flush with EV_SYN event ev.type = EV_SYN; ev.code = SYN_REPORT; ev.value = 0; ret = TEMP_FAILURE_RETRY(::write(mUinputFd, &ev, sizeof(ev))); if (ret < 0) { ALOGW("write to uinput node failed: %s", strerror(errno)); return; } // Set lid state property if (property_set(LID_STATE_PROPERTY, (data ? LID_STATE_CLOSED : LID_STATE_OPEN)) < 0) { ALOGW("could not set lid_state property"); } } #endif // LID_STATE_REPORTING_ENABLED #ifdef DIRECT_REPORT_ENABLED void HubConnection::sendDirectReportEvent(const sensors_event_t *nev, size_t n) { // short circuit to avoid lock operation if (n == 0) { return; } // no intention to block sensor delivery thread. when lock is needed ignore // the event (this only happens when the channel is reconfiured, so it's ok if (mDirectChannelLock.tryLock() == NO_ERROR) { while (n--) { auto i = mSensorToChannel.find(nev->sensor); if (i != mSensorToChannel.end()) { for (auto &j : i->second) { if ((uint64_t)nev->timestamp > j.second.lastTimestamp && intervalLargeEnough( nev->timestamp - j.second.lastTimestamp, rateLevelToDeviceSamplingPeriodNs( nev->sensor, j.second.rateLevel))) { mDirectChannel[j.first]->write(nev); j.second.lastTimestamp = nev->timestamp; } } } ++nev; } mDirectChannelLock.unlock(); } } void HubConnection::mergeDirectReportRequest(struct ConfigCmd *cmd, int handle) { int maxRateLevel = SENSOR_DIRECT_RATE_STOP; auto j = mSensorToChannel.find(handle); if (j != mSensorToChannel.end()) { for (auto &i : j->second) { maxRateLevel = std::max(i.second.rateLevel, maxRateLevel); } } for (auto handle : mSensorState[handle].alt) { auto j = mSensorToChannel.find(handle); if (j != mSensorToChannel.end()) { for (auto &i : j->second) { maxRateLevel = std::max(i.second.rateLevel, maxRateLevel); } } } uint64_t period = rateLevelToDeviceSamplingPeriodNs(handle, maxRateLevel); if (period != INT64_MAX) { rate_q10_t rate; rate = period_ns_to_frequency_q10(period); cmd->rate = (rate > cmd->rate || cmd->cmd == CONFIG_CMD_DISABLE) ? rate : cmd->rate; cmd->latency = 0; cmd->cmd = CONFIG_CMD_ENABLE; } } int HubConnection::addDirectChannel(const struct sensors_direct_mem_t *mem) { std::unique_ptr ch; int ret = NO_MEMORY; Mutex::Autolock autoLock(mDirectChannelLock); for (const auto& c : mDirectChannel) { if (c.second->memoryMatches(mem)) { // cannot reusing same memory return BAD_VALUE; } } switch(mem->type) { case SENSOR_DIRECT_MEM_TYPE_ASHMEM: ch = std::make_unique(mem); break; case SENSOR_DIRECT_MEM_TYPE_GRALLOC: ch = std::make_unique(mem); break; default: ret = INVALID_OPERATION; } if (ch) { if (ch->isValid()) { ret = mDirectChannelHandle++; mDirectChannel.insert(std::make_pair(ret, std::move(ch))); } else { ret = ch->getError(); ALOGW("Direct channel object(type:%d) has error %d upon init", mem->type, ret); } } return ret; } int HubConnection::removeDirectChannel(int channel_handle) { // make sure no active sensor in this channel std::vector activeSensorList; stopAllDirectReportOnChannel(channel_handle, &activeSensorList); // sensor service is responsible for stop all sensors before remove direct // channel. Thus, this is an error. if (!activeSensorList.empty()) { std::stringstream ss; std::copy(activeSensorList.begin(), activeSensorList.end(), std::ostream_iterator(ss, ",")); ALOGW("Removing channel %d when sensors (%s) are not stopped.", channel_handle, ss.str().c_str()); } // remove the channel record Mutex::Autolock autoLock(mDirectChannelLock); mDirectChannel.erase(channel_handle); return NO_ERROR; } int HubConnection::stopAllDirectReportOnChannel( int channel_handle, std::vector *activeSensorList) { Mutex::Autolock autoLock(mDirectChannelLock); if (mDirectChannel.find(channel_handle) == mDirectChannel.end()) { return BAD_VALUE; } std::vector sensorToStop; for (auto &it : mSensorToChannel) { auto j = it.second.find(channel_handle); if (j != it.second.end()) { it.second.erase(j); if (it.second.empty()) { sensorToStop.push_back(it.first); } } } if (activeSensorList != nullptr) { *activeSensorList = sensorToStop; } // re-evaluate and send config for all sensor that need to be stopped bool ret = true; for (auto sensor_handle : sensorToStop) { Mutex::Autolock autoLock2(mLock); struct ConfigCmd cmd; initConfigCmd(&cmd, sensor_handle); int result = sendCmd(&cmd, sizeof(cmd)); ret = ret && (result == sizeof(cmd)); } return ret ? NO_ERROR : BAD_VALUE; } int HubConnection::configDirectReport(int sensor_handle, int channel_handle, int rate_level) { if (sensor_handle == -1 && rate_level == SENSOR_DIRECT_RATE_STOP) { return stopAllDirectReportOnChannel(channel_handle, nullptr); } if (!isValidHandle(sensor_handle)) { return BAD_VALUE; } // clamp to fast if (rate_level > SENSOR_DIRECT_RATE_FAST) { rate_level = SENSOR_DIRECT_RATE_FAST; } // manage direct channel data structure Mutex::Autolock autoLock(mDirectChannelLock); auto i = mDirectChannel.find(channel_handle); if (i == mDirectChannel.end()) { return BAD_VALUE; } auto j = mSensorToChannel.find(sensor_handle); if (j == mSensorToChannel.end()) { return BAD_VALUE; } j->second.erase(channel_handle); if (rate_level != SENSOR_DIRECT_RATE_STOP) { j->second.insert(std::make_pair(channel_handle, (DirectChannelTimingInfo){0, rate_level})); } Mutex::Autolock autoLock2(mLock); struct ConfigCmd cmd; initConfigCmd(&cmd, sensor_handle); int ret = sendCmd(&cmd, sizeof(cmd)); if (rate_level == SENSOR_DIRECT_RATE_STOP) { ret = NO_ERROR; } else { ret = (ret == sizeof(cmd)) ? sensor_handle : BAD_VALUE; } return ret; } bool HubConnection::isDirectReportSupported() const { return true; } void HubConnection::updateSampleRate(int handle, int reason) { bool affected = mSensorToChannel.find(handle) != mSensorToChannel.end(); for (size_t i = 0; i < MAX_ALTERNATES && !affected; ++i) { if (mSensorState[handle].alt[i] != COMMS_SENSOR_INVALID) { affected |= mSensorToChannel.find(mSensorState[handle].alt[i]) != mSensorToChannel.end(); } } if (!affected) { return; } switch (reason) { case CONFIG_CMD_ENABLE: { constexpr uint64_t PERIOD_800HZ = 1250000; uint64_t period_multiplier = (frequency_q10_to_period_ns(mSensorState[handle].rate) + PERIOD_800HZ / 2) / PERIOD_800HZ; uint64_t desiredTSample = PERIOD_800HZ; while (period_multiplier /= 2) { desiredTSample *= 2; } mSensorState[handle].desiredTSample = desiredTSample; ALOGV("DesiredTSample for handle 0x%x set to %" PRIu64, handle, desiredTSample); break; } case CONFIG_CMD_DISABLE: mSensorState[handle].desiredTSample = INT64_MAX; ALOGV("DesiredTSample 0x%x set to disable", handle); break; default: ALOGW("%s: unexpected reason = %d, no-op", __FUNCTION__, reason); break; } } bool HubConnection::isSampleIntervalSatisfied(int handle, uint64_t timestamp) { if (mSensorToChannel.find(handle) == mSensorToChannel.end()) { return true; } if (mSensorState[handle].lastTimestamp >= timestamp || mSensorState[handle].desiredTSample == INT64_MAX) { return false; } else if (intervalLargeEnough(timestamp - mSensorState[handle].lastTimestamp, mSensorState[handle].desiredTSample)) { mSensorState[handle].lastTimestamp = timestamp; return true; } else { return false; } } uint64_t HubConnection::rateLevelToDeviceSamplingPeriodNs(int handle, int rateLevel) const { if (mSensorToChannel.find(handle) == mSensorToChannel.end()) { return INT64_MAX; } switch (rateLevel) { case SENSOR_DIRECT_RATE_VERY_FAST: [[fallthrough]]; // No sensor support VERY_FAST, fall through case SENSOR_DIRECT_RATE_FAST: if (handle != COMMS_SENSOR_MAG && handle != COMMS_SENSOR_MAG_UNCALIBRATED) { return 2500*1000; // 400Hz } [[fallthrough]]; case SENSOR_DIRECT_RATE_NORMAL: return 20*1000*1000; // 50 Hz default: return INT64_MAX; } } #else // DIRECT_REPORT_ENABLED // nop functions if feature is turned off int HubConnection::addDirectChannel(const struct sensors_direct_mem_t *) { return INVALID_OPERATION; } int HubConnection::removeDirectChannel(int) { return INVALID_OPERATION; } int HubConnection::configDirectReport(int, int, int) { return INVALID_OPERATION; } void HubConnection::sendDirectReportEvent(const sensors_event_t *, size_t) { } void HubConnection::mergeDirectReportRequest(struct ConfigCmd *, int) { } bool HubConnection::isDirectReportSupported() const { return false; } void HubConnection::updateSampleRate(int, int) { } bool HubConnection::isSampleIntervalSatisfied(int, uint64_t) { return true; } #endif // DIRECT_REPORT_ENABLED } // namespace android