GNSS Batching - Default Implementation

Connections from .hal layer, through to
Location Manager System APIs

Bug: 31974439
Test: builds, boots, ordinary GNSS & new GNSS batching
      basic functional checks on Marlin
Change-Id: If75118c27b5ed34cfc16c9f817b60a3be5485095
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 01a1efc..1578562 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -58,6 +58,7 @@
 static jmethodID method_reportGeofenceResumeStatus;
 static jmethodID method_reportMeasurementData;
 static jmethodID method_reportNavigationMessages;
+static jmethodID method_reportLocationBatch;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -80,6 +81,8 @@
 using android::hardware::gnss::V1_0::IAGnssRil;
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnss;
+using android::hardware::gnss::V1_0::IGnssBatching;
+using android::hardware::gnss::V1_0::IGnssBatchingCallback;
 using android::hardware::gnss::V1_0::IGnssCallback;
 using android::hardware::gnss::V1_0::IGnssConfiguration;
 using android::hardware::gnss::V1_0::IGnssDebug;
@@ -100,6 +103,7 @@
 sp<IAGnssRil> agnssRilIface = nullptr;
 sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
 sp<IAGnss> agnssIface = nullptr;
+sp<IGnssBatching> gnssBatchingIface = nullptr;
 sp<IGnssDebug> gnssDebugIface = nullptr;
 sp<IGnssConfiguration> gnssConfigurationIface = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
@@ -139,6 +143,7 @@
 class JavaObject {
  public:
     JavaObject(JNIEnv* env, const char* class_name);
+    JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
     virtual ~JavaObject();
 
     template<class T>
@@ -159,6 +164,12 @@
     object_ = env_->NewObject(clazz_, ctor);
 }
 
+JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) {
+    clazz_ = env_->FindClass(class_name);
+    jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V");
+    object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
+}
+
 JavaObject::~JavaObject() {
     env_->DeleteLocalRef(clazz_);
 }
@@ -591,6 +602,7 @@
     env->CallVoidMethod(mCallbacksObj,
                         method_reportNavigationMessages,
                         navigationMessage);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
     env->DeleteLocalRef(navigationMessage);
     return Void();
 }
@@ -925,6 +937,81 @@
     return Void();
 }
 
+/*
+ * GnssBatchingCallback interface implements the callback methods
+ * required by the IGnssBatching interface.
+ */
+struct GnssBatchingCallback : public IGnssBatchingCallback {
+    /*
+    * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback
+    * follow.
+    */
+    Return<void> gnssLocationBatchCb(
+        const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations)
+        override;
+ private:
+    jobject translateLocation(
+            JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location);
+};
+
+Return<void> GnssBatchingCallback::gnssLocationBatchCb(
+        const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) {
+    JNIEnv* env = getJniEnv();
+
+    jobjectArray jLocations = env->NewObjectArray(locations.size(),
+            env->FindClass("android/location/Location"), nullptr);
+
+    for (uint16_t i = 0; i < locations.size(); ++i) {
+        jobject jLocation = translateLocation(env, &locations[i]);
+        env->SetObjectArrayElement(jLocations, i, jLocation);
+        env->DeleteLocalRef(jLocation);
+    }
+
+    env->CallVoidMethod(mCallbacksObj, method_reportLocationBatch, jLocations);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+    env->DeleteLocalRef(jLocations);
+
+    return Void();
+}
+
+// TODO: Use this common code to translate location for Geofencing and regular Location
+jobject GnssBatchingCallback::translateLocation(
+        JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location) {
+    JavaObject object(env, "android/location/Location", "gps");
+
+    uint16_t flags = static_cast<uint32_t>(location->gnssLocationFlags);
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) {
+        SET(Latitude, location->latitudeDegrees);
+        SET(Longitude, location->longitudeDegrees);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) {
+        SET(Altitude, location->altitudeMeters);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) {
+        SET(Speed, location->speedMetersPerSec);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) {
+        SET(Bearing, location->bearingDegrees);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
+        SET(Accuracy, location->horizontalAccuracyMeters);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+        SET(VerticalAccuracyMeters, location->verticalAccuracyMeters);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) {
+        SET(SpeedAccuracyMetersPerSecond, location->speedAccuracyMetersPerSecond);
+    }
+    if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) {
+        SET(BearingAccuracyDegrees, location->bearingAccuracyDegrees);
+    }
+    SET(Time, location->timestamp);
+
+    return object.get();
+}
+
+
 static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
     method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFFFFJ)V");
     method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
@@ -959,6 +1046,10 @@
             clazz,
             "reportNavigationMessage",
             "(Landroid/location/GnssNavigationMessage;)V");
+    method_reportLocationBatch = env->GetMethodID(
+            clazz,
+            "reportLocationBatch",
+            "([Landroid/location/Location;)V");
 
     /*
      * Save a pointer to JVM.
@@ -1033,6 +1124,13 @@
         } else {
             gnssGeofencingIface = gnssGeofencing;
         }
+
+        auto gnssBatching = gnssHal->getExtensionGnssBatching();
+        if (!gnssBatching.isOk()) {
+            ALOGD("Unable to get a handle to gnssBatching");
+        } else {
+            gnssBatchingIface = gnssBatching;
+        }
     } else {
       ALOGE("Unable to get GPS service\n");
     }
@@ -1704,6 +1802,67 @@
     }
 }
 
+static jint android_location_GnssLocationProvider_get_batch_size(JNIEnv*, jclass) {
+    if (gnssBatchingIface == nullptr) {
+        return 0; // batching not supported, size = 0
+    }
+    auto result = gnssBatchingIface->getBatchSize();
+    if (result.isOk()) {
+        return static_cast<jint>(result);
+    } else {
+        return 0; // failure in binder, don't support batching
+    }
+}
+
+static jboolean android_location_GnssLocationProvider_init_batching(JNIEnv*, jclass) {
+    if (gnssBatchingIface == nullptr) {
+        return JNI_FALSE; // batching not supported
+    }
+    sp<IGnssBatchingCallback> gnssBatchingCbIface = new GnssBatchingCallback();
+
+    return static_cast<jboolean>(gnssBatchingIface->init(gnssBatchingCbIface));
+}
+
+static void android_location_GnssLocationProvider_cleanup_batching(JNIEnv*, jclass) {
+    if (gnssBatchingIface == nullptr) {
+        return; // batching not supported
+    }
+    gnssBatchingIface->cleanup();
+}
+
+static jboolean android_location_GnssLocationProvider_start_batch(JNIEnv*, jclass,
+        jlong periodNanos, jboolean wakeOnFifoFull) {
+    if (gnssBatchingIface == nullptr) {
+        return JNI_FALSE; // batching not supported
+    }
+
+    IGnssBatching::Options options;
+    options.periodNanos = periodNanos;
+    if (wakeOnFifoFull) {
+        options.flags = static_cast<uint8_t>(IGnssBatching::Flag::WAKEUP_ON_FIFO_FULL);
+    } else {
+        options.flags = 0;
+    }
+
+    return static_cast<jboolean>(gnssBatchingIface->start(options));
+}
+
+static void android_location_GnssLocationProvider_flush_batch(JNIEnv*, jclass) {
+    if (gnssBatchingIface == nullptr) {
+        return; // batching not supported
+    }
+
+    gnssBatchingIface->flush();
+}
+
+static jboolean android_location_GnssLocationProvider_stop_batch(JNIEnv*, jclass) {
+    if (gnssBatchingIface == nullptr) {
+        return JNI_FALSE; // batching not supported
+    }
+
+    return gnssBatchingIface->stop();
+}
+
 static const JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", reinterpret_cast<void *>(
@@ -1829,6 +1988,27 @@
     {"native_set_emergency_supl_pdn",
             "(I)Z",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
+    {"native_get_batch_size",
+            "()I",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_get_batch_size)},
+    {"native_init_batching",
+            "()Z",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)},
+    {"native_start_batch",
+            "(JZ)Z",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_start_batch)},
+    {"native_flush_batch",
+            "()V",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_flush_batch)},
+    {"native_stop_batch",
+            "()Z",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_stop_batch)},
+    {"native_init_batching",
+            "()Z",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)},
+    {"native_cleanup_batching",
+            "()V",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_cleanup_batching)},
 };
 
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {