summaryrefslogtreecommitdiffstats
path: root/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/jni/com_android_server_location_FlpHardwareProvider.cpp')
-rw-r--r--services/core/jni/com_android_server_location_FlpHardwareProvider.cpp1002
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 */