com_android_server_location_FlpHardwareProvider.cpp revision 5ce66d8dcc786b3d6a0fdfb42074e6c9741590ef
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  sFlpInterface = NULL;
143
144  // get references to the Java provider methods
145  sOnLocationReport = env->GetMethodID(
146      clazz,
147      "onLocationReport",
148      "([Landroid/location/Location;)V");
149  sOnDataReport = env->GetMethodID(
150      clazz,
151      "onDataReport",
152      "(Ljava/lang/String;)V"
153      );
154  sOnGeofenceTransition = env->GetMethodID(
155      clazz,
156      "onGeofenceTransition",
157      "(ILandroid/location/Location;IJI)V"
158      );
159  sOnGeofenceMonitorStatus = env->GetMethodID(
160      clazz,
161      "onGeofenceMonitorStatus",
162      "(IILandroid/location/Location;)V"
163      );
164  sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V");
165  sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V");
166  sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V");
167  sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V");
168
169  // open the hardware module
170  const hw_module_t* module = NULL;
171  int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module);
172  if (err != 0) {
173    ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
174    return;
175  }
176
177  err = module->methods->open(
178      module,
179      FUSED_LOCATION_HARDWARE_MODULE_ID,
180      &sHardwareDevice);
181  if (err != 0) {
182    ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
183    return;
184  }
185
186  // acquire the interfaces pointers
187  flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice);
188  sFlpInterface = flp_device->get_flp_interface(flp_device);
189
190  if (sFlpInterface != NULL) {
191    sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>(
192        sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE));
193
194    sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>(
195        sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE));
196
197    sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>(
198        sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE));
199  }
200}
201
202/*
203 * Helper function to unwrap a java object back into a FlpLocation structure.
204 */
205static void TranslateFromObject(
206    JNIEnv* env,
207    jobject locationObject,
208    FlpLocation& location) {
209  location.size = sizeof(FlpLocation);
210  location.flags = 0;
211
212  jclass locationClass = env->GetObjectClass(locationObject);
213
214  jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D");
215  location.latitude = env->CallDoubleMethod(locationObject, getLatitude);
216  jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D");
217  location.longitude = env->CallDoubleMethod(locationObject, getLongitude);
218  jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J");
219  location.timestamp = env->CallLongMethod(locationObject, getTime);
220  location.flags |= FLP_LOCATION_HAS_LAT_LONG;
221
222  jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z");
223  if (env->CallBooleanMethod(locationObject, hasAltitude)) {
224    jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D");
225    location.altitude = env->CallDoubleMethod(locationObject, getAltitude);
226    location.flags |= FLP_LOCATION_HAS_ALTITUDE;
227  }
228
229  jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z");
230  if (env->CallBooleanMethod(locationObject, hasSpeed)) {
231    jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F");
232    location.speed = env->CallFloatMethod(locationObject, getSpeed);
233    location.flags |= FLP_LOCATION_HAS_SPEED;
234  }
235
236  jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z");
237  if (env->CallBooleanMethod(locationObject, hasBearing)) {
238    jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F");
239    location.bearing = env->CallFloatMethod(locationObject, getBearing);
240    location.flags |= FLP_LOCATION_HAS_BEARING;
241  }
242
243  jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z");
244  if (env->CallBooleanMethod(locationObject, hasAccuracy)) {
245    jmethodID getAccuracy = env->GetMethodID(
246        locationClass,
247        "getAccuracy",
248        "()F"
249        );
250    location.accuracy = env->CallFloatMethod(locationObject, getAccuracy);
251    location.flags |= FLP_LOCATION_HAS_ACCURACY;
252  }
253
254  // TODO: wire sources_used if Location class exposes them
255
256  env->DeleteLocalRef(locationClass);
257}
258
259/*
260 * Helper function to unwrap FlpBatchOptions from the Java Runtime calls.
261 */
262static void TranslateFromObject(
263    JNIEnv* env,
264    jobject batchOptionsObject,
265    FlpBatchOptions& batchOptions) {
266  jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject);
267
268  jmethodID getMaxPower = env->GetMethodID(
269      batchOptionsClass,
270      "getMaxPowerAllocationInMW",
271      "()D"
272      );
273  batchOptions.max_power_allocation_mW = env->CallDoubleMethod(
274      batchOptionsObject,
275      getMaxPower
276      );
277
278  jmethodID getPeriod = env->GetMethodID(
279      batchOptionsClass,
280      "getPeriodInNS",
281      "()J"
282      );
283  batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod);
284
285  jmethodID getSourcesToUse = env->GetMethodID(
286      batchOptionsClass,
287      "getSourcesToUse",
288      "()I"
289      );
290  batchOptions.sources_to_use = env->CallIntMethod(
291      batchOptionsObject,
292      getSourcesToUse
293      );
294
295  jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I");
296  batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags);
297
298  env->DeleteLocalRef(batchOptionsClass);
299}
300
301/*
302 * Helper function to unwrap Geofence structures from the Java Runtime calls.
303 */
304static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
305    JNIEnv* env,
306    jobject geofenceRequestObject,
307    Geofence& geofence) {
308  jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
309
310  jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
311  geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
312
313  jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
314  // this works because GeofenceHardwareRequest.java and fused_location.h have
315  // the same notion of geofence types
316  GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
317  if(type != TYPE_CIRCLE) {
318    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
319  }
320  geofence.data->type = type;
321  GeofenceCircle& circle = geofence.data->geofence.circle;
322
323  jmethodID getLatitude = env->GetMethodID(
324      geofenceRequestClass,
325      "getLatitude",
326      "()D");
327  circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
328
329  jmethodID getLongitude = env->GetMethodID(
330      geofenceRequestClass,
331      "getLongitude",
332      "()D");
333  circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
334
335  jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
336  circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
337
338  GeofenceOptions* options = geofence.options;
339  jmethodID getMonitorTransitions = env->GetMethodID(
340      geofenceRequestClass,
341      "getMonitorTransitions",
342      "()I");
343  options->monitor_transitions = env->CallIntMethod(
344      geofenceRequestObject,
345      getMonitorTransitions);
346
347  jmethodID getUnknownTimer = env->GetMethodID(
348      geofenceRequestClass,
349      "getUnknownTimer",
350      "()I");
351  options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
352
353  jmethodID getNotificationResponsiveness = env->GetMethodID(
354      geofenceRequestClass,
355      "getNotificationResponsiveness",
356      "()I");
357  options->notification_responsivenes_ms = env->CallIntMethod(
358      geofenceRequestObject,
359      getNotificationResponsiveness);
360
361  jmethodID getLastTransition = env->GetMethodID(
362      geofenceRequestClass,
363      "getLastTransition",
364      "()I");
365  options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
366
367  // TODO: set data.sources_to_use when available
368
369  env->DeleteLocalRef(geofenceRequestClass);
370}
371
372/*
373 * Helper function to transform FlpLocation into a java object.
374 */
375static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
376  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
377  jmethodID locationCtor = sCallbackEnv->GetMethodID(
378      locationClass,
379      "<init>",
380      "(Ljava/lang/String;)V"
381      );
382
383  // the provider is set in the upper JVM layer
384  locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL);
385  jint flags = location->flags;
386
387  // set the valid information in the object
388  if (flags & FLP_LOCATION_HAS_LAT_LONG) {
389    jmethodID setLatitude = sCallbackEnv->GetMethodID(
390        locationClass,
391        "setLatitude",
392        "(D)V"
393        );
394    sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude);
395
396    jmethodID setLongitude = sCallbackEnv->GetMethodID(
397        locationClass,
398        "setLongitude",
399        "(D)V"
400        );
401    sCallbackEnv->CallVoidMethod(
402        locationObject,
403        setLongitude,
404        location->longitude
405        );
406
407    jmethodID setTime = sCallbackEnv->GetMethodID(
408        locationClass,
409        "setTime",
410        "(J)V"
411        );
412    sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp);
413  }
414
415  if (flags & FLP_LOCATION_HAS_ALTITUDE) {
416    jmethodID setAltitude = sCallbackEnv->GetMethodID(
417        locationClass,
418        "setAltitude",
419        "(D)V"
420        );
421    sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude);
422  }
423
424  if (flags & FLP_LOCATION_HAS_SPEED) {
425    jmethodID setSpeed = sCallbackEnv->GetMethodID(
426        locationClass,
427        "setSpeed",
428        "(F)V"
429        );
430    sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed);
431  }
432
433  if (flags & FLP_LOCATION_HAS_BEARING) {
434    jmethodID setBearing = sCallbackEnv->GetMethodID(
435        locationClass,
436        "setBearing",
437        "(F)V"
438        );
439    sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing);
440  }
441
442  if (flags & FLP_LOCATION_HAS_ACCURACY) {
443    jmethodID setAccuracy = sCallbackEnv->GetMethodID(
444        locationClass,
445        "setAccuracy",
446        "(F)V"
447        );
448    sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy);
449  }
450
451  // TODO: wire FlpLocation::sources_used when needed
452
453  sCallbackEnv->DeleteLocalRef(locationClass);
454}
455
456/*
457 * Helper function to serialize FlpLocation structures.
458 */
459static void TranslateToObjectArray(
460    int32_t locationsCount,
461    FlpLocation** locations,
462    jobjectArray& locationsArray) {
463  jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME);
464  locationsArray = sCallbackEnv->NewObjectArray(
465      locationsCount,
466      locationClass,
467      /* initialElement */ NULL
468      );
469
470  for (int i = 0; i < locationsCount; ++i) {
471    jobject locationObject = NULL;
472    TranslateToObject(locations[i], locationObject);
473    sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject);
474    sCallbackEnv->DeleteLocalRef(locationObject);
475  }
476
477  sCallbackEnv->DeleteLocalRef(locationClass);
478}
479
480static void LocationCallback(int32_t locationsCount, FlpLocation** locations) {
481  if(!IsValidCallbackThread()) {
482    return;
483  }
484
485  if(locationsCount == 0 || locations == NULL) {
486    ALOGE(
487        "Invalid LocationCallback. Count: %d, Locations: %p",
488        locationsCount,
489        locations
490        );
491    return;
492  }
493
494  jobjectArray locationsArray = NULL;
495  TranslateToObjectArray(locationsCount, locations, locationsArray);
496
497  sCallbackEnv->CallVoidMethod(
498      sCallbacksObj,
499      sOnLocationReport,
500      locationsArray
501      );
502  CheckExceptions(sCallbackEnv, __FUNCTION__);
503
504  if(locationsArray != NULL) {
505    sCallbackEnv->DeleteLocalRef(locationsArray);
506  }
507}
508
509static void AcquireWakelock() {
510  acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
511}
512
513static void ReleaseWakelock() {
514  release_wake_lock(WAKE_LOCK_NAME);
515}
516
517FlpCallbacks sFlpCallbacks = {
518  sizeof(FlpCallbacks),
519  LocationCallback,
520  AcquireWakelock,
521  ReleaseWakelock,
522  SetThreadEvent
523};
524
525static void ReportData(char* data, int length) {
526  jstring stringData = NULL;
527
528  if(length != 0 && data != NULL) {
529    stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length);
530  } else {
531    ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data);
532    return;
533  }
534
535  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData);
536  CheckExceptions(sCallbackEnv, __FUNCTION__);
537}
538
539FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = {
540  sizeof(FlpDiagnosticCallbacks),
541  SetThreadEvent,
542  ReportData
543};
544
545static void GeofenceTransitionCallback(
546    int32_t geofenceId,
547    FlpLocation* location,
548    int32_t transition,
549    FlpUtcTime timestamp,
550    uint32_t sourcesUsed
551    ) {
552  if(!IsValidCallbackThread()) {
553    return;
554  }
555
556  if(location == NULL) {
557    ALOGE("GeofenceTransition received with invalid location: %p", location);
558    return;
559  }
560
561  jobject locationObject = NULL;
562  TranslateToObject(location, locationObject);
563
564  sCallbackEnv->CallVoidMethod(
565      sCallbacksObj,
566      sOnGeofenceTransition,
567      geofenceId,
568      locationObject,
569      transition,
570      timestamp,
571      sourcesUsed
572      );
573  CheckExceptions(sCallbackEnv, __FUNCTION__);
574
575  if(locationObject != NULL) {
576    sCallbackEnv->DeleteLocalRef(locationObject);
577  }
578}
579
580static void GeofenceMonitorStatusCallback(
581    int32_t status,
582    uint32_t source,
583    FlpLocation* lastLocation) {
584  if(!IsValidCallbackThread()) {
585    return;
586  }
587
588  jobject locationObject = NULL;
589  if(lastLocation != NULL) {
590    TranslateToObject(lastLocation, locationObject);
591  }
592
593  sCallbackEnv->CallVoidMethod(
594      sCallbacksObj,
595      sOnGeofenceMonitorStatus,
596      status,
597      source,
598      locationObject
599      );
600  CheckExceptions(sCallbackEnv, __FUNCTION__);
601
602  if(locationObject != NULL) {
603    sCallbackEnv->DeleteLocalRef(locationObject);
604  }
605}
606
607static void GeofenceAddCallback(int32_t geofenceId, int32_t result) {
608  if(!IsValidCallbackThread()) {
609    return;
610  }
611
612  sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result);
613  CheckExceptions(sCallbackEnv, __FUNCTION__);
614}
615
616static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) {
617  if(!IsValidCallbackThread()) {
618    return;
619  }
620
621  sCallbackEnv->CallVoidMethod(
622      sCallbacksObj,
623      sOnGeofenceRemove,
624      geofenceId,
625      result
626      );
627  CheckExceptions(sCallbackEnv, __FUNCTION__);
628}
629
630static void GeofencePauseCallback(int32_t geofenceId, int32_t result) {
631  if(!IsValidCallbackThread()) {
632    return;
633  }
634
635  sCallbackEnv->CallVoidMethod(
636      sCallbacksObj,
637      sOnGeofencePause,
638      geofenceId,
639      result
640      );
641  CheckExceptions(sCallbackEnv, __FUNCTION__);
642}
643
644static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) {
645  if(!IsValidCallbackThread()) {
646    return;
647  }
648
649  sCallbackEnv->CallVoidMethod(
650      sCallbacksObj,
651      sOnGeofenceResume,
652      geofenceId,
653      result
654      );
655  CheckExceptions(sCallbackEnv, __FUNCTION__);
656}
657
658FlpGeofenceCallbacks sFlpGeofenceCallbacks = {
659  sizeof(FlpGeofenceCallbacks),
660  GeofenceTransitionCallback,
661  GeofenceMonitorStatusCallback,
662  GeofenceAddCallback,
663  GeofenceRemoveCallback,
664  GeofencePauseCallback,
665  GeofenceResumeCallback,
666  SetThreadEvent
667};
668
669/*
670 * Initializes the Fused Location Provider in the native side. It ensures that
671 * the Flp interfaces are initialized properly.
672 */
673static void Init(JNIEnv* env, jobject obj) {
674  if(sCallbacksObj == NULL) {
675    sCallbacksObj = env->NewGlobalRef(obj);
676  }
677
678  // initialize the Flp interfaces
679  if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) {
680    ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
681  }
682
683  if(sFlpDiagnosticInterface != NULL) {
684    sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks);
685  }
686
687  if(sFlpGeofencingInterface != NULL) {
688    sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks);
689  }
690
691  // TODO: inject any device context if when needed
692}
693
694static jboolean IsSupported(JNIEnv* env, jclass clazz) {
695  if (sFlpInterface == NULL) {
696    return JNI_FALSE;
697  }
698  return JNI_TRUE;
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  env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/);
941}
942
943static JNINativeMethod sMethods[] = {
944  //{"name", "signature", functionPointer }
945  {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)},
946  {"nativeInit", "()V", reinterpret_cast<void*>(Init)},
947  {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
948  {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
949  {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
950  {"nativeStartBatching",
951        "(ILandroid/location/FusedBatchOptions;)V",
952        reinterpret_cast<void*>(StartBatching)},
953  {"nativeUpdateBatchingOptions",
954        "(ILandroid/location/FusedBatchOptions;)V",
955        reinterpret_cast<void*>(UpdateBatchingOptions)},
956  {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
957  {"nativeRequestBatchedLocation",
958        "(I)V",
959        reinterpret_cast<void*>(GetBatchedLocation)},
960  {"nativeInjectLocation",
961        "(Landroid/location/Location;)V",
962        reinterpret_cast<void*>(InjectLocation)},
963  {"nativeIsDiagnosticSupported",
964        "()Z",
965        reinterpret_cast<void*>(IsDiagnosticSupported)},
966  {"nativeInjectDiagnosticData",
967        "(Ljava/lang/String;)V",
968        reinterpret_cast<void*>(InjectDiagnosticData)},
969  {"nativeIsDeviceContextSupported",
970        "()Z",
971        reinterpret_cast<void*>(IsDeviceContextSupported)},
972  {"nativeInjectDeviceContext",
973        "(I)V",
974        reinterpret_cast<void*>(InjectDeviceContext)},
975  {"nativeIsGeofencingSupported",
976        "()Z",
977        reinterpret_cast<void*>(IsGeofencingSupported)},
978  {"nativeAddGeofences",
979        "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
980        reinterpret_cast<void*>(AddGeofences)},
981  {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
982  {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
983  {"nativeModifyGeofenceOption",
984        "(IIIIII)V",
985        reinterpret_cast<void*>(ModifyGeofenceOption)},
986  {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
987};
988
989/*
990 * Registration method invoked on JNI Load.
991 */
992int register_android_server_location_FlpHardwareProvider(JNIEnv* env) {
993  return jniRegisterNativeMethods(
994      env,
995      "com/android/server/location/FlpHardwareProvider",
996      sMethods,
997      NELEM(sMethods)
998      );
999}
1000
1001} /* name-space Android */
1002