Fix Java reference leak in the JNI layer Bug: 10624887 Change-Id: I656973653d82c73a7ba2b51f22989ffed144c5df
1003 lines
29 KiB
C++
1003 lines
29 KiB
C++
/*
|
|
* Copyright (C) 2013 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/license/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 "FlpHardwareProvider"
|
|
#define LOG_NDEBUG 0
|
|
|
|
#define WAKE_LOCK_NAME "FLP"
|
|
#define LOCATION_CLASS_NAME "android/location/Location"
|
|
|
|
#include "jni.h"
|
|
#include "JNIHelp.h"
|
|
#include "android_runtime/AndroidRuntime.h"
|
|
#include "android_runtime/Log.h"
|
|
#include "hardware/fused_location.h"
|
|
#include "hardware_legacy/power.h"
|
|
|
|
static jobject sCallbacksObj = NULL;
|
|
static JNIEnv *sCallbackEnv = NULL;
|
|
static hw_device_t* sHardwareDevice = NULL;
|
|
|
|
static jmethodID sOnLocationReport = NULL;
|
|
static jmethodID sOnDataReport = NULL;
|
|
static jmethodID sOnGeofenceTransition = NULL;
|
|
static jmethodID sOnGeofenceMonitorStatus = NULL;
|
|
static jmethodID sOnGeofenceAdd = NULL;
|
|
static jmethodID sOnGeofenceRemove = NULL;
|
|
static jmethodID sOnGeofencePause = NULL;
|
|
static jmethodID sOnGeofenceResume = NULL;
|
|
|
|
static const FlpLocationInterface* sFlpInterface = NULL;
|
|
static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL;
|
|
static const FlpGeofencingInterface* sFlpGeofencingInterface = NULL;
|
|
static const FlpDeviceContextInterface* sFlpDeviceContextInterface = NULL;
|
|
|
|
namespace android {
|
|
|
|
static inline void CheckExceptions(JNIEnv* env, const char* methodName) {
|
|
if(!env->ExceptionCheck()) {
|
|
return;
|
|
}
|
|
|
|
ALOGE("An exception was thrown by '%s'.", methodName);
|
|
LOGE_EX(env);
|
|
env->ExceptionClear();
|
|
}
|
|
|
|
static inline void ThrowOnError(
|
|
JNIEnv* env,
|
|
int resultCode,
|
|
const char* methodName) {
|
|
if(resultCode == FLP_RESULT_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
ALOGE("Error %d in '%s'", resultCode, methodName);
|
|
env->FatalError(methodName);
|
|
}
|
|
|
|
static bool IsValidCallbackThread() {
|
|
JNIEnv* env = AndroidRuntime::getJNIEnv();
|
|
|
|
if(sCallbackEnv == NULL || sCallbackEnv != env) {
|
|
ALOGE("CallbackThread check fail: env=%p, expected=%p", env, sCallbackEnv);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int SetThreadEvent(ThreadEvent event) {
|
|
JavaVM* javaVm = AndroidRuntime::getJavaVM();
|
|
|
|
switch(event) {
|
|
case ASSOCIATE_JVM:
|
|
{
|
|
if(sCallbackEnv != NULL) {
|
|
ALOGE(
|
|
"Attempted to associate callback in '%s'. Callback already associated.",
|
|
__FUNCTION__
|
|
);
|
|
return FLP_RESULT_ERROR;
|
|
}
|
|
|
|
JavaVMAttachArgs args = {
|
|
JNI_VERSION_1_6,
|
|
"FLP Service Callback Thread",
|
|
/* group */ NULL
|
|
};
|
|
|
|
jint attachResult = javaVm->AttachCurrentThread(&sCallbackEnv, &args);
|
|
if (attachResult != 0) {
|
|
ALOGE("Callback thread attachment error: %d", attachResult);
|
|
return FLP_RESULT_ERROR;
|
|
}
|
|
|
|
ALOGV("Callback thread attached: %p", sCallbackEnv);
|
|
break;
|
|
}
|
|
case DISASSOCIATE_JVM:
|
|
{
|
|
if (!IsValidCallbackThread()) {
|
|
ALOGE(
|
|
"Attempted to dissasociate an unnownk callback thread : '%s'.",
|
|
__FUNCTION__
|
|
);
|
|
return FLP_RESULT_ERROR;
|
|
}
|
|
|
|
if (javaVm->DetachCurrentThread() != 0) {
|
|
return FLP_RESULT_ERROR;
|
|
}
|
|
|
|
sCallbackEnv = NULL;
|
|
break;
|
|
}
|
|
default:
|
|
ALOGE("Invalid ThreadEvent request %d", event);
|
|
return FLP_RESULT_ERROR;
|
|
}
|
|
|
|
return FLP_RESULT_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Initializes the FlpHardwareProvider class from the native side by opening
|
|
* the HW module and obtaining the proper interfaces.
|
|
*/
|
|
static void ClassInit(JNIEnv* env, jclass clazz) {
|
|
// get references to the Java provider methods
|
|
sOnLocationReport = env->GetMethodID(
|
|
clazz,
|
|
"onLocationReport",
|
|
"([Landroid/location/Location;)V");
|
|
sOnDataReport = env->GetMethodID(
|
|
clazz,
|
|
"onDataReport",
|
|
"(Ljava/lang/String;)V"
|
|
);
|
|
sOnGeofenceTransition = env->GetMethodID(
|
|
clazz,
|
|
"onGeofenceTransition",
|
|
"(ILandroid/location/Location;IJI)V"
|
|
);
|
|
sOnGeofenceMonitorStatus = env->GetMethodID(
|
|
clazz,
|
|
"onGeofenceMonitorStatus",
|
|
"(IILandroid/location/Location;)V"
|
|
);
|
|
sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V");
|
|
sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
|
|
sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
|
|
sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
|
|
}
|
|
|
|
/*
|
|
* Helper function to unwrap a java object back into a FlpLocation structure.
|
|
*/
|
|
static void TranslateFromObject(
|
|
JNIEnv* env,
|
|
jobject locationObject,
|
|
FlpLocation& location) {
|
|
location.size = sizeof(FlpLocation);
|
|
location.flags = 0;
|
|
|
|
jclass locationClass = env->GetObjectClass(locationObject);
|
|
|
|
jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D");
|
|
location.latitude = env->CallDoubleMethod(locationObject, getLatitude);
|
|
jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D");
|
|
location.longitude = env->CallDoubleMethod(locationObject, getLongitude);
|
|
jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J");
|
|
location.timestamp = env->CallLongMethod(locationObject, getTime);
|
|
location.flags |= FLP_LOCATION_HAS_LAT_LONG;
|
|
|
|
jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z");
|
|
if (env->CallBooleanMethod(locationObject, hasAltitude)) {
|
|
jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D");
|
|
location.altitude = env->CallDoubleMethod(locationObject, getAltitude);
|
|
location.flags |= FLP_LOCATION_HAS_ALTITUDE;
|
|
}
|
|
|
|
jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z");
|
|
if (env->CallBooleanMethod(locationObject, hasSpeed)) {
|
|
jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F");
|
|
location.speed = env->CallFloatMethod(locationObject, getSpeed);
|
|
location.flags |= FLP_LOCATION_HAS_SPEED;
|
|
}
|
|
|
|
jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z");
|
|
if (env->CallBooleanMethod(locationObject, hasBearing)) {
|
|
jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F");
|
|
location.bearing = env->CallFloatMethod(locationObject, getBearing);
|
|
location.flags |= FLP_LOCATION_HAS_BEARING;
|
|
}
|
|
|
|
jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z");
|
|
if (env->CallBooleanMethod(locationObject, hasAccuracy)) {
|
|
jmethodID getAccuracy = env->GetMethodID(
|
|
locationClass,
|
|
"getAccuracy",
|
|
"()F"
|
|
);
|
|
location.accuracy = env->CallFloatMethod(locationObject, getAccuracy);
|
|
location.flags |= FLP_LOCATION_HAS_ACCURACY;
|
|
}
|
|
|
|
// TODO: wire sources_used if Location class exposes them
|
|
|
|
env->DeleteLocalRef(locationClass);
|
|
}
|
|
|
|
/*
|
|
* Helper function to unwrap FlpBatchOptions from the Java Runtime calls.
|
|
*/
|
|
static void TranslateFromObject(
|
|
JNIEnv* env,
|
|
jobject batchOptionsObject,
|
|
FlpBatchOptions& batchOptions) {
|
|
jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject);
|
|
|
|
jmethodID getMaxPower = env->GetMethodID(
|
|
batchOptionsClass,
|
|
"getMaxPowerAllocationInMW",
|
|
"()D"
|
|
);
|
|
batchOptions.max_power_allocation_mW = env->CallDoubleMethod(
|
|
batchOptionsObject,
|
|
getMaxPower
|
|
);
|
|
|
|
jmethodID getPeriod = env->GetMethodID(
|
|
batchOptionsClass,
|
|
"getPeriodInNS",
|
|
"()J"
|
|
);
|
|
batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod);
|
|
|
|
jmethodID getSourcesToUse = env->GetMethodID(
|
|
batchOptionsClass,
|
|
"getSourcesToUse",
|
|
"()I"
|
|
);
|
|
batchOptions.sources_to_use = env->CallIntMethod(
|
|
batchOptionsObject,
|
|
getSourcesToUse
|
|
);
|
|
|
|
jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
|
|
batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
|
|
|
|
env->DeleteLocalRef(batchOptionsClass);
|
|
}
|
|
|
|
/*
|
|
* Helper function to unwrap Geofence structures from the Java Runtime calls.
|
|
*/
|
|
static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
|
|
JNIEnv* env,
|
|
jobject geofenceRequestObject,
|
|
Geofence& geofence) {
|
|
jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
|
|
|
|
jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
|
|
geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
|
|
|
|
jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
|
|
// this works because GeofenceHardwareRequest.java and fused_location.h have
|
|
// the same notion of geofence types
|
|
GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
|
|
if(type != TYPE_CIRCLE) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
geofence.data->type = type;
|
|
GeofenceCircle& circle = geofence.data->geofence.circle;
|
|
|
|
jmethodID getLatitude = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getLatitude",
|
|
"()D");
|
|
circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
|
|
|
|
jmethodID getLongitude = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getLongitude",
|
|
"()D");
|
|
circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
|
|
|
|
jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
|
|
circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
|
|
|
|
GeofenceOptions* options = geofence.options;
|
|
jmethodID getMonitorTransitions = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getMonitorTransitions",
|
|
"()I");
|
|
options->monitor_transitions = env->CallIntMethod(
|
|
geofenceRequestObject,
|
|
getMonitorTransitions);
|
|
|
|
jmethodID getUnknownTimer = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getUnknownTimer",
|
|
"()I");
|
|
options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
|
|
|
|
jmethodID getNotificationResponsiveness = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getNotificationResponsiveness",
|
|
"()I");
|
|
options->notification_responsivenes_ms = env->CallIntMethod(
|
|
geofenceRequestObject,
|
|
getNotificationResponsiveness);
|
|
|
|
jmethodID getLastTransition = env->GetMethodID(
|
|
geofenceRequestClass,
|
|
"getLastTransition",
|
|
"()I");
|
|
options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
|
|
|
|
// TODO: set data.sources_to_use when available
|
|
|
|
env->DeleteLocalRef(geofenceRequestClass);
|
|
}
|
|
|
|
/*
|
|
* Helper function to transform FlpLocation into a java object.
|
|
*/
|
|
static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
|
|
jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
|
|
jmethodID locationCtor = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"<init>",
|
|
"(Ljava/lang/String;)V"
|
|
);
|
|
|
|
// the provider is set in the upper JVM layer
|
|
locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL);
|
|
jint flags = location->flags;
|
|
|
|
// set the valid information in the object
|
|
if (flags & FLP_LOCATION_HAS_LAT_LONG) {
|
|
jmethodID setLatitude = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setLatitude",
|
|
"(D)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude);
|
|
|
|
jmethodID setLongitude = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setLongitude",
|
|
"(D)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(
|
|
locationObject,
|
|
setLongitude,
|
|
location->longitude
|
|
);
|
|
|
|
jmethodID setTime = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setTime",
|
|
"(J)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp);
|
|
}
|
|
|
|
if (flags & FLP_LOCATION_HAS_ALTITUDE) {
|
|
jmethodID setAltitude = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setAltitude",
|
|
"(D)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude);
|
|
}
|
|
|
|
if (flags & FLP_LOCATION_HAS_SPEED) {
|
|
jmethodID setSpeed = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setSpeed",
|
|
"(F)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed);
|
|
}
|
|
|
|
if (flags & FLP_LOCATION_HAS_BEARING) {
|
|
jmethodID setBearing = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setBearing",
|
|
"(F)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing);
|
|
}
|
|
|
|
if (flags & FLP_LOCATION_HAS_ACCURACY) {
|
|
jmethodID setAccuracy = sCallbackEnv->GetMethodID(
|
|
locationClass,
|
|
"setAccuracy",
|
|
"(F)V"
|
|
);
|
|
sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy);
|
|
}
|
|
|
|
// TODO: wire FlpLocation::sources_used when needed
|
|
|
|
sCallbackEnv->DeleteLocalRef(locationClass);
|
|
}
|
|
|
|
/*
|
|
* Helper function to serialize FlpLocation structures.
|
|
*/
|
|
static void TranslateToObjectArray(
|
|
int32_t locationsCount,
|
|
FlpLocation** locations,
|
|
jobjectArray& locationsArray) {
|
|
jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
|
|
locationsArray = sCallbackEnv->NewObjectArray(
|
|
locationsCount,
|
|
locationClass,
|
|
/* initialElement */ NULL
|
|
);
|
|
|
|
for (int i = 0; i < locationsCount; ++i) {
|
|
jobject locationObject = NULL;
|
|
TranslateToObject(locations[i], locationObject);
|
|
sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
|
|
sCallbackEnv->DeleteLocalRef(locationObject);
|
|
}
|
|
|
|
sCallbackEnv->DeleteLocalRef(locationClass);
|
|
}
|
|
|
|
static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
if(locationsCount == 0 || locations == NULL) {
|
|
ALOGE(
|
|
"Invalid LocationCallback. Count: %d, Locations: %p",
|
|
locationsCount,
|
|
locations
|
|
);
|
|
return;
|
|
}
|
|
|
|
jobjectArray locationsArray = NULL;
|
|
TranslateToObjectArray(locationsCount, locations, locationsArray);
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnLocationReport,
|
|
locationsArray
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
|
|
if(locationsArray != NULL) {
|
|
sCallbackEnv->DeleteLocalRef(locationsArray);
|
|
}
|
|
}
|
|
|
|
static void AcquireWakelock() {
|
|
acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
|
|
}
|
|
|
|
static void ReleaseWakelock() {
|
|
release_wake_lock(WAKE_LOCK_NAME);
|
|
}
|
|
|
|
FlpCallbacks sFlpCallbacks = {
|
|
sizeof(FlpCallbacks),
|
|
LocationCallback,
|
|
AcquireWakelock,
|
|
ReleaseWakelock,
|
|
SetThreadEvent
|
|
};
|
|
|
|
static void ReportData(char* data, int length) {
|
|
jstring stringData = NULL;
|
|
|
|
if(length != 0 && data != NULL) {
|
|
stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length);
|
|
} else {
|
|
ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
}
|
|
|
|
FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = {
|
|
sizeof(FlpDiagnosticCallbacks),
|
|
SetThreadEvent,
|
|
ReportData
|
|
};
|
|
|
|
static void GeofenceTransitionCallback(
|
|
int32_t geofenceId,
|
|
FlpLocation* location,
|
|
int32_t transition,
|
|
FlpUtcTime timestamp,
|
|
uint32_t sourcesUsed
|
|
) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
if(location == NULL) {
|
|
ALOGE("GeofenceTransition received with invalid location: %p", location);
|
|
return;
|
|
}
|
|
|
|
jobject locationObject = NULL;
|
|
TranslateToObject(location, locationObject);
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnGeofenceTransition,
|
|
geofenceId,
|
|
locationObject,
|
|
transition,
|
|
timestamp,
|
|
sourcesUsed
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
|
|
if(locationObject != NULL) {
|
|
sCallbackEnv->DeleteLocalRef(locationObject);
|
|
}
|
|
}
|
|
|
|
static void GeofenceMonitorStatusCallback(
|
|
int32_t status,
|
|
uint32_t source,
|
|
FlpLocation* lastLocation) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
jobject locationObject = NULL;
|
|
if(lastLocation != NULL) {
|
|
TranslateToObject(lastLocation, locationObject);
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnGeofenceMonitorStatus,
|
|
status,
|
|
source,
|
|
locationObject
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
|
|
if(locationObject != NULL) {
|
|
sCallbackEnv->DeleteLocalRef(locationObject);
|
|
}
|
|
}
|
|
|
|
static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
}
|
|
|
|
static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnGeofenceRemove,
|
|
geofenceId,
|
|
result
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
}
|
|
|
|
static void GeofencePauseCallback(int32_t geofenceId, int32_t result) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnGeofencePause,
|
|
geofenceId,
|
|
result
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
}
|
|
|
|
static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) {
|
|
if(!IsValidCallbackThread()) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sCallbacksObj,
|
|
sOnGeofenceResume,
|
|
geofenceId,
|
|
result
|
|
);
|
|
CheckExceptions(sCallbackEnv, __FUNCTION__);
|
|
}
|
|
|
|
FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
|
|
sizeof(FlpGeofenceCallbacks),
|
|
GeofenceTransitionCallback,
|
|
GeofenceMonitorStatusCallback,
|
|
GeofenceAddCallback,
|
|
GeofenceRemoveCallback,
|
|
GeofencePauseCallback,
|
|
GeofenceResumeCallback,
|
|
SetThreadEvent
|
|
};
|
|
|
|
/*
|
|
* Initializes the Fused Location Provider in the native side. It ensures that
|
|
* the Flp interfaces are initialized properly.
|
|
*/
|
|
static void Init(JNIEnv* env, jobject obj) {
|
|
if(sHardwareDevice != NULL) {
|
|
ALOGD("Hardware Device already opened.");
|
|
return;
|
|
}
|
|
|
|
const hw_module_t* module = NULL;
|
|
int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
|
|
if(err != 0) {
|
|
ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
|
|
return;
|
|
}
|
|
|
|
err = module->methods->open(
|
|
module,
|
|
FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
|
|
if(err != 0) {
|
|
ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
|
|
return;
|
|
}
|
|
|
|
sFlpInterface = NULL;
|
|
flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
|
|
sFlpInterface = flp_device->get_flp_interface(flp_device);
|
|
|
|
if(sFlpInterface != NULL) {
|
|
sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
|
|
sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE)
|
|
);
|
|
|
|
sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
|
|
sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE)
|
|
);
|
|
|
|
sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
|
|
sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE)
|
|
);
|
|
}
|
|
|
|
if(sCallbacksObj == NULL) {
|
|
sCallbacksObj = env->NewGlobalRef(obj);
|
|
}
|
|
|
|
// initialize the Flp interfaces
|
|
if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
if(sFlpDiagnosticInterface != NULL) {
|
|
sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks);
|
|
}
|
|
|
|
if(sFlpGeofencingInterface != NULL) {
|
|
sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks);
|
|
}
|
|
|
|
// TODO: inject any device context if when needed
|
|
}
|
|
|
|
static jboolean IsSupported(JNIEnv* env, jclass clazz) {
|
|
return sFlpInterface != NULL;
|
|
}
|
|
|
|
static jint GetBatchSize(JNIEnv* env, jobject object) {
|
|
if(sFlpInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
return sFlpInterface->get_batch_size();
|
|
}
|
|
|
|
static void StartBatching(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jint id,
|
|
jobject optionsObject) {
|
|
if(sFlpInterface == NULL || optionsObject == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
FlpBatchOptions options;
|
|
TranslateFromObject(env, optionsObject, options);
|
|
int result = sFlpInterface->start_batching(id, &options);
|
|
ThrowOnError(env, result, __FUNCTION__);
|
|
}
|
|
|
|
static void UpdateBatchingOptions(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jint id,
|
|
jobject optionsObject) {
|
|
if(sFlpInterface == NULL || optionsObject == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
FlpBatchOptions options;
|
|
TranslateFromObject(env, optionsObject, options);
|
|
int result = sFlpInterface->update_batching_options(id, &options);
|
|
ThrowOnError(env, result, __FUNCTION__);
|
|
}
|
|
|
|
static void StopBatching(JNIEnv* env, jobject object, jint id) {
|
|
if(sFlpInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpInterface->stop_batching(id);
|
|
}
|
|
|
|
static void Cleanup(JNIEnv* env, jobject object) {
|
|
if(sFlpInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpInterface->cleanup();
|
|
|
|
if(sCallbacksObj != NULL) {
|
|
env->DeleteGlobalRef(sCallbacksObj);
|
|
sCallbacksObj = NULL;
|
|
}
|
|
|
|
sFlpInterface = NULL;
|
|
sFlpDiagnosticInterface = NULL;
|
|
sFlpDeviceContextInterface = NULL;
|
|
sFlpGeofencingInterface = NULL;
|
|
|
|
if(sHardwareDevice != NULL) {
|
|
sHardwareDevice->close(sHardwareDevice);
|
|
sHardwareDevice = NULL;
|
|
}
|
|
}
|
|
|
|
static void GetBatchedLocation(JNIEnv* env, jobject object, jint lastNLocations) {
|
|
if(sFlpInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpInterface->get_batched_location(lastNLocations);
|
|
}
|
|
|
|
static void InjectLocation(JNIEnv* env, jobject object, jobject locationObject) {
|
|
if(locationObject == NULL) {
|
|
ALOGE("Invalid location for injection: %p", locationObject);
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
if(sFlpInterface == NULL) {
|
|
// there is no listener, bail
|
|
return;
|
|
}
|
|
|
|
FlpLocation location;
|
|
TranslateFromObject(env, locationObject, location);
|
|
int result = sFlpInterface->inject_location(&location);
|
|
if (result != FLP_RESULT_SUCCESS) {
|
|
// do not throw but log, this operation should be fire and forget
|
|
ALOGE("Error %d in '%s'", result, __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
static jboolean IsDiagnosticSupported() {
|
|
return sFlpDiagnosticInterface != NULL;
|
|
}
|
|
|
|
static void InjectDiagnosticData(JNIEnv* env, jobject object, jstring stringData) {
|
|
if(stringData == NULL) {
|
|
ALOGE("Invalid diagnostic data for injection: %p", stringData);
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
if(sFlpDiagnosticInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
int length = env->GetStringLength(stringData);
|
|
const jchar* data = env->GetStringChars(stringData, /* isCopy */ NULL);
|
|
if(data == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
int result = sFlpDiagnosticInterface->inject_data((char*) data, length);
|
|
ThrowOnError(env, result, __FUNCTION__);
|
|
}
|
|
|
|
static jboolean IsDeviceContextSupported() {
|
|
return sFlpDeviceContextInterface != NULL;
|
|
}
|
|
|
|
static void InjectDeviceContext(JNIEnv* env, jobject object, jint enabledMask) {
|
|
if(sFlpDeviceContextInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
int result = sFlpDeviceContextInterface->inject_device_context(enabledMask);
|
|
ThrowOnError(env, result, __FUNCTION__);
|
|
}
|
|
|
|
static jboolean IsGeofencingSupported() {
|
|
return sFlpGeofencingInterface != NULL;
|
|
}
|
|
|
|
static void AddGeofences(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jobjectArray geofenceRequestsArray) {
|
|
if(geofenceRequestsArray == NULL) {
|
|
ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray);
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
if (sFlpGeofencingInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray);
|
|
if(geofenceRequestsCount == 0) {
|
|
return;
|
|
}
|
|
|
|
Geofence* geofences = new Geofence[geofenceRequestsCount];
|
|
if (geofences == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__);
|
|
}
|
|
|
|
for (int i = 0; i < geofenceRequestsCount; ++i) {
|
|
geofences[i].data = new GeofenceData();
|
|
geofences[i].options = new GeofenceOptions();
|
|
jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
|
|
|
|
TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
|
|
env->DeleteLocalRef(geofenceObject);
|
|
}
|
|
|
|
sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
|
|
if (geofences != NULL) {
|
|
for(int i = 0; i < geofenceRequestsCount; ++i) {
|
|
delete geofences[i].data;
|
|
delete geofences[i].options;
|
|
}
|
|
delete[] geofences;
|
|
}
|
|
}
|
|
|
|
static void PauseGeofence(JNIEnv* env, jobject object, jint geofenceId) {
|
|
if(sFlpGeofencingInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpGeofencingInterface->pause_geofence(geofenceId);
|
|
}
|
|
|
|
static void ResumeGeofence(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jint geofenceId,
|
|
jint monitorTransitions) {
|
|
if(sFlpGeofencingInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpGeofencingInterface->resume_geofence(geofenceId, monitorTransitions);
|
|
}
|
|
|
|
static void ModifyGeofenceOption(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jint geofenceId,
|
|
jint lastTransition,
|
|
jint monitorTransitions,
|
|
jint notificationResponsiveness,
|
|
jint unknownTimer,
|
|
jint sourcesToUse) {
|
|
if(sFlpGeofencingInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
GeofenceOptions options = {
|
|
lastTransition,
|
|
monitorTransitions,
|
|
notificationResponsiveness,
|
|
unknownTimer,
|
|
(uint32_t)sourcesToUse
|
|
};
|
|
|
|
sFlpGeofencingInterface->modify_geofence_option(geofenceId, &options);
|
|
}
|
|
|
|
static void RemoveGeofences(
|
|
JNIEnv* env,
|
|
jobject object,
|
|
jintArray geofenceIdsArray) {
|
|
if(sFlpGeofencingInterface == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
jsize geofenceIdsCount = env->GetArrayLength(geofenceIdsArray);
|
|
jint* geofenceIds = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
|
|
if(geofenceIds == NULL) {
|
|
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
|
|
}
|
|
|
|
sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds);
|
|
env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
|
|
}
|
|
|
|
static JNINativeMethod sMethods[] = {
|
|
//{"name", "signature", functionPointer }
|
|
{"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
|
|
{"nativeInit", "()V", reinterpret_cast<void*>(Init)},
|
|
{"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
|
|
{"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
|
|
{"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
|
|
{"nativeStartBatching",
|
|
"(ILandroid/location/FusedBatchOptions;)V",
|
|
reinterpret_cast<void*>(StartBatching)},
|
|
{"nativeUpdateBatchingOptions",
|
|
"(ILandroid/location/FusedBatchOptions;)V",
|
|
reinterpret_cast<void*>(UpdateBatchingOptions)},
|
|
{"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
|
|
{"nativeRequestBatchedLocation",
|
|
"(I)V",
|
|
reinterpret_cast<void*>(GetBatchedLocation)},
|
|
{"nativeInjectLocation",
|
|
"(Landroid/location/Location;)V",
|
|
reinterpret_cast<void*>(InjectLocation)},
|
|
{"nativeIsDiagnosticSupported",
|
|
"()Z",
|
|
reinterpret_cast<void*>(IsDiagnosticSupported)},
|
|
{"nativeInjectDiagnosticData",
|
|
"(Ljava/lang/String;)V",
|
|
reinterpret_cast<void*>(InjectDiagnosticData)},
|
|
{"nativeIsDeviceContextSupported",
|
|
"()Z",
|
|
reinterpret_cast<void*>(IsDeviceContextSupported)},
|
|
{"nativeInjectDeviceContext",
|
|
"(I)V",
|
|
reinterpret_cast<void*>(InjectDeviceContext)},
|
|
{"nativeIsGeofencingSupported",
|
|
"()Z",
|
|
reinterpret_cast<void*>(IsGeofencingSupported)},
|
|
{"nativeAddGeofences",
|
|
"([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
|
|
reinterpret_cast<void*>(AddGeofences)},
|
|
{"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
|
|
{"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
|
|
{"nativeModifyGeofenceOption",
|
|
"(IIIIII)V",
|
|
reinterpret_cast<void*>(ModifyGeofenceOption)},
|
|
{"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
|
|
};
|
|
|
|
/*
|
|
* Registration method invoked on JNI Load.
|
|
*/
|
|
int register_android_server_location_FlpHardwareProvider(JNIEnv* env) {
|
|
return jniRegisterNativeMethods(
|
|
env,
|
|
"com/android/server/location/FlpHardwareProvider",
|
|
sMethods,
|
|
NELEM(sMethods)
|
|
);
|
|
}
|
|
|
|
} /* name-space Android */
|