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