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