1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the license at
7 *
8 *      http://www.apache.org/license/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the license.
15 */
16
17#define LOG_TAG "FlpHardwareProvider"
18#define LOG_NDEBUG  0
19
20#define WAKE_LOCK_NAME  "FLP"
21#define LOCATION_CLASS_NAME "android/location/Location"
22
23#include "jni.h"
24#include "JNIHelp.h"
25#include "android_runtime/AndroidRuntime.h"
26#include "android_runtime/Log.h"
27#include "hardware/fused_location.h"
28#include "hardware_legacy/power.h"
29
30static jobject sCallbacksObj = NULL;
31static JNIEnv *sCallbackEnv = NULL;
32static hw_device_t* sHardwareDevice = NULL;
33
34static jmethodID sSetVersion = NULL;
35static jmethodID sOnLocationReport = NULL;
36static jmethodID sOnDataReport = NULL;
37static jmethodID sOnBatchingCapabilities = NULL;
38static jmethodID sOnBatchingStatus = NULL;
39static jmethodID sOnGeofenceTransition = NULL;
40static jmethodID sOnGeofenceMonitorStatus = NULL;
41static jmethodID sOnGeofenceAdd = NULL;
42static jmethodID sOnGeofenceRemove = NULL;
43static jmethodID sOnGeofencePause = NULL;
44static jmethodID sOnGeofenceResume = NULL;
45static jmethodID sOnGeofencingCapabilities = NULL;
46
47static const FlpLocationInterface* sFlpInterface = NULL;
48static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL;
49static const FlpGeofencingInterface* sFlpGeofencingInterface = NULL;
50static const FlpDeviceContextInterface* sFlpDeviceContextInterface = NULL;
51
52namespace android {
53
54static inline void CheckExceptions(JNIEnv* env, const char* methodName) {
55  if(!env->ExceptionCheck()) {
56    return;
57  }
58
59  ALOGE("An exception was thrown by '%s'.", methodName);
60  LOGE_EX(env);
61  env->ExceptionClear();
62}
63
64static inline void ThrowOnError(
65    JNIEnv* env,
66    int resultCode,
67    const char* methodName) {
68  if(resultCode == FLP_RESULT_SUCCESS) {
69    return;
70  }
71
72  ALOGE("Error %d in '%s'", resultCode, methodName);
73  // TODO: this layer needs to be refactored to return error codes to Java
74  // raising a FatalError is harsh, and because FLP Hardware Provider is loaded inside the system
75  // service, it can cause the device to reboot, or remain in a reboot loop
76  // a simple exception is still dumped to logcat, but it is handled more gracefully
77  jclass exceptionClass = env->FindClass("java/lang/RuntimeException");
78  env->ThrowNew(exceptionClass, methodName);
79}
80
81static bool IsValidCallbackThread() {
82  JNIEnv* env = AndroidRuntime::getJNIEnv();
83
84  if(sCallbackEnv == NULL || sCallbackEnv != env) {
85    ALOGE("CallbackThread check fail: env=%p, expected=%p", env, sCallbackEnv);
86    return false;
87  }
88
89  return true;
90}
91
92static void BatchingCapabilitiesCallback(int32_t capabilities) {
93  if(!IsValidCallbackThread()) {
94    return;
95  }
96
97  sCallbackEnv->CallVoidMethod(
98      sCallbacksObj,
99      sOnBatchingCapabilities,
100      capabilities
101      );
102  CheckExceptions(sCallbackEnv, __FUNCTION__);
103}
104
105static void BatchingStatusCallback(int32_t status) {
106  if(!IsValidCallbackThread()) {
107    return;
108  }
109
110  sCallbackEnv->CallVoidMethod(
111      sCallbacksObj,
112      sOnBatchingStatus,
113      status
114      );
115  CheckExceptions(sCallbackEnv, __FUNCTION__);
116}
117
118static int SetThreadEvent(ThreadEvent event) {
119  JavaVM* javaVm = AndroidRuntime::getJavaVM();
120
121  switch(event) {
122    case ASSOCIATE_JVM:
123    {
124      if(sCallbackEnv != NULL) {
125        ALOGE(
126            "Attempted to associate callback in '%s'. Callback already associated.",
127            __FUNCTION__
128            );
129        return FLP_RESULT_ERROR;
130      }
131
132      JavaVMAttachArgs args = {
133          JNI_VERSION_1_6,
134          "FLP Service Callback Thread",
135          /* group */ NULL
136      };
137
138      jint attachResult = javaVm->AttachCurrentThread(&sCallbackEnv, &args);
139      if (attachResult != 0) {
140        ALOGE("Callback thread attachment error: %d", attachResult);
141        return FLP_RESULT_ERROR;
142      }
143
144      ALOGV("Callback thread attached: %p", sCallbackEnv);
145
146      // Send the version to the upper layer.
147      sCallbackEnv->CallVoidMethod(
148            sCallbacksObj,
149            sSetVersion,
150            sFlpInterface->size == sizeof(FlpLocationInterface) ? 2 : 1
151            );
152      CheckExceptions(sCallbackEnv, __FUNCTION__);
153      break;
154    }
155    case DISASSOCIATE_JVM:
156    {
157      if (!IsValidCallbackThread()) {
158        ALOGE(
159            "Attempted to dissasociate an unnownk callback thread : '%s'.",
160            __FUNCTION__
161            );
162        return FLP_RESULT_ERROR;
163      }
164
165      if (javaVm->DetachCurrentThread() != 0) {
166        return FLP_RESULT_ERROR;
167      }
168
169      sCallbackEnv = NULL;
170      break;
171    }
172    default:
173      ALOGE("Invalid ThreadEvent request %d", event);
174      return FLP_RESULT_ERROR;
175  }
176
177  return FLP_RESULT_SUCCESS;
178}
179
180/*
181 * Initializes the FlpHardwareProvider class from the native side by opening
182 * the HW module and obtaining the proper interfaces.
183 */
184static void ClassInit(JNIEnv* env, jclass clazz) {
185  sFlpInterface = NULL;
186
187  // get references to the Java provider methods
188  sSetVersion = env->GetMethodID(
189        clazz,
190        "setVersion",
191        "(I)V");
192  sOnLocationReport = env->GetMethodID(
193      clazz,
194      "onLocationReport",
195      "([Landroid/location/Location;)V");
196  sOnDataReport = env->GetMethodID(
197      clazz,
198      "onDataReport",
199      "(Ljava/lang/String;)V"
200      );
201    sOnBatchingCapabilities = env->GetMethodID(
202        clazz,
203        "onBatchingCapabilities",
204        "(I)V");
205    sOnBatchingStatus = env->GetMethodID(
206            clazz,
207            "onBatchingStatus",
208            "(I)V");
209    sOnGeofencingCapabilities = env->GetMethodID(
210            clazz,
211            "onGeofencingCapabilities",
212            "(I)V");
213  sOnGeofenceTransition = env->GetMethodID(
214      clazz,
215      "onGeofenceTransition",
216      "(ILandroid/location/Location;IJI)V"
217      );
218  sOnGeofenceMonitorStatus = env->GetMethodID(
219      clazz,
220      "onGeofenceMonitorStatus",
221      "(IILandroid/location/Location;)V"
222      );
223  sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V");
224  sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
225  sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
226  sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
227
228  // open the hardware module
229  const hw_module_t* module = NULL;
230  int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
231  if (err != 0) {
232    ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
233    return;
234  }
235
236  err = module->methods->open(
237      module,
238      FUSED_LOCATION_HARDWARE_MODULE_ID,
239      &sHardwareDevice);
240  if (err != 0) {
241    ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
242    return;
243  }
244
245  // acquire the interfaces pointers
246  flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
247  sFlpInterface = flp_device->get_flp_interface(flp_device);
248
249  if (sFlpInterface != NULL) {
250    sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
251        sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
252
253    sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
254        sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
255
256    sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
257        sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
258  }
259}
260
261/*
262 * Helper function to unwrap a java object back into a FlpLocation structure.
263 */
264static void TranslateFromObject(
265    JNIEnv* env,
266    jobject locationObject,
267    FlpLocation& location) {
268  location.size = sizeof(FlpLocation);
269  location.flags = 0;
270
271  jclass locationClass = env->GetObjectClass(locationObject);
272
273  jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D");
274  location.latitude = env->CallDoubleMethod(locationObject, getLatitude);
275  jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D");
276  location.longitude = env->CallDoubleMethod(locationObject, getLongitude);
277  jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J");
278  location.timestamp = env->CallLongMethod(locationObject, getTime);
279  location.flags |= FLP_LOCATION_HAS_LAT_LONG;
280
281  jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z");
282  if (env->CallBooleanMethod(locationObject, hasAltitude)) {
283    jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D");
284    location.altitude = env->CallDoubleMethod(locationObject, getAltitude);
285    location.flags |= FLP_LOCATION_HAS_ALTITUDE;
286  }
287
288  jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z");
289  if (env->CallBooleanMethod(locationObject, hasSpeed)) {
290    jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F");
291    location.speed = env->CallFloatMethod(locationObject, getSpeed);
292    location.flags |= FLP_LOCATION_HAS_SPEED;
293  }
294
295  jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z");
296  if (env->CallBooleanMethod(locationObject, hasBearing)) {
297    jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F");
298    location.bearing = env->CallFloatMethod(locationObject, getBearing);
299    location.flags |= FLP_LOCATION_HAS_BEARING;
300  }
301
302  jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z");
303  if (env->CallBooleanMethod(locationObject, hasAccuracy)) {
304    jmethodID getAccuracy = env->GetMethodID(
305        locationClass,
306        "getAccuracy",
307        "()F"
308        );
309    location.accuracy = env->CallFloatMethod(locationObject, getAccuracy);
310    location.flags |= FLP_LOCATION_HAS_ACCURACY;
311  }
312
313  // TODO: wire sources_used if Location class exposes them
314
315  env->DeleteLocalRef(locationClass);
316}
317
318/*
319 * Helper function to unwrap FlpBatchOptions from the Java Runtime calls.
320 */
321static void TranslateFromObject(
322    JNIEnv* env,
323    jobject batchOptionsObject,
324    FlpBatchOptions& batchOptions) {
325  jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject);
326
327  jmethodID getMaxPower = env->GetMethodID(
328      batchOptionsClass,
329      "getMaxPowerAllocationInMW",
330      "()D"
331      );
332  batchOptions.max_power_allocation_mW = env->CallDoubleMethod(
333      batchOptionsObject,
334      getMaxPower
335      );
336
337  jmethodID getPeriod = env->GetMethodID(
338      batchOptionsClass,
339      "getPeriodInNS",
340      "()J"
341      );
342  batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod);
343
344  jmethodID getSourcesToUse = env->GetMethodID(
345      batchOptionsClass,
346      "getSourcesToUse",
347      "()I"
348      );
349  batchOptions.sources_to_use = env->CallIntMethod(
350      batchOptionsObject,
351      getSourcesToUse
352      );
353
354  jmethodID getSmallestDisplacementMeters = env->GetMethodID(
355      batchOptionsClass,
356      "getSmallestDisplacementMeters",
357      "()F"
358      );
359  batchOptions.smallest_displacement_meters
360      = env->CallFloatMethod(batchOptionsObject, getSmallestDisplacementMeters);
361
362  jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
363  batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
364
365  env->DeleteLocalRef(batchOptionsClass);
366}
367
368/*
369 * Helper function to unwrap Geofence structures from the Java Runtime calls.
370 */
371static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
372    JNIEnv* env,
373    jobject geofenceRequestObject,
374    Geofence& geofence) {
375  jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
376
377  jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
378  geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
379
380  jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
381  // this works because GeofenceHardwareRequest.java and fused_location.h have
382  // the same notion of geofence types
383  GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
384  if(type != TYPE_CIRCLE) {
385    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
386  }
387  geofence.data->type = type;
388  GeofenceCircle& circle = geofence.data->geofence.circle;
389
390  jmethodID getLatitude = env->GetMethodID(
391      geofenceRequestClass,
392      "getLatitude",
393      "()D");
394  circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
395
396  jmethodID getLongitude = env->GetMethodID(
397      geofenceRequestClass,
398      "getLongitude",
399      "()D");
400  circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
401
402  jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
403  circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
404
405  GeofenceOptions* options = geofence.options;
406  jmethodID getMonitorTransitions = env->GetMethodID(
407      geofenceRequestClass,
408      "getMonitorTransitions",
409      "()I");
410  options->monitor_transitions = env->CallIntMethod(
411      geofenceRequestObject,
412      getMonitorTransitions);
413
414  jmethodID getUnknownTimer = env->GetMethodID(
415      geofenceRequestClass,
416      "getUnknownTimer",
417      "()I");
418  options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
419
420  jmethodID getNotificationResponsiveness = env->GetMethodID(
421      geofenceRequestClass,
422      "getNotificationResponsiveness",
423      "()I");
424  options->notification_responsivenes_ms = env->CallIntMethod(
425      geofenceRequestObject,
426      getNotificationResponsiveness);
427
428  jmethodID getLastTransition = env->GetMethodID(
429      geofenceRequestClass,
430      "getLastTransition",
431      "()I");
432  options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
433
434  jmethodID getSourceTechnologies =
435      env->GetMethodID(geofenceRequestClass, "getSourceTechnologies", "()I");
436  options->sources_to_use = env->CallIntMethod(geofenceRequestObject, getSourceTechnologies);
437
438  env->DeleteLocalRef(geofenceRequestClass);
439}
440
441/*
442 * Helper function to transform FlpLocation into a java object.
443 */
444static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
445  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
446  jmethodID locationCtor = sCallbackEnv->GetMethodID(
447      locationClass,
448      "<init>",
449      "(Ljava/lang/String;)V"
450      );
451
452  // the provider is set in the upper JVM layer
453  locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL);
454  jint flags = location->flags;
455
456  // set the valid information in the object
457  if (flags & FLP_LOCATION_HAS_LAT_LONG) {
458    jmethodID setLatitude = sCallbackEnv->GetMethodID(
459        locationClass,
460        "setLatitude",
461        "(D)V"
462        );
463    sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude);
464
465    jmethodID setLongitude = sCallbackEnv->GetMethodID(
466        locationClass,
467        "setLongitude",
468        "(D)V"
469        );
470    sCallbackEnv->CallVoidMethod(
471        locationObject,
472        setLongitude,
473        location->longitude
474        );
475
476    jmethodID setTime = sCallbackEnv->GetMethodID(
477        locationClass,
478        "setTime",
479        "(J)V"
480        );
481    sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp);
482  }
483
484  if (flags & FLP_LOCATION_HAS_ALTITUDE) {
485    jmethodID setAltitude = sCallbackEnv->GetMethodID(
486        locationClass,
487        "setAltitude",
488        "(D)V"
489        );
490    sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude);
491  }
492
493  if (flags & FLP_LOCATION_HAS_SPEED) {
494    jmethodID setSpeed = sCallbackEnv->GetMethodID(
495        locationClass,
496        "setSpeed",
497        "(F)V"
498        );
499    sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed);
500  }
501
502  if (flags & FLP_LOCATION_HAS_BEARING) {
503    jmethodID setBearing = sCallbackEnv->GetMethodID(
504        locationClass,
505        "setBearing",
506        "(F)V"
507        );
508    sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing);
509  }
510
511  if (flags & FLP_LOCATION_HAS_ACCURACY) {
512    jmethodID setAccuracy = sCallbackEnv->GetMethodID(
513        locationClass,
514        "setAccuracy",
515        "(F)V"
516        );
517    sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy);
518  }
519
520  // TODO: wire FlpLocation::sources_used when needed
521
522  sCallbackEnv->DeleteLocalRef(locationClass);
523}
524
525/*
526 * Helper function to serialize FlpLocation structures.
527 */
528static void TranslateToObjectArray(
529    int32_t locationsCount,
530    FlpLocation** locations,
531    jobjectArray& locationsArray) {
532  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
533  locationsArray = sCallbackEnv->NewObjectArray(
534      locationsCount,
535      locationClass,
536      /* initialElement */ NULL
537      );
538
539  for (int i = 0; i < locationsCount; ++i) {
540    jobject locationObject = NULL;
541    TranslateToObject(locations[i], locationObject);
542    sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
543    sCallbackEnv->DeleteLocalRef(locationObject);
544  }
545
546  sCallbackEnv->DeleteLocalRef(locationClass);
547}
548
549static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
550  if(!IsValidCallbackThread()) {
551    return;
552  }
553
554  if(locationsCount == 0 || locations == NULL) {
555    ALOGE(
556        "Invalid LocationCallback. Count: %d, Locations: %p",
557        locationsCount,
558        locations
559        );
560    return;
561  }
562
563  jobjectArray locationsArray = NULL;
564  TranslateToObjectArray(locationsCount, locations, locationsArray);
565
566  sCallbackEnv->CallVoidMethod(
567      sCallbacksObj,
568      sOnLocationReport,
569      locationsArray
570      );
571  CheckExceptions(sCallbackEnv, __FUNCTION__);
572
573  if(locationsArray != NULL) {
574    sCallbackEnv->DeleteLocalRef(locationsArray);
575  }
576}
577
578static void AcquireWakelock() {
579  acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
580}
581
582static void ReleaseWakelock() {
583  release_wake_lock(WAKE_LOCK_NAME);
584}
585
586FlpCallbacks sFlpCallbacks = {
587  sizeof(FlpCallbacks),
588  LocationCallback,
589  AcquireWakelock,
590  ReleaseWakelock,
591  SetThreadEvent,
592  BatchingCapabilitiesCallback,
593  BatchingStatusCallback
594};
595
596static void ReportData(char* data, int length) {
597  jstring stringData = NULL;
598
599  if(length != 0 && data != NULL) {
600    stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length);
601  } else {
602    ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data);
603    return;
604  }
605
606  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData);
607  CheckExceptions(sCallbackEnv, __FUNCTION__);
608}
609
610FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = {
611  sizeof(FlpDiagnosticCallbacks),
612  SetThreadEvent,
613  ReportData
614};
615
616static void GeofenceTransitionCallback(
617    int32_t geofenceId,
618    FlpLocation* location,
619    int32_t transition,
620    FlpUtcTime timestamp,
621    uint32_t sourcesUsed
622    ) {
623  if(!IsValidCallbackThread()) {
624    return;
625  }
626
627  if(location == NULL) {
628    ALOGE("GeofenceTransition received with invalid location: %p", location);
629    return;
630  }
631
632  jobject locationObject = NULL;
633  TranslateToObject(location, locationObject);
634
635  sCallbackEnv->CallVoidMethod(
636      sCallbacksObj,
637      sOnGeofenceTransition,
638      geofenceId,
639      locationObject,
640      transition,
641      timestamp,
642      sourcesUsed
643      );
644  CheckExceptions(sCallbackEnv, __FUNCTION__);
645
646  if(locationObject != NULL) {
647    sCallbackEnv->DeleteLocalRef(locationObject);
648  }
649}
650
651static void GeofenceMonitorStatusCallback(
652    int32_t status,
653    uint32_t source,
654    FlpLocation* lastLocation) {
655  if(!IsValidCallbackThread()) {
656    return;
657  }
658
659  jobject locationObject = NULL;
660  if(lastLocation != NULL) {
661    TranslateToObject(lastLocation, locationObject);
662  }
663
664  sCallbackEnv->CallVoidMethod(
665      sCallbacksObj,
666      sOnGeofenceMonitorStatus,
667      status,
668      source,
669      locationObject
670      );
671  CheckExceptions(sCallbackEnv, __FUNCTION__);
672
673  if(locationObject != NULL) {
674    sCallbackEnv->DeleteLocalRef(locationObject);
675  }
676}
677
678static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
679  if(!IsValidCallbackThread()) {
680    return;
681  }
682
683  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result);
684  CheckExceptions(sCallbackEnv, __FUNCTION__);
685}
686
687static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) {
688  if(!IsValidCallbackThread()) {
689    return;
690  }
691
692  sCallbackEnv->CallVoidMethod(
693      sCallbacksObj,
694      sOnGeofenceRemove,
695      geofenceId,
696      result
697      );
698  CheckExceptions(sCallbackEnv, __FUNCTION__);
699}
700
701static void GeofencePauseCallback(int32_t geofenceId, int32_t result) {
702  if(!IsValidCallbackThread()) {
703    return;
704  }
705
706  sCallbackEnv->CallVoidMethod(
707      sCallbacksObj,
708      sOnGeofencePause,
709      geofenceId,
710      result
711      );
712  CheckExceptions(sCallbackEnv, __FUNCTION__);
713}
714
715static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) {
716  if(!IsValidCallbackThread()) {
717    return;
718  }
719
720  sCallbackEnv->CallVoidMethod(
721      sCallbacksObj,
722      sOnGeofenceResume,
723      geofenceId,
724      result
725      );
726  CheckExceptions(sCallbackEnv, __FUNCTION__);
727}
728
729static void GeofencingCapabilitiesCallback(int32_t capabilities) {
730  if(!IsValidCallbackThread()) {
731    return;
732  }
733
734  sCallbackEnv->CallVoidMethod(
735      sCallbacksObj,
736      sOnGeofencingCapabilities,
737      capabilities
738      );
739  CheckExceptions(sCallbackEnv, __FUNCTION__);
740}
741
742FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
743  sizeof(FlpGeofenceCallbacks),
744  GeofenceTransitionCallback,
745  GeofenceMonitorStatusCallback,
746  GeofenceAddCallback,
747  GeofenceRemoveCallback,
748  GeofencePauseCallback,
749  GeofenceResumeCallback,
750  SetThreadEvent,
751  GeofencingCapabilitiesCallback
752};
753
754/*
755 * Initializes the Fused Location Provider in the native side. It ensures that
756 * the Flp interfaces are initialized properly.
757 */
758static void Init(JNIEnv* env, jobject obj) {
759  if(sCallbacksObj == NULL) {
760    sCallbacksObj = env->NewGlobalRef(obj);
761  }
762
763  // initialize the Flp interfaces
764  if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) {
765    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
766  }
767
768  if(sFlpDiagnosticInterface != NULL) {
769    sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks);
770  }
771
772  if(sFlpGeofencingInterface != NULL) {
773    sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks);
774  }
775
776  // TODO: inject any device context if when needed
777}
778
779static jboolean IsSupported(JNIEnv* /* env */, jclass /* clazz */) {
780  if (sFlpInterface == NULL) {
781    return JNI_FALSE;
782  }
783  return JNI_TRUE;
784}
785
786static jint GetBatchSize(JNIEnv* env, jobject /* object */) {
787  if(sFlpInterface == NULL) {
788    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
789  }
790
791  return sFlpInterface->get_batch_size();
792}
793
794static void StartBatching(
795    JNIEnv* env,
796    jobject /* object */,
797    jint id,
798    jobject optionsObject) {
799  if(sFlpInterface == NULL || optionsObject == NULL) {
800    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
801  }
802
803  FlpBatchOptions options;
804  TranslateFromObject(env, optionsObject, options);
805  int result = sFlpInterface->start_batching(id, &options);
806  ThrowOnError(env, result, __FUNCTION__);
807}
808
809static void UpdateBatchingOptions(
810    JNIEnv* env,
811    jobject /* object */,
812    jint id,
813    jobject optionsObject) {
814  if(sFlpInterface == NULL || optionsObject == NULL) {
815    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
816  }
817
818  FlpBatchOptions options;
819  TranslateFromObject(env, optionsObject, options);
820  int result = sFlpInterface->update_batching_options(id, &options);
821  ThrowOnError(env, result, __FUNCTION__);
822}
823
824static void StopBatching(JNIEnv* env, jobject /* object */, jint id) {
825  if(sFlpInterface == NULL) {
826    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
827  }
828
829  sFlpInterface->stop_batching(id);
830}
831
832static void Cleanup(JNIEnv* env, jobject /* object */) {
833  if(sFlpInterface == NULL) {
834    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
835  }
836
837  sFlpInterface->cleanup();
838
839  if(sCallbacksObj != NULL) {
840    env->DeleteGlobalRef(sCallbacksObj);
841    sCallbacksObj = NULL;
842  }
843
844  sFlpInterface = NULL;
845  sFlpDiagnosticInterface = NULL;
846  sFlpDeviceContextInterface = NULL;
847  sFlpGeofencingInterface = NULL;
848
849  if(sHardwareDevice != NULL) {
850    sHardwareDevice->close(sHardwareDevice);
851    sHardwareDevice = NULL;
852  }
853}
854
855static void GetBatchedLocation(JNIEnv* env, jobject /* object */, jint lastNLocations) {
856  if(sFlpInterface == NULL) {
857    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
858  }
859
860  sFlpInterface->get_batched_location(lastNLocations);
861}
862
863static void FlushBatchedLocations(JNIEnv* env, jobject /* object */) {
864  if(sFlpInterface == NULL) {
865    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
866  }
867
868  sFlpInterface->flush_batched_locations();
869}
870
871static void InjectLocation(JNIEnv* env, jobject /* object */, jobject locationObject) {
872  if(locationObject == NULL) {
873    ALOGE("Invalid location for injection: %p", locationObject);
874    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
875  }
876
877  if(sFlpInterface == NULL) {
878    // there is no listener, bail
879    return;
880  }
881
882  FlpLocation location;
883  TranslateFromObject(env, locationObject, location);
884  int result = sFlpInterface->inject_location(&location);
885  if (result != FLP_RESULT_SUCCESS) {
886    // do not throw but log, this operation should be fire and forget
887    ALOGE("Error %d in '%s'", result, __FUNCTION__);
888  }
889}
890
891static jboolean IsDiagnosticSupported() {
892  return sFlpDiagnosticInterface != NULL;
893}
894
895static void InjectDiagnosticData(JNIEnv* env, jobject /* object */, jstring stringData) {
896  if(stringData == NULL) {
897    ALOGE("Invalid diagnostic data for injection: %p", stringData);
898    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
899  }
900
901  if(sFlpDiagnosticInterface == NULL) {
902    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
903  }
904
905  int length = env->GetStringLength(stringData);
906  const jchar* data = env->GetStringChars(stringData, /* isCopy */ NULL);
907  if(data == NULL) {
908    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
909  }
910
911  int result = sFlpDiagnosticInterface->inject_data((char*) data, length);
912  ThrowOnError(env, result, __FUNCTION__);
913}
914
915static jboolean IsDeviceContextSupported() {
916  return sFlpDeviceContextInterface != NULL;
917}
918
919static void InjectDeviceContext(JNIEnv* env, jobject /* object */, jint enabledMask) {
920  if(sFlpDeviceContextInterface == NULL) {
921    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
922  }
923
924  int result = sFlpDeviceContextInterface->inject_device_context(enabledMask);
925  ThrowOnError(env, result, __FUNCTION__);
926}
927
928static jboolean IsGeofencingSupported() {
929  return sFlpGeofencingInterface != NULL;
930}
931
932static void AddGeofences(
933    JNIEnv* env,
934    jobject /* object */,
935    jobjectArray geofenceRequestsArray) {
936  if(geofenceRequestsArray == NULL) {
937    ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray);
938    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
939  }
940
941  if (sFlpGeofencingInterface == NULL) {
942    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
943  }
944
945  jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray);
946  if(geofenceRequestsCount == 0) {
947    return;
948  }
949
950  Geofence* geofences = new Geofence[geofenceRequestsCount];
951  if (geofences == NULL) {
952    ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__);
953  }
954
955  for (int i = 0; i < geofenceRequestsCount; ++i) {
956    geofences[i].data = new GeofenceData();
957    geofences[i].options = new GeofenceOptions();
958    jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
959
960    TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
961    env->DeleteLocalRef(geofenceObject);
962  }
963
964  sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
965  if (geofences != NULL) {
966    for(int i = 0; i < geofenceRequestsCount; ++i) {
967      delete geofences[i].data;
968      delete geofences[i].options;
969    }
970    delete[] geofences;
971  }
972}
973
974static void PauseGeofence(JNIEnv* env, jobject /* object */, jint geofenceId) {
975  if(sFlpGeofencingInterface == NULL) {
976    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
977  }
978
979  sFlpGeofencingInterface->pause_geofence(geofenceId);
980}
981
982static void ResumeGeofence(
983    JNIEnv* env,
984    jobject /* object */,
985    jint geofenceId,
986    jint monitorTransitions) {
987  if(sFlpGeofencingInterface == NULL) {
988    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
989  }
990
991  sFlpGeofencingInterface->resume_geofence(geofenceId, monitorTransitions);
992}
993
994static void ModifyGeofenceOption(
995    JNIEnv* env,
996    jobject /* object */,
997    jint geofenceId,
998    jint lastTransition,
999    jint monitorTransitions,
1000    jint notificationResponsiveness,
1001    jint unknownTimer,
1002    jint sourcesToUse) {
1003  if(sFlpGeofencingInterface == NULL) {
1004    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
1005  }
1006
1007  GeofenceOptions options = {
1008      lastTransition,
1009      monitorTransitions,
1010      notificationResponsiveness,
1011      unknownTimer,
1012      (uint32_t)sourcesToUse
1013  };
1014
1015  sFlpGeofencingInterface->modify_geofence_option(geofenceId, &options);
1016}
1017
1018static void RemoveGeofences(
1019    JNIEnv* env,
1020    jobject /* object */,
1021    jintArray geofenceIdsArray) {
1022  if(sFlpGeofencingInterface == NULL) {
1023    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
1024  }
1025
1026  jsize geofenceIdsCount = env->GetArrayLength(geofenceIdsArray);
1027  jint* geofenceIds = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
1028  if(geofenceIds == NULL) {
1029    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
1030  }
1031
1032  sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds);
1033  env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
1034}
1035
1036static JNINativeMethod sMethods[] = {
1037  //{"name", "signature", functionPointer }
1038  {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
1039  {"nativeInit", "()V", reinterpret_cast<void*>(Init)},
1040  {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
1041  {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
1042  {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
1043  {"nativeStartBatching",
1044        "(ILandroid/location/FusedBatchOptions;)V",
1045        reinterpret_cast<void*>(StartBatching)},
1046  {"nativeUpdateBatchingOptions",
1047        "(ILandroid/location/FusedBatchOptions;)V",
1048        reinterpret_cast<void*>(UpdateBatchingOptions)},
1049  {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
1050  {"nativeRequestBatchedLocation",
1051        "(I)V",
1052        reinterpret_cast<void*>(GetBatchedLocation)},
1053  {"nativeFlushBatchedLocations",
1054          "()V",
1055          reinterpret_cast<void*>(FlushBatchedLocations)},
1056  {"nativeInjectLocation",
1057        "(Landroid/location/Location;)V",
1058        reinterpret_cast<void*>(InjectLocation)},
1059  {"nativeIsDiagnosticSupported",
1060        "()Z",
1061        reinterpret_cast<void*>(IsDiagnosticSupported)},
1062  {"nativeInjectDiagnosticData",
1063        "(Ljava/lang/String;)V",
1064        reinterpret_cast<void*>(InjectDiagnosticData)},
1065  {"nativeIsDeviceContextSupported",
1066        "()Z",
1067        reinterpret_cast<void*>(IsDeviceContextSupported)},
1068  {"nativeInjectDeviceContext",
1069        "(I)V",
1070        reinterpret_cast<void*>(InjectDeviceContext)},
1071  {"nativeIsGeofencingSupported",
1072        "()Z",
1073        reinterpret_cast<void*>(IsGeofencingSupported)},
1074  {"nativeAddGeofences",
1075        "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
1076        reinterpret_cast<void*>(AddGeofences)},
1077  {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
1078  {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
1079  {"nativeModifyGeofenceOption",
1080        "(IIIIII)V",
1081        reinterpret_cast<void*>(ModifyGeofenceOption)},
1082  {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
1083};
1084
1085/*
1086 * Registration method invoked on JNI Load.
1087 */
1088int register_android_server_location_FlpHardwareProvider(JNIEnv* env) {
1089  return jniRegisterNativeMethods(
1090      env,
1091      "com/android/server/location/FlpHardwareProvider",
1092      sMethods,
1093      NELEM(sMethods)
1094      );
1095}
1096
1097} /* name-space Android */
1098