1/*
2 * Copyright (C) Texas Instruments - http://www.ti.com/
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/**
18* @file SensorListener.cpp
19*
20* This file listens and propogates sensor events to CameraHal.
21*
22*/
23
24#define LOG_TAG "CameraHAL"
25
26#include "SensorListener.h"
27#include "CameraHal.h"
28
29#include <stdint.h>
30#include <math.h>
31#include <sys/types.h>
32
33namespace android {
34
35/*** static declarations ***/
36static const float RADIANS_2_DEG = (float) (180 / M_PI);
37// measured values on device...might need tuning
38static const int DEGREES_90_THRESH = 50;
39static const int DEGREES_180_THRESH = 170;
40static const int DEGREES_270_THRESH = 250;
41
42static int sensor_events_listener(int fd, int events, void* data)
43{
44    SensorListener* listener = (SensorListener*) data;
45    ssize_t num_sensors;
46    ASensorEvent sen_events[8];
47    while ((num_sensors = listener->mSensorEventQueue->read(sen_events, 8)) > 0) {
48        for (int i = 0; i < num_sensors; i++) {
49            if (sen_events[i].type == Sensor::TYPE_ACCELEROMETER) {
50                float x = sen_events[i].vector.azimuth;
51                float y = sen_events[i].vector.pitch;
52                float z = sen_events[i].vector.roll;
53                float radius = 0;
54                int tilt = 0, orient = 0;
55
56                CAMHAL_LOGVA("ACCELEROMETER EVENT");
57                CAMHAL_LOGVB(" azimuth = %f pitch = %f roll = %f",
58                             sen_events[i].vector.azimuth,
59                             sen_events[i].vector.pitch,
60                             sen_events[i].vector.roll);
61                // see http://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
62                // about conversion from cartesian to spherical for orientation calculations
63                radius = (float) sqrt(x * x + y * y + z * z);
64                tilt = (int) asinf(z / radius) * RADIANS_2_DEG;
65                orient = (int) atan2f(-x, y) * RADIANS_2_DEG;
66
67                if (orient < 0) {
68                    orient += 360;
69                }
70
71                if (orient >= DEGREES_270_THRESH) {
72                    orient = 270;
73                } else if (orient >= DEGREES_180_THRESH) {
74                    orient = 180;
75                } else if (orient >= DEGREES_90_THRESH) {
76                    orient = 90;
77                } else {
78                    orient = 0;
79                }
80                listener->handleOrientation(orient, tilt);
81                CAMHAL_LOGVB(" tilt = %d orientation = %d", tilt, orient);
82            } else if (sen_events[i].type == Sensor::TYPE_GYROSCOPE) {
83                CAMHAL_LOGVA("GYROSCOPE EVENT");
84            }
85        }
86    }
87
88    if (num_sensors < 0 && num_sensors != -EAGAIN) {
89        CAMHAL_LOGEB("reading events failed: %s", strerror(-num_sensors));
90    }
91
92    return 1;
93}
94
95/****** public - member functions ******/
96SensorListener::SensorListener() {
97    LOG_FUNCTION_NAME;
98
99    sensorsEnabled = 0;
100    mOrientationCb = NULL;
101    mSensorEventQueue = NULL;
102    mSensorLooperThread = NULL;
103
104    LOG_FUNCTION_NAME_EXIT;
105}
106
107SensorListener::~SensorListener() {
108    LOG_FUNCTION_NAME;
109
110    CAMHAL_LOGDA("Kill looper thread");
111    if (mSensorLooperThread.get()) {
112        // 1. Request exit
113        // 2. Wake up looper which should be polling for an event
114        // 3. Wait for exit
115        mSensorLooperThread->requestExit();
116        mSensorLooperThread->wake();
117        mSensorLooperThread->join();
118        mSensorLooperThread.clear();
119        mSensorLooperThread = NULL;
120    }
121
122    CAMHAL_LOGDA("Kill looper");
123    if (mLooper.get()) {
124        mLooper->removeFd(mSensorEventQueue->getFd());
125        mLooper.clear();
126        mLooper = NULL;
127    }
128    CAMHAL_LOGDA("SensorListener destroyed");
129
130    LOG_FUNCTION_NAME_EXIT;
131}
132
133status_t SensorListener::initialize() {
134    status_t ret = NO_ERROR;
135    SensorManager& mgr(SensorManager::getInstance());
136
137    LOG_FUNCTION_NAME;
138
139    sp<Looper> mLooper;
140
141    mSensorEventQueue = mgr.createEventQueue();
142    if (mSensorEventQueue == NULL) {
143        CAMHAL_LOGEA("createEventQueue returned NULL");
144        ret = NO_INIT;
145        goto out;
146    }
147
148    mLooper = new Looper(false);
149    mLooper->addFd(mSensorEventQueue->getFd(), 0, ALOOPER_EVENT_INPUT, sensor_events_listener, this);
150
151    if (mSensorLooperThread.get() == NULL)
152            mSensorLooperThread = new SensorLooperThread(mLooper.get());
153
154    if (mSensorLooperThread.get() == NULL) {
155        CAMHAL_LOGEA("Couldn't create sensor looper thread");
156        ret = NO_MEMORY;
157        goto out;
158    }
159
160    ret = mSensorLooperThread->run("sensor looper thread", PRIORITY_URGENT_DISPLAY);
161    if (ret == INVALID_OPERATION){
162        CAMHAL_LOGDA("thread already running ?!?");
163    } else if (ret != NO_ERROR) {
164        CAMHAL_LOGEA("couldn't run thread");
165        goto out;
166    }
167
168 out:
169    LOG_FUNCTION_NAME_EXIT;
170    return ret;
171}
172
173void SensorListener::setCallbacks(orientation_callback_t orientation_cb, void *cookie) {
174    LOG_FUNCTION_NAME;
175
176    if (orientation_cb) {
177        mOrientationCb = orientation_cb;
178    }
179    mCbCookie = cookie;
180
181    LOG_FUNCTION_NAME_EXIT;
182}
183
184void SensorListener::handleOrientation(uint32_t orientation, uint32_t tilt) {
185    LOG_FUNCTION_NAME;
186
187    Mutex::Autolock lock(&mLock);
188
189    if (mOrientationCb && (sensorsEnabled & SENSOR_ORIENTATION)) {
190        mOrientationCb(orientation, tilt, mCbCookie);
191    }
192
193    LOG_FUNCTION_NAME_EXIT;
194}
195
196void SensorListener::enableSensor(sensor_type_t type) {
197    Sensor const* sensor;
198    SensorManager& mgr(SensorManager::getInstance());
199
200    LOG_FUNCTION_NAME;
201
202    Mutex::Autolock lock(&mLock);
203
204    if ((type & SENSOR_ORIENTATION) && !(sensorsEnabled & SENSOR_ORIENTATION)) {
205        sensor = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
206        CAMHAL_LOGDB("orientation = %p (%s)", sensor, sensor->getName().string());
207        mSensorEventQueue->enableSensor(sensor);
208        mSensorEventQueue->setEventRate(sensor, ms2ns(100));
209        sensorsEnabled |= SENSOR_ORIENTATION;
210    }
211
212    LOG_FUNCTION_NAME_EXIT;
213}
214
215void SensorListener::disableSensor(sensor_type_t type) {
216    Sensor const* sensor;
217    SensorManager& mgr(SensorManager::getInstance());
218
219    LOG_FUNCTION_NAME;
220
221    Mutex::Autolock lock(&mLock);
222
223    if ((type & SENSOR_ORIENTATION) && (sensorsEnabled & SENSOR_ORIENTATION)) {
224        sensor = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
225        CAMHAL_LOGDB("orientation = %p (%s)", sensor, sensor->getName().string());
226        mSensorEventQueue->disableSensor(sensor);
227        sensorsEnabled &= ~SENSOR_ORIENTATION;
228    }
229
230    LOG_FUNCTION_NAME_EXIT;
231}
232
233} // namespace android
234