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