/* * Copyright (C) 2012 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 "BubbleLevelImpl" //#define LOG_NDEBUG 0 #include <utils/Log.h> #include <binder/IServiceManager.h> #include "BubbleLevelImpl.h" namespace android { static int sensor_callback(int fd, int events, void* data); #define BL_SENSOR_POLL_INTERVAL_MS 20 #define BL_SENSOR_POLL_TIMEOUT_MS (BL_SENSOR_POLL_INTERVAL_MS * 5) #define BL_SENSOR_POLL_COUNT 10 #define BL_SENSOR_LEVEL_THRESHOLD (2.0) BubbleLevelImpl::BubbleLevelImpl() : Thread(false), mState(BL_STATE_IDLE), mCmd(BL_CMD_NONE), mPollIntervalSec(BL_POLL_INTERVAL_DEFAULT_SEC), mPollCount(0), mLevelCount(0), mCallBack(NULL), mUserData(NULL), mNumSensors(0), mAccelerometer(NULL), mInitStatus(-ENODEV) { init(); } int BubbleLevelImpl::init() { if (mInitStatus == 0) { return 0; } if (defaultServiceManager()->checkService(String16("sensorservice")) == 0) { ALOGW("CSTOR sensorservice not ready yet"); return mInitStatus; } SensorManager& mgr(SensorManager::getInstance()); Sensor const* const* sensorList; mNumSensors = mgr.getSensorList(&sensorList); if (mNumSensors <= 0) { ALOGE("CSTOR mNumSensors error %d", mNumSensors); goto exit; } mAccelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER); if (mAccelerometer == NULL) { ALOGE("CSTOR mAccelerometer error NULL"); goto exit; } mSensorEventQueue = mgr.createEventQueue(); if (mSensorEventQueue == 0) { ALOGE("createEventQueue returned 0"); goto exit; } mLooper = new Looper(false); mLooper->addFd(mSensorEventQueue->getFd(), 0, ALOOPER_EVENT_INPUT, sensor_callback, this); mInitStatus = 0; exit: return mInitStatus; } BubbleLevelImpl::~BubbleLevelImpl() { { Mutex::Autolock _l(mStateLock); mCmd = BL_CMD_EXIT; mLooper->wake(); mCond.broadcast(); } requestExitAndWait(); } void BubbleLevelImpl::onFirstRef() { if (mInitStatus == 0) { run("Acc Loop", ANDROID_PRIORITY_URGENT_AUDIO); } } bool BubbleLevelImpl::threadLoop() { bool isLevel; while(!exitPending()) { { Mutex::Autolock _l(mStateLock); isLevel = false; switch (mCmd) { case BL_CMD_POLL_ONCE: case BL_CMD_START_POLL: if (mState == BL_STATE_IDLE) { mSensorEventQueue->enableSensor(mAccelerometer); mSensorEventQueue->setEventRate(mAccelerometer, ms2ns(BL_SENSOR_POLL_INTERVAL_MS)); mPollCount = 0; mLevelCount = 0; if (mCmd == BL_CMD_START_POLL) { mState = BL_STATE_POLLING; } else { mState = BL_STATE_POLLING_ONCE; } } if ((mCmd == BL_CMD_START_POLL) && (mState == BL_STATE_POLLING_ONCE)) { mState = BL_STATE_POLLING; } break; case BL_CMD_STOP_POLL: if (mState == BL_STATE_POLLING || mState == BL_STATE_POLLING_ONCE || mState == BL_STATE_SLEEPING) { mSensorEventQueue->disableSensor(mAccelerometer); mState = BL_STATE_IDLE; } break; case BL_CMD_EXIT: continue; case BL_CMD_NONE: break; default: ALOGE("unknown command: %d", mCmd); } mCmd = BL_CMD_NONE; switch (mState) { case BL_STATE_IDLE: mCond.wait(mStateLock); continue; case BL_STATE_POLLING: case BL_STATE_POLLING_ONCE: if (mPollCount >= BL_SENSOR_POLL_COUNT) { // majority vote isLevel = (mLevelCount > (BL_SENSOR_POLL_COUNT / 2)); if (mState == BL_STATE_POLLING_ONCE) { mCmd = BL_CMD_STOP_POLL; } mState = BL_STATE_SLEEPING; } break; case BL_STATE_SLEEPING: mCond.waitRelative(mStateLock, seconds(mPollIntervalSec)); mPollCount = 0; mLevelCount = 0; mState = BL_STATE_POLLING; break; default: ALOGE("unknown state: %d", mState); mState = BL_STATE_IDLE; continue; } } if (mState == BL_STATE_SLEEPING) { Mutex::Autolock _l(mCallbackLock); if (mCallBack != NULL) { mCallBack(isLevel, mUserData); } continue; } mLooper->pollOnce(BL_SENSOR_POLL_TIMEOUT_MS); } ALOGV("threadLoop EXIT"); return false; } int BubbleLevelImpl::setCallback(BubbleLevel_CallBack_t callback, void *userData) { Mutex::Autolock _l(mCallbackLock); if (mInitStatus != 0) { return mInitStatus; } mCallBack = callback; mUserData = userData; return 0; } int BubbleLevelImpl::setPollInterval(unsigned int seconds) { if (seconds < BL_POLL_INTERVAL_MIN_SEC) { return -EINVAL; } Mutex::Autolock _l(mStateLock); if (mInitStatus != 0) { return mInitStatus; } mPollIntervalSec = seconds; return 0; } int BubbleLevelImpl::startPolling() { Mutex::Autolock _l(mStateLock); if (mInitStatus != 0) { return mInitStatus; } if (mCmd != BL_CMD_EXIT) { mCmd = BL_CMD_START_POLL; mCond.signal(); } return 0; } int BubbleLevelImpl::stopPolling() { Mutex::Autolock _l(mStateLock); if (mInitStatus != 0) { return mInitStatus; } if (mCmd != BL_CMD_EXIT) { mCmd = BL_CMD_STOP_POLL; mCond.signal(); } return 0; } int BubbleLevelImpl::pollOnce() { Mutex::Autolock _l(mStateLock); if (mInitStatus != 0) { return mInitStatus; } if (mCmd != BL_CMD_EXIT) { mCmd = BL_CMD_POLL_ONCE; mCond.signal(); } return 0; } static int sensor_callback(int fd, int events, void* data) { sp<BubbleLevelImpl> bl = sp<BubbleLevelImpl>((BubbleLevelImpl *)data); bl->lockState(); if (((bl->state() != BubbleLevelImpl::BL_STATE_POLLING) && (bl->state() != BubbleLevelImpl::BL_STATE_POLLING_ONCE)) || (bl->pollCount() >= BL_SENSOR_POLL_COUNT)) { bl->unlockState(); return 1; } bl->unlockState(); sp<SensorEventQueue> sensorEventQueue = bl->sensorEventQueue(); size_t numSensors = bl->numSensors(); bool isLevel = false; ASensorEvent sensorEvents[numSensors]; ssize_t ret = sensorEventQueue->read(sensorEvents, numSensors); if (ret > 0) { for (int i = 0; i < ret; i++) { if (sensorEvents[i].type == Sensor::TYPE_ACCELEROMETER) { ALOGV("sensor_callback() azimuth = %f pitch = %f roll = %f", sensorEvents[i].vector.azimuth, sensorEvents[i].vector.pitch, sensorEvents[i].vector.roll); if ((sensorEvents[i].vector.roll > 0.0) && (sensorEvents[i].vector.azimuth < BL_SENSOR_LEVEL_THRESHOLD) && (sensorEvents[i].vector.azimuth > -BL_SENSOR_LEVEL_THRESHOLD) && (sensorEvents[i].vector.pitch < BL_SENSOR_LEVEL_THRESHOLD) && (sensorEvents[i].vector.pitch > -BL_SENSOR_LEVEL_THRESHOLD)) { isLevel = true; } break; } } } bl->lockState(); bl->incPollCount(); if (isLevel) { bl->incLevelCount(); } bl->unlockState(); return 1; } }; // namespace android extern "C" { static int bl_set_callback(const struct bubble_level *bubble_level, BubbleLevel_CallBack_t callback, void *userData) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; return bl->bubble_level->setCallback(callback, userData); } static int bl_set_poll_interval(const struct bubble_level *bubble_level, unsigned int seconds) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; return bl->bubble_level->setPollInterval(seconds); } static int bl_start_polling(const struct bubble_level *bubble_level) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; return bl->bubble_level->startPolling(); } static int bl_stop_polling(const struct bubble_level *bubble_level) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; return bl->bubble_level->stopPolling(); } static int bl_poll_once(const struct bubble_level *bubble_level) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; return bl->bubble_level->pollOnce(); } struct bubble_level *bubble_level_create() { bubble_level_C_impl *bl = new bubble_level_C_impl(); bl->bubble_level = new android::BubbleLevelImpl(); if (bl->bubble_level->initStatus() != 0) { bubble_level_release((struct bubble_level *)bl); return NULL; } bl->interface.set_callback = bl_set_callback; bl->interface.set_poll_interval = bl_set_poll_interval; bl->interface.start_polling = bl_start_polling; bl->interface.stop_polling = bl_stop_polling; bl->interface.poll_once = bl_poll_once; return (struct bubble_level *)bl; } void bubble_level_release(const struct bubble_level *bubble_level) { bubble_level_C_impl *bl = (bubble_level_C_impl *)bubble_level; if (bl == NULL) return; bl->bubble_level.clear(); delete bubble_level; } };