blob: c45b0a115abfb6af2c17e01e81ea3da9ed3504ff [file] [log] [blame]
/*
* 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;
}
};