diff options
Diffstat (limited to 'services/core/jni/com_android_server_location_FlpHardwareProvider.cpp')
-rw-r--r-- | services/core/jni/com_android_server_location_FlpHardwareProvider.cpp | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp new file mode 100644 index 0000000..6c14887 --- /dev/null +++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp @@ -0,0 +1,1002 @@ +/* + * 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 */ |