blob: d0504fb481ca91c32fbdff5806ad4bd6fbe84abc [file] [log] [blame]
/*
* Copyright 2008, 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 "SensorManager"
#include <nativehelper/JNIHelp.h>
#include "android_os_MessageQueue.h"
#include "core_jni_helpers.h"
#include "jni.h"
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <vndk/hardware_buffer.h>
#include <sensor/Sensor.h>
#include <sensor/SensorEventQueue.h>
#include <sensor/SensorManager.h>
#include <cutils/native_handle.h>
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/Vector.h>
#include <map>
namespace {
using namespace android;
struct {
jclass clazz;
jmethodID dispatchSensorEvent;
jmethodID dispatchFlushCompleteEvent;
jmethodID dispatchAdditionalInfoEvent;
} gBaseEventQueueClassInfo;
struct SensorOffsets
{
jclass clazz;
//fields
jfieldID name;
jfieldID vendor;
jfieldID version;
jfieldID handle;
jfieldID range;
jfieldID resolution;
jfieldID power;
jfieldID minDelay;
jfieldID fifoReservedEventCount;
jfieldID fifoMaxEventCount;
jfieldID stringType;
jfieldID requiredPermission;
jfieldID maxDelay;
jfieldID flags;
//methods
jmethodID setType;
jmethodID setUuid;
jmethodID init;
} gSensorOffsets;
struct ListOffsets {
jclass clazz;
jmethodID add;
} gListOffsets;
struct StringOffsets {
jclass clazz;
jmethodID intern;
jstring emptyString;
} gStringOffsets;
/*
* nativeClassInit is not inteneded to be thread-safe. It should be called before other native...
* functions (except nativeCreate).
*/
static void
nativeClassInit (JNIEnv *_env, jclass _this)
{
//android.hardware.Sensor
SensorOffsets& sensorOffsets = gSensorOffsets;
jclass sensorClass = (jclass)
MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "android/hardware/Sensor"));
sensorOffsets.clazz = sensorClass;
sensorOffsets.name = GetFieldIDOrDie(_env, sensorClass, "mName", "Ljava/lang/String;");
sensorOffsets.vendor = GetFieldIDOrDie(_env, sensorClass, "mVendor", "Ljava/lang/String;");
sensorOffsets.version = GetFieldIDOrDie(_env, sensorClass, "mVersion", "I");
sensorOffsets.handle = GetFieldIDOrDie(_env, sensorClass, "mHandle", "I");
sensorOffsets.range = GetFieldIDOrDie(_env, sensorClass, "mMaxRange", "F");
sensorOffsets.resolution = GetFieldIDOrDie(_env, sensorClass, "mResolution","F");
sensorOffsets.power = GetFieldIDOrDie(_env, sensorClass, "mPower", "F");
sensorOffsets.minDelay = GetFieldIDOrDie(_env, sensorClass, "mMinDelay", "I");
sensorOffsets.fifoReservedEventCount =
GetFieldIDOrDie(_env,sensorClass, "mFifoReservedEventCount", "I");
sensorOffsets.fifoMaxEventCount = GetFieldIDOrDie(_env,sensorClass, "mFifoMaxEventCount", "I");
sensorOffsets.stringType =
GetFieldIDOrDie(_env,sensorClass, "mStringType", "Ljava/lang/String;");
sensorOffsets.requiredPermission =
GetFieldIDOrDie(_env,sensorClass, "mRequiredPermission", "Ljava/lang/String;");
sensorOffsets.maxDelay = GetFieldIDOrDie(_env,sensorClass, "mMaxDelay", "I");
sensorOffsets.flags = GetFieldIDOrDie(_env,sensorClass, "mFlags", "I");
sensorOffsets.setType = GetMethodIDOrDie(_env,sensorClass, "setType", "(I)Z");
sensorOffsets.setUuid = GetMethodIDOrDie(_env,sensorClass, "setUuid", "(JJ)V");
sensorOffsets.init = GetMethodIDOrDie(_env,sensorClass, "<init>", "()V");
// java.util.List;
ListOffsets& listOffsets = gListOffsets;
jclass listClass = (jclass) MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "java/util/List"));
listOffsets.clazz = listClass;
listOffsets.add = GetMethodIDOrDie(_env,listClass, "add", "(Ljava/lang/Object;)Z");
// initialize java.lang.String and empty string intern
StringOffsets& stringOffsets = gStringOffsets;
stringOffsets.clazz = MakeGlobalRefOrDie(_env, FindClassOrDie(_env, "java/lang/String"));
stringOffsets.intern =
GetMethodIDOrDie(_env, stringOffsets.clazz, "intern", "()Ljava/lang/String;");
ScopedLocalRef<jstring> empty(_env, _env->NewStringUTF(""));
stringOffsets.emptyString = (jstring)
MakeGlobalRefOrDie(_env, _env->CallObjectMethod(empty.get(), stringOffsets.intern));
}
static jstring getJavaInternedString(JNIEnv *env, const String8 &string) {
if (string == "") {
return gStringOffsets.emptyString;
}
ScopedLocalRef<jstring> javaString(env, env->NewStringUTF(string.string()));
jstring internedString = (jstring)
env->CallObjectMethod(javaString.get(), gStringOffsets.intern);
return internedString;
}
static jlong
nativeCreate
(JNIEnv *env, jclass clazz, jstring opPackageName)
{
ScopedUtfChars opPackageNameUtf(env, opPackageName);
return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
}
static jobject
translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nativeSensor) {
const SensorOffsets& sensorOffsets(gSensorOffsets);
if (sensor == NULL) {
// Sensor sensor = new Sensor();
sensor = env->NewObject(sensorOffsets.clazz, sensorOffsets.init, "");
}
if (sensor != NULL) {
jstring name = getJavaInternedString(env, nativeSensor.getName());
jstring vendor = getJavaInternedString(env, nativeSensor.getVendor());
jstring requiredPermission =
getJavaInternedString(env, nativeSensor.getRequiredPermission());
env->SetObjectField(sensor, sensorOffsets.name, name);
env->SetObjectField(sensor, sensorOffsets.vendor, vendor);
env->SetIntField(sensor, sensorOffsets.version, nativeSensor.getVersion());
env->SetIntField(sensor, sensorOffsets.handle, nativeSensor.getHandle());
env->SetFloatField(sensor, sensorOffsets.range, nativeSensor.getMaxValue());
env->SetFloatField(sensor, sensorOffsets.resolution, nativeSensor.getResolution());
env->SetFloatField(sensor, sensorOffsets.power, nativeSensor.getPowerUsage());
env->SetIntField(sensor, sensorOffsets.minDelay, nativeSensor.getMinDelay());
env->SetIntField(sensor, sensorOffsets.fifoReservedEventCount,
nativeSensor.getFifoReservedEventCount());
env->SetIntField(sensor, sensorOffsets.fifoMaxEventCount,
nativeSensor.getFifoMaxEventCount());
env->SetObjectField(sensor, sensorOffsets.requiredPermission,
requiredPermission);
env->SetIntField(sensor, sensorOffsets.maxDelay, nativeSensor.getMaxDelay());
env->SetIntField(sensor, sensorOffsets.flags, nativeSensor.getFlags());
if (env->CallBooleanMethod(sensor, sensorOffsets.setType, nativeSensor.getType())
== JNI_FALSE) {
jstring stringType = getJavaInternedString(env, nativeSensor.getStringType());
env->SetObjectField(sensor, sensorOffsets.stringType, stringType);
}
// TODO(b/29547335): Rename "setUuid" method to "setId".
int64_t id = nativeSensor.getId();
env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, 0);
}
return sensor;
}
static jboolean
nativeGetSensorAtIndex(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensor, jint index)
{
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
Sensor const* const* sensorList;
ssize_t count = mgr->getSensorList(&sensorList);
if (ssize_t(index) >= count) {
return false;
}
return translateNativeSensorToJavaSensor(env, sensor, *sensorList[index]) != NULL;
}
static void
nativeGetDynamicSensors(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensorList) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
const ListOffsets& listOffsets(gListOffsets);
Vector<Sensor> nativeList;
mgr->getDynamicSensorList(nativeList);
ALOGI("DYNS native SensorManager.getDynamicSensorList return %zu sensors", nativeList.size());
for (size_t i = 0; i < nativeList.size(); ++i) {
jobject sensor = translateNativeSensorToJavaSensor(env, NULL, nativeList[i]);
// add to list
env->CallBooleanMethod(sensorList, listOffsets.add, sensor);
}
}
static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong sensorManager) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
return mgr->isDataInjectionEnabled();
}
static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
jlong size, jint channelType, jint fd, jobject hardwareBufferObj) {
const native_handle_t *nativeHandle = nullptr;
NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0);
if (channelType == SENSOR_DIRECT_MEM_TYPE_ASHMEM) {
native_handle_t *handle = native_handle_init(ashmemHandle, 1, 0);
handle->data[0] = fd;
nativeHandle = handle;
} else if (channelType == SENSOR_DIRECT_MEM_TYPE_GRALLOC) {
AHardwareBuffer *hardwareBuffer =
android_hardware_HardwareBuffer_getNativeHardwareBuffer(_env, hardwareBufferObj);
if (hardwareBuffer != nullptr) {
nativeHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
}
}
if (nativeHandle == nullptr) {
return BAD_VALUE;
}
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
return mgr->createDirectChannel(size, channelType, nativeHandle);
}
static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
jint channelHandle) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
mgr->destroyDirectChannel(channelHandle);
}
static jint nativeConfigDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager,
jint channelHandle, jint sensorHandle, jint rate) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
return mgr->configureDirectChannel(channelHandle, sensorHandle, rate);
}
static jint nativeSetOperationParameter(JNIEnv *_env, jclass _this, jlong sensorManager,
jint handle, jint type, jfloatArray floats, jintArray ints) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
Vector<float> floatVector;
Vector<int32_t> int32Vector;
if (floats != nullptr) {
floatVector.resize(_env->GetArrayLength(floats));
_env->GetFloatArrayRegion(floats, 0, _env->GetArrayLength(floats), floatVector.editArray());
}
if (ints != nullptr) {
int32Vector.resize(_env->GetArrayLength(ints));
_env->GetIntArrayRegion(ints, 0, _env->GetArrayLength(ints), int32Vector.editArray());
}
return mgr->setOperationParameter(handle, type, floatVector, int32Vector);
}
//----------------------------------------------------------------------------
class Receiver : public LooperCallback {
sp<SensorEventQueue> mSensorQueue;
sp<MessageQueue> mMessageQueue;
jobject mReceiverWeakGlobal;
jfloatArray mFloatScratch;
jintArray mIntScratch;
public:
Receiver(const sp<SensorEventQueue>& sensorQueue,
const sp<MessageQueue>& messageQueue,
jobject receiverWeak) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
mSensorQueue = sensorQueue;
mMessageQueue = messageQueue;
mReceiverWeakGlobal = env->NewGlobalRef(receiverWeak);
mIntScratch = (jintArray) env->NewGlobalRef(env->NewIntArray(16));
mFloatScratch = (jfloatArray) env->NewGlobalRef(env->NewFloatArray(16));
}
~Receiver() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mReceiverWeakGlobal);
env->DeleteGlobalRef(mFloatScratch);
env->DeleteGlobalRef(mIntScratch);
}
sp<SensorEventQueue> getSensorEventQueue() const {
return mSensorQueue;
}
void destroy() {
mMessageQueue->getLooper()->removeFd( mSensorQueue->getFd() );
}
private:
virtual void onFirstRef() {
LooperCallback::onFirstRef();
mMessageQueue->getLooper()->addFd(mSensorQueue->getFd(), 0,
ALOOPER_EVENT_INPUT, this, mSensorQueue.get());
}
virtual int handleEvent(int fd, int events, void* data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
sp<SensorEventQueue> q = reinterpret_cast<SensorEventQueue *>(data);
ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal));
ssize_t n;
ASensorEvent buffer[16];
while ((n = q->read(buffer, 16)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].type == SENSOR_TYPE_STEP_COUNTER) {
// step-counter returns a uint64, but the java API only deals with floats
float value = float(buffer[i].u64.step_counter);
env->SetFloatArrayRegion(mFloatScratch, 0, 1, &value);
} else if (buffer[i].type == SENSOR_TYPE_DYNAMIC_SENSOR_META) {
float value[2];
value[0] = buffer[i].dynamic_sensor_meta.connected ? 1.f: 0.f;
value[1] = float(buffer[i].dynamic_sensor_meta.handle);
env->SetFloatArrayRegion(mFloatScratch, 0, 2, value);
} else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
env->SetIntArrayRegion(mIntScratch, 0, 14,
buffer[i].additional_info.data_int32);
env->SetFloatArrayRegion(mFloatScratch, 0, 14,
buffer[i].additional_info.data_float);
} else {
env->SetFloatArrayRegion(mFloatScratch, 0, 16, buffer[i].data);
}
if (buffer[i].type == SENSOR_TYPE_META_DATA) {
// This is a flush complete sensor event. Call dispatchFlushCompleteEvent
// method.
if (receiverObj.get()) {
env->CallVoidMethod(receiverObj.get(),
gBaseEventQueueClassInfo.dispatchFlushCompleteEvent,
buffer[i].meta_data.sensor);
}
} else if (buffer[i].type == SENSOR_TYPE_ADDITIONAL_INFO) {
// This is a flush complete sensor event. Call dispatchAdditionalInfoEvent
// method.
if (receiverObj.get()) {
int type = buffer[i].additional_info.type;
int serial = buffer[i].additional_info.serial;
env->CallVoidMethod(receiverObj.get(),
gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent,
buffer[i].sensor,
type, serial,
mFloatScratch,
mIntScratch,
buffer[i].timestamp);
}
}else {
int8_t status;
switch (buffer[i].type) {
case SENSOR_TYPE_ORIENTATION:
case SENSOR_TYPE_MAGNETIC_FIELD:
case SENSOR_TYPE_ACCELEROMETER:
case SENSOR_TYPE_GYROSCOPE:
case SENSOR_TYPE_GRAVITY:
case SENSOR_TYPE_LINEAR_ACCELERATION:
status = buffer[i].vector.status;
break;
case SENSOR_TYPE_HEART_RATE:
status = buffer[i].heart_rate.status;
break;
default:
status = SENSOR_STATUS_ACCURACY_HIGH;
break;
}
if (receiverObj.get()) {
env->CallVoidMethod(receiverObj.get(),
gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor,
mFloatScratch,
status,
buffer[i].timestamp);
}
}
if (env->ExceptionCheck()) {
mSensorQueue->sendAck(buffer, n);
ALOGE("Exception dispatching input event.");
return 1;
}
}
mSensorQueue->sendAck(buffer, n);
}
if (n<0 && n != -EAGAIN) {
// FIXME: error receiving events, what to do in this case?
}
return 1;
}
};
static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager,
jobject eventQWeak, jobject msgQ, jstring packageName,
jint mode, jstring opPackageName, jstring attributionTag) {
SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager);
ScopedUtfChars packageUtf(env, packageName);
String8 clientName(packageUtf.c_str());
String16 attributionTagName("");
if (attributionTag != nullptr) {
ScopedUtfChars attrUtf(env, attributionTag);
attributionTagName = String16(attrUtf.c_str());
}
sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode, attributionTagName));
if (queue == NULL) {
jniThrowRuntimeException(env, "Cannot construct native SensorEventQueue.");
return 0;
}
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<Receiver> receiver = new Receiver(queue, messageQueue, eventQWeak);
receiver->incStrong((void*)nativeInitSensorEventQueue);
return jlong(receiver.get());
}
static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us,
jint maxBatchReportLatency) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,
0);
}
static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
return receiver->getSensorEventQueue()->disableSensor(handle);
}
static void nativeDestroySensorEventQueue(JNIEnv *env, jclass clazz, jlong eventQ) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
receiver->destroy();
receiver->decStrong((void*)nativeInitSensorEventQueue);
}
static jint nativeFlushSensor(JNIEnv *env, jclass clazz, jlong eventQ) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
return receiver->getSensorEventQueue()->flush();
}
static jint nativeInjectSensorData(JNIEnv *env, jclass clazz, jlong eventQ, jint handle,
jfloatArray values, jint accuracy, jlong timestamp) {
sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
// Create a sensor_event from the above data which can be injected into the HAL.
ASensorEvent sensor_event;
memset(&sensor_event, 0, sizeof(sensor_event));
sensor_event.sensor = handle;
sensor_event.timestamp = timestamp;
env->GetFloatArrayRegion(values, 0, env->GetArrayLength(values), sensor_event.data);
return receiver->getSensorEventQueue()->injectSensorEvent(sensor_event);
}
//----------------------------------------------------------------------------
static const JNINativeMethod gSystemSensorManagerMethods[] = {
{"nativeClassInit",
"()V",
(void*)nativeClassInit },
{"nativeCreate",
"(Ljava/lang/String;)J",
(void*)nativeCreate },
{"nativeGetSensorAtIndex",
"(JLandroid/hardware/Sensor;I)Z",
(void*)nativeGetSensorAtIndex },
{"nativeGetDynamicSensors",
"(JLjava/util/List;)V",
(void*)nativeGetDynamicSensors },
{"nativeIsDataInjectionEnabled",
"(J)Z",
(void*)nativeIsDataInjectionEnabled },
{"nativeCreateDirectChannel",
"(JJIILandroid/hardware/HardwareBuffer;)I",
(void*)nativeCreateDirectChannel },
{"nativeDestroyDirectChannel",
"(JI)V",
(void*)nativeDestroyDirectChannel },
{"nativeConfigDirectChannel",
"(JIII)I",
(void*)nativeConfigDirectChannel },
{"nativeSetOperationParameter",
"(JII[F[I)I",
(void*)nativeSetOperationParameter },
};
static const JNINativeMethod gBaseEventQueueMethods[] = {
{"nativeInitBaseEventQueue",
"(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/"
"String;Ljava/lang/String;)J",
(void *)nativeInitSensorEventQueue},
{"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor},
{"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor},
{"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue},
{"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor},
{"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData},
};
} //unnamed namespace
int register_android_hardware_SensorManager(JNIEnv *env)
{
RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager",
gSystemSensorManagerMethods, NELEM(gSystemSensorManagerMethods));
RegisterMethodsOrDie(env, "android/hardware/SystemSensorManager$BaseEventQueue",
gBaseEventQueueMethods, NELEM(gBaseEventQueueMethods));
gBaseEventQueueClassInfo.clazz = FindClassOrDie(env,
"android/hardware/SystemSensorManager$BaseEventQueue");
gBaseEventQueueClassInfo.dispatchSensorEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchSensorEvent", "(I[FIJ)V");
gBaseEventQueueClassInfo.dispatchFlushCompleteEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchFlushCompleteEvent", "(I)V");
gBaseEventQueueClassInfo.dispatchAdditionalInfoEvent = GetMethodIDOrDie(env,
gBaseEventQueueClassInfo.clazz, "dispatchAdditionalInfoEvent", "(III[F[I)V");
return 0;
}