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