/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* Copyright 2012 Mozilla Foundation and Mozilla contributors * * 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. */ #include #include #include "mozilla/DebugOnly.h" #include "base/basictypes.h" #include "base/thread.h" #include "Hal.h" #include "HalLog.h" #include "HalSensor.h" #include "hardware/sensors.h" #include "nsThreadUtils.h" using namespace mozilla::hal; namespace mozilla { // The value from SensorDevice.h (Android) #define DEFAULT_DEVICE_POLL_RATE 200000000 /*200ms*/ // ProcessOrientation.cpp needs smaller poll rate to detect delay between // different orientation angles #define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/ // This is present in Android from API level 18 onwards, which is 4.3. We might // be building on something before 4.3, so use a local define for its value #define MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR 15 double radToDeg(double a) { return a * (180.0 / M_PI); } static SensorType HardwareSensorToHalSensor(int type) { switch(type) { case SENSOR_TYPE_ORIENTATION: return SENSOR_ORIENTATION; case SENSOR_TYPE_ACCELEROMETER: return SENSOR_ACCELERATION; case SENSOR_TYPE_PROXIMITY: return SENSOR_PROXIMITY; case SENSOR_TYPE_LIGHT: return SENSOR_LIGHT; case SENSOR_TYPE_GYROSCOPE: return SENSOR_GYROSCOPE; case SENSOR_TYPE_LINEAR_ACCELERATION: return SENSOR_LINEAR_ACCELERATION; case SENSOR_TYPE_ROTATION_VECTOR: return SENSOR_ROTATION_VECTOR; case MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR: return SENSOR_GAME_ROTATION_VECTOR; default: return SENSOR_UNKNOWN; } } static SensorAccuracyType HardwareStatusToHalAccuracy(int status) { return static_cast(status); } static int HalSensorToHardwareSensor(SensorType type) { switch(type) { case SENSOR_ORIENTATION: return SENSOR_TYPE_ORIENTATION; case SENSOR_ACCELERATION: return SENSOR_TYPE_ACCELEROMETER; case SENSOR_PROXIMITY: return SENSOR_TYPE_PROXIMITY; case SENSOR_LIGHT: return SENSOR_TYPE_LIGHT; case SENSOR_GYROSCOPE: return SENSOR_TYPE_GYROSCOPE; case SENSOR_LINEAR_ACCELERATION: return SENSOR_TYPE_LINEAR_ACCELERATION; case SENSOR_ROTATION_VECTOR: return SENSOR_TYPE_ROTATION_VECTOR; case SENSOR_GAME_ROTATION_VECTOR: return MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR; default: return -1; } } static int SensorseventStatus(const sensors_event_t& data) { int type = data.type; switch(type) { case SENSOR_ORIENTATION: return data.orientation.status; case SENSOR_LINEAR_ACCELERATION: case SENSOR_ACCELERATION: return data.acceleration.status; case SENSOR_GYROSCOPE: return data.gyro.status; } return SENSOR_STATUS_UNRELIABLE; } class SensorRunnable : public nsRunnable { public: SensorRunnable(const sensors_event_t& data, const sensor_t* sensors, ssize_t size) { mSensorData.sensor() = HardwareSensorToHalSensor(data.type); mSensorData.accuracy() = HardwareStatusToHalAccuracy(SensorseventStatus(data)); mSensorData.timestamp() = data.timestamp; if (mSensorData.sensor() == SENSOR_GYROSCOPE) { // libhardware returns gyro as rad. convert. mSensorValues.AppendElement(radToDeg(data.data[0])); mSensorValues.AppendElement(radToDeg(data.data[1])); mSensorValues.AppendElement(radToDeg(data.data[2])); } else if (mSensorData.sensor() == SENSOR_PROXIMITY) { mSensorValues.AppendElement(data.data[0]); mSensorValues.AppendElement(0); // Determine the maxRange for this sensor. for (ssize_t i = 0; i < size; i++) { if (sensors[i].type == SENSOR_TYPE_PROXIMITY) { mSensorValues.AppendElement(sensors[i].maxRange); } } } else if (mSensorData.sensor() == SENSOR_LIGHT) { mSensorValues.AppendElement(data.data[0]); } else if (mSensorData.sensor() == SENSOR_ROTATION_VECTOR) { mSensorValues.AppendElement(data.data[0]); mSensorValues.AppendElement(data.data[1]); mSensorValues.AppendElement(data.data[2]); if (data.data[3] == 0.0) { // data.data[3] was optional in Android <= API level 18. It can be computed from 012, // but it's better to take the actual value if one is provided. The computation is // v = 1 - d[0]*d[0] - d[1]*d[1] - d[2]*d[2] // d[3] = v > 0 ? sqrt(v) : 0; // I'm assuming that it will be 0 if it's not passed in. (The values form a unit // quaternion, so the angle can be computed from the direction vector.) float sx = data.data[0], sy = data.data[1], sz = data.data[2]; float v = 1.0f - sx*sx - sy*sy - sz*sz; mSensorValues.AppendElement(v > 0.0f ? sqrt(v) : 0.0f); } else { mSensorValues.AppendElement(data.data[3]); } } else if (mSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) { mSensorValues.AppendElement(data.data[0]); mSensorValues.AppendElement(data.data[1]); mSensorValues.AppendElement(data.data[2]); mSensorValues.AppendElement(data.data[3]); } else { mSensorValues.AppendElement(data.data[0]); mSensorValues.AppendElement(data.data[1]); mSensorValues.AppendElement(data.data[2]); } mSensorData.values() = mSensorValues; } ~SensorRunnable() {} NS_IMETHOD Run() { NotifySensorChange(mSensorData); return NS_OK; } private: SensorData mSensorData; nsAutoTArray mSensorValues; }; namespace hal_impl { static DebugOnly sSensorRefCount[NUM_SENSOR_TYPE]; static base::Thread* sPollingThread; static sensors_poll_device_t* sSensorDevice; static sensors_module_t* sSensorModule; static void PollSensors() { const size_t numEventMax = 16; sensors_event_t buffer[numEventMax]; const sensor_t* sensors; int size = sSensorModule->get_sensors_list(sSensorModule, &sensors); do { // didn't check sSensorDevice because already be done on creating pollingThread. int n = sSensorDevice->poll(sSensorDevice, buffer, numEventMax); if (n < 0) { HAL_ERR("Error polling for sensor data (err=%d)", n); break; } for (int i = 0; i < n; ++i) { // FIXME: bug 802004, add proper support for the magnetic field sensor. if (buffer[i].type == SENSOR_TYPE_MAGNETIC_FIELD) continue; // Bug 938035, transfer HAL data for orientation sensor to meet w3c spec // ex: HAL report alpha=90 means East but alpha=90 means West in w3c spec if (buffer[i].type == SENSOR_TYPE_ORIENTATION) { buffer[i].orientation.azimuth = 360 - buffer[i].orientation.azimuth; buffer[i].orientation.pitch = -buffer[i].orientation.pitch; buffer[i].orientation.roll = -buffer[i].orientation.roll; } if (HardwareSensorToHalSensor(buffer[i].type) == SENSOR_UNKNOWN) { // Emulator is broken and gives us events without types set int index; for (index = 0; index < size; index++) { if (sensors[index].handle == buffer[i].sensor) { break; } } if (index < size && HardwareSensorToHalSensor(sensors[index].type) != SENSOR_UNKNOWN) { buffer[i].type = sensors[index].type; } else { HAL_LOG("Could not determine sensor type of event"); continue; } } NS_DispatchToMainThread(new SensorRunnable(buffer[i], sensors, size)); } } while (true); } static void SwitchSensor(bool aActivate, sensor_t aSensor, pthread_t aThreadId) { int index = HardwareSensorToHalSensor(aSensor.type); MOZ_ASSERT(sSensorRefCount[index] || aActivate); sSensorDevice->activate(sSensorDevice, aSensor.handle, aActivate); if (aActivate) { if (aSensor.type == SENSOR_TYPE_ACCELEROMETER) { sSensorDevice->setDelay(sSensorDevice, aSensor.handle, ACCELEROMETER_POLL_RATE); } else { sSensorDevice->setDelay(sSensorDevice, aSensor.handle, DEFAULT_DEVICE_POLL_RATE); } } if (aActivate) { sSensorRefCount[index]++; } else { sSensorRefCount[index]--; } } static void SetSensorState(SensorType aSensor, bool activate) { int type = HalSensorToHardwareSensor(aSensor); const sensor_t* sensors = nullptr; int size = sSensorModule->get_sensors_list(sSensorModule, &sensors); for (ssize_t i = 0; i < size; i++) { if (sensors[i].type == type) { SwitchSensor(activate, sensors[i], pthread_self()); break; } } } void EnableSensorNotifications(SensorType aSensor) { if (!sSensorModule) { hw_get_module(SENSORS_HARDWARE_MODULE_ID, (hw_module_t const**)&sSensorModule); if (!sSensorModule) { HAL_ERR("Can't get sensor HAL module\n"); return; } sensors_open(&sSensorModule->common, &sSensorDevice); if (!sSensorDevice) { sSensorModule = nullptr; HAL_ERR("Can't get sensor poll device from module \n"); return; } sensor_t const* sensors; int count = sSensorModule->get_sensors_list(sSensorModule, &sensors); for (size_t i=0 ; iactivate(sSensorDevice, sensors[i].handle, 0); } } if (!sPollingThread) { sPollingThread = new base::Thread("GonkSensors"); MOZ_ASSERT(sPollingThread); // sPollingThread never terminates because poll may never return sPollingThread->Start(); sPollingThread->message_loop()->PostTask(FROM_HERE, NewRunnableFunction(PollSensors)); } SetSensorState(aSensor, true); } void DisableSensorNotifications(SensorType aSensor) { if (!sSensorModule) { return; } SetSensorState(aSensor, false); } } // hal_impl } // mozilla