diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index d37c7b03dfb5a..2c4eb1b920869 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -22,6 +22,9 @@ cc_library_static { "BroadcastRadio/convert.cpp", "BroadcastRadio/regions.cpp", "gnss/GnssConfiguration.cpp", + "gnss/GnssMeasurement.cpp", + "gnss/GnssMeasurementCallback.cpp", + "gnss/Utils.cpp", "stats/PowerStatsPuller.cpp", "stats/SubsystemSleepStatePuller.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 1a6128fa92ba0..0ec8b3a7fee2e 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -41,6 +41,8 @@ #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" #include "gnss/GnssConfiguration.h" +#include "gnss/GnssMeasurement.h" +#include "gnss/Utils.h" #include "hardware_legacy/power.h" #include "jni.h" #include "utils/Log.h" @@ -56,11 +58,8 @@ #include #include -static jclass class_gnssMeasurementsEvent; -static jclass class_gnssMeasurement; static jclass class_location; static jclass class_gnssNavigationMessage; -static jclass class_gnssClock; static jclass class_gnssAntennaInfoBuilder; static jclass class_gnssPowerStats; static jclass class_phaseCenterOffset; @@ -68,7 +67,7 @@ static jclass class_sphericalCorrections; static jclass class_arrayList; static jclass class_doubleArray; -static jobject mCallbacksObj = nullptr; +jobject android::mCallbacksObj = nullptr; static jmethodID method_reportLocation; static jmethodID method_reportStatus; @@ -90,7 +89,6 @@ static jmethodID method_reportGeofenceAddStatus; static jmethodID method_reportGeofenceRemoveStatus; static jmethodID method_reportGeofencePauseStatus; static jmethodID method_reportGeofenceResumeStatus; -static jmethodID method_reportMeasurementData; static jmethodID method_reportAntennaInfo; static jmethodID method_reportNavigationMessages; static jmethodID method_reportLocationBatch; @@ -123,11 +121,8 @@ static jmethodID method_correctionPlaneAltDeg; static jmethodID method_correctionPlaneAzimDeg; static jmethodID method_reportNfwNotification; static jmethodID method_isInEmergencySession; -static jmethodID method_gnssMeasurementsEventCtor; static jmethodID method_locationCtor; static jmethodID method_gnssNavigationMessageCtor; -static jmethodID method_gnssClockCtor; -static jmethodID method_gnssMeasurementCtor; static jmethodID method_gnssAntennaInfoBuilderCtor; static jmethodID method_gnssPowerStatsCtor; static jmethodID method_phaseCenterOffsetCtor; @@ -145,7 +140,7 @@ static jmethodID method_setSubHalPowerIndicationCapabilities; * Save a pointer to JavaVm to attach/detach threads executing * callback methods that need to make JNI calls. */ -static JavaVM* sJvm; +JavaVM* android::ScopedJniThreadAttach::sJvm; using android::OK; using android::sp; @@ -201,14 +196,6 @@ using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug; using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug; using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo; using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback; -using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; -using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement; -using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement; -using IGnssMeasurement_V2_1 = android::hardware::gnss::V2_1::IGnssMeasurement; -using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback; -using IGnssMeasurementCallback_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurementCallback; -using IGnssMeasurementCallback_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurementCallback; -using IGnssMeasurementCallback_V2_1 = android::hardware::gnss::V2_1::IGnssMeasurementCallback; using IAGnssRil_V1_0 = android::hardware::gnss::V1_0::IAGnssRil; using IAGnssRil_V2_0 = android::hardware::gnss::V2_0::IAGnssRil; using IAGnss_V1_0 = android::hardware::gnss::V1_0::IAGnss; @@ -246,12 +233,11 @@ struct GnssDeathRecipient : virtual public hidl_death_recipient ALOGE("IGNSS hidl service failed, trying to recover..."); JNIEnv* env = android::AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(mCallbacksObj, method_reportGnssServiceDied); + env->CallVoidMethod(android::mCallbacksObj, method_reportGnssServiceDied); } }; // Must match the value from GnssMeasurement.java -static const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1<<4); static const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1<<4); sp gnssHalDeathRecipient = nullptr; @@ -274,10 +260,6 @@ sp gnssBatchingIface_V2_0 = nullptr; sp gnssDebugIface = nullptr; sp gnssDebugIface_V2_0 = nullptr; sp gnssNiIface = nullptr; -sp gnssMeasurementIface = nullptr; -sp gnssMeasurementIface_V1_1 = nullptr; -sp gnssMeasurementIface_V2_0 = nullptr; -sp gnssMeasurementIface_V2_1 = nullptr; sp gnssNavigationMessageIface = nullptr; sp gnssPowerIndicationIface = nullptr; sp gnssCorrectionsIface_V1_0 = nullptr; @@ -286,6 +268,7 @@ sp gnssVisibilityControlIface = nullptr; sp gnssAntennaInfoIface = nullptr; std::unique_ptr gnssConfigurationIface = nullptr; +std::unique_ptr gnssMeasurementIface = nullptr; #define WAKE_LOCK_NAME "GPS" @@ -305,172 +288,11 @@ bool hasLatLong(const GnssLocation_V2_0& location) { } } // namespace -template -class JavaMethodHelper { - public: - // Helper function to call setter on a Java object. - static void callJavaMethod( - JNIEnv* env, - jclass clazz, - jobject object, - const char* method_name, - T value); - - private: - static const char* const signature_; -}; - -template -void JavaMethodHelper::callJavaMethod( - JNIEnv* env, - jclass clazz, - jobject object, - const char* method_name, - T value) { - jmethodID method = env->GetMethodID(clazz, method_name, signature_); - env->CallVoidMethod(object, method, value); -} - -class JavaObject { - public: - JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor); - JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1); - JavaObject(JNIEnv* env, jclass clazz, jobject object); - - virtual ~JavaObject() = default; - - template - void callSetter(const char* method_name, T value); - template - void callSetter(const char* method_name, T* value, size_t size); - jobject get(); - - private: - JNIEnv* env_; - jclass clazz_; - jobject object_; -}; - -JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) : env_(env), - clazz_(clazz) { - object_ = env_->NewObject(clazz_, defaultCtor); -} - - -JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char * sz_arg_1) - : env_(env), clazz_(clazz) { - jstring szArg = env->NewStringUTF(sz_arg_1); - object_ = env_->NewObject(clazz_, stringCtor, szArg); - if (szArg) { - env_->DeleteLocalRef(szArg); - } -} - - -JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object) - : env_(env), clazz_(clazz), object_(object) { -} - -template -void JavaObject::callSetter(const char* method_name, T value) { - JavaMethodHelper::callJavaMethod( - env_, clazz_, object_, method_name, value); -} - -template<> -void JavaObject::callSetter( - const char* method_name, uint8_t* value, size_t size) { - jbyteArray array = env_->NewByteArray(size); - env_->SetByteArrayRegion(array, 0, size, reinterpret_cast(value)); - jmethodID method = env_->GetMethodID( - clazz_, - method_name, - "([B)V"); - env_->CallVoidMethod(object_, method, array); - env_->DeleteLocalRef(array); -} - -jobject JavaObject::get() { - return object_; -} - -// Define Java method signatures for all known types. -template<> -const char *const JavaMethodHelper::signature_ = "(B)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(B)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(S)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(S)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(I)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(I)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(J)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(J)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(F)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(D)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(Z)V"; -template<> -const char *const JavaMethodHelper::signature_ = "(Ljava/lang/String;)V"; -template <> -const char* const JavaMethodHelper::signature_ = "([D)V"; - -#define SET(setter, value) object.callSetter("set" # setter, (value)) static inline jboolean boolToJbool(bool value) { return value ? JNI_TRUE : JNI_FALSE; } -template -static inline void logHidlError(Return& result, const char* errorMessage) { - ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str()); -} - -template -static jboolean checkHidlReturn(Return& result, const char* errorMessage) { - if (!result.isOk()) { - logHidlError(result, errorMessage); - return JNI_FALSE; - } else { - return JNI_TRUE; - } -} - -static jboolean checkHidlReturn(Return& result, const char* errorMessage) { - if (!result.isOk()) { - logHidlError(result, errorMessage); - return JNI_FALSE; - } else if (!result) { - ALOGE("%s", errorMessage); - return JNI_FALSE; - } else { - return JNI_TRUE; - } -} - -static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { - if (env->ExceptionCheck()) { - ALOGE("An exception was thrown by callback '%s'.", methodName); - LOGE_EX(env); - env->ExceptionClear(); - } -} - -static jboolean checkAidlStatus(const Status& status, const char* errorMessage) { - if (!status.isOk()) { - ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str()); - return JNI_FALSE; - } - return JNI_TRUE; -} - struct ScopedJniString { ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) { mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr); @@ -499,62 +321,6 @@ private: const char* mNativeString; }; -class ScopedJniThreadAttach { -public: - ScopedJniThreadAttach() { - /* - * attachResult will also be JNI_OK if the thead was already attached to - * JNI before the call to AttachCurrentThread(). - */ - jint attachResult = sJvm->AttachCurrentThread(&mEnv, nullptr); - LOG_ALWAYS_FATAL_IF(attachResult != JNI_OK, "Unable to attach thread. Error %d", - attachResult); - } - - ~ScopedJniThreadAttach() { - jint detachResult = sJvm->DetachCurrentThread(); - /* - * Return if the thread was already detached. Log error for any other - * failure. - */ - if (detachResult == JNI_EDETACHED) { - return; - } - - LOG_ALWAYS_FATAL_IF(detachResult != JNI_OK, "Unable to detach thread. Error %d", - detachResult); - } - - JNIEnv* getEnv() { - /* - * Checking validity of mEnv in case the thread was detached elsewhere. - */ - LOG_ALWAYS_FATAL_IF(AndroidRuntime::getJNIEnv() != mEnv); - return mEnv; - } - -private: - JNIEnv* mEnv = nullptr; -}; - -thread_local std::unique_ptr tJniThreadAttacher; - -static JNIEnv* getJniEnv() { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - - /* - * If env is nullptr, the thread is not already attached to - * JNI. It is attached below and the destructor for ScopedJniThreadAttach - * will detach it on thread exit. - */ - if (env == nullptr) { - tJniThreadAttacher.reset(new ScopedJniThreadAttach()); - env = tJniThreadAttacher->getEnv(); - } - - return env; -} - static jobject translateGnssLocation(JNIEnv* env, const GnssLocation_V1_0& location) { JavaObject object(env, class_location, method_locationCtor, "gps"); @@ -1385,322 +1151,6 @@ void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject anten checkAndClearExceptionFromCallback(env, __FUNCTION__); } -/* - * GnssMeasurementCallback implements the callback methods required for the - * GnssMeasurement interface. - */ -struct GnssMeasurementCallback : public IGnssMeasurementCallback_V2_1 { - Return gnssMeasurementCb_2_1(const IGnssMeasurementCallback_V2_1::GnssData& data) - override; - Return gnssMeasurementCb_2_0(const IGnssMeasurementCallback_V2_0::GnssData& data) - override; - Return gnssMeasurementCb(const IGnssMeasurementCallback_V1_1::GnssData& data) override; - Return GnssMeasurementCb(const IGnssMeasurementCallback_V1_0::GnssData& data) override; - private: - template - void translateSingleGnssMeasurement(const T* measurement, JavaObject& object); - - template - jobjectArray translateAllGnssMeasurements(JNIEnv* env, const T* measurements, size_t count); - - template - void translateAndSetGnssData(const T& data); - - template - size_t getMeasurementCount(const T& data); - - template - void translateGnssClock(JavaObject& object, const T& data); - - void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray); -}; - -Return GnssMeasurementCallback::gnssMeasurementCb_2_1( - const IGnssMeasurementCallback_V2_1::GnssData& data) { - translateAndSetGnssData(data); - return Void(); -} - -Return GnssMeasurementCallback::gnssMeasurementCb_2_0( - const IGnssMeasurementCallback_V2_0::GnssData& data) { - translateAndSetGnssData(data); - return Void(); -} - -Return GnssMeasurementCallback::gnssMeasurementCb( - const IGnssMeasurementCallback_V1_1::GnssData& data) { - translateAndSetGnssData(data); - return Void(); -} - -Return GnssMeasurementCallback::GnssMeasurementCb( - const IGnssMeasurementCallback_V1_0::GnssData& data) { - translateAndSetGnssData(data); - return Void(); -} - -template -void GnssMeasurementCallback::translateAndSetGnssData(const T& data) { - JNIEnv* env = getJniEnv(); - - JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor); - translateGnssClock(gnssClockJavaObject, data); - jobject clock = gnssClockJavaObject.get(); - - size_t count = getMeasurementCount(data); - jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements.data(), count); - setMeasurementData(env, clock, measurementArray); - - env->DeleteLocalRef(clock); - env->DeleteLocalRef(measurementArray); -} - -template<> -size_t GnssMeasurementCallback::getMeasurementCount - (const IGnssMeasurementCallback_V1_0::GnssData& data) { - return data.measurementCount; -} - -template -size_t GnssMeasurementCallback::getMeasurementCount(const T& data) { - return data.measurements.size(); -} - -// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); -template<> -void GnssMeasurementCallback::translateSingleGnssMeasurement - ( - const IGnssMeasurementCallback_V1_0::GnssMeasurement* measurement, - JavaObject& object) { - uint32_t flags = static_cast(measurement->flags); - - SET(Svid, static_cast(measurement->svid)); - SET(ConstellationType, static_cast(measurement->constellation)); - SET(TimeOffsetNanos, measurement->timeOffsetNs); - SET(State, static_cast(measurement->state)); - SET(ReceivedSvTimeNanos, measurement->receivedSvTimeInNs); - SET(ReceivedSvTimeUncertaintyNanos, - measurement->receivedSvTimeUncertaintyInNs); - SET(Cn0DbHz, measurement->cN0DbHz); - SET(PseudorangeRateMetersPerSecond, measurement->pseudorangeRateMps); - SET(PseudorangeRateUncertaintyMetersPerSecond, - measurement->pseudorangeRateUncertaintyMps); - SET(AccumulatedDeltaRangeState, - (static_cast(measurement->accumulatedDeltaRangeState) & - ~ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0 - SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM); - SET(AccumulatedDeltaRangeUncertaintyMeters, - measurement->accumulatedDeltaRangeUncertaintyM); - - if (flags & static_cast(GnssMeasurementFlags::HAS_CARRIER_FREQUENCY)) { - SET(CarrierFrequencyHz, measurement->carrierFrequencyHz); - } - - // Intentionally not copying deprecated fields of carrierCycles, - // carrierPhase, carrierPhaseUncertainty - - SET(MultipathIndicator, static_cast(measurement->multipathIndicator)); - - if (flags & static_cast(GnssMeasurementFlags::HAS_SNR)) { - SET(SnrInDb, measurement->snrDb); - } - - if (flags & static_cast(GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL)) { - SET(AutomaticGainControlLevelInDb, measurement->agcLevelDb); - } -} - -// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); -template<> -void GnssMeasurementCallback::translateSingleGnssMeasurement - ( - const IGnssMeasurementCallback_V1_1::GnssMeasurement* measurement_V1_1, - JavaObject& object) { - translateSingleGnssMeasurement(&(measurement_V1_1->v1_0), object); - - // Set the V1_1 flag, and mark that new field has valid information for Java Layer - SET(AccumulatedDeltaRangeState, - (static_cast(measurement_V1_1->accumulatedDeltaRangeState) | - ADR_STATE_HALF_CYCLE_REPORTED)); -} - -// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); -template<> -void GnssMeasurementCallback::translateSingleGnssMeasurement - ( - const IGnssMeasurementCallback_V2_0::GnssMeasurement* measurement_V2_0, - JavaObject& object) { - JNIEnv* env = getJniEnv(); - translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); - - jstring codeType = env->NewStringUTF(measurement_V2_0->codeType.c_str()); - SET(CodeType, codeType); - - // Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated. - SET(State, static_cast(measurement_V2_0->state)); - - // Overwrite with v2_0.constellation since v2_0->v1_1->v1_0.constellation is deprecated. - SET(ConstellationType, static_cast(measurement_V2_0->constellation)); - - if (codeType) { - env->DeleteLocalRef(codeType); - } -} - -// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); -template<> -void GnssMeasurementCallback::translateSingleGnssMeasurement - ( - const IGnssMeasurementCallback_V2_1::GnssMeasurement* measurement_V2_1, - JavaObject& object) { - translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object); - - SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz); - - if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB) { - SET(FullInterSignalBiasNanos, measurement_V2_1->fullInterSignalBiasNs); - } - - if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB_UNCERTAINTY) { - SET(FullInterSignalBiasUncertaintyNanos, - measurement_V2_1->fullInterSignalBiasUncertaintyNs); - } - - if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) { - SET(SatelliteInterSignalBiasNanos, measurement_V2_1->satelliteInterSignalBiasNs); - } - - if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) { - SET(SatelliteInterSignalBiasUncertaintyNanos, - measurement_V2_1->satelliteInterSignalBiasUncertaintyNs); - } -} - -template -void GnssMeasurementCallback::translateGnssClock(JavaObject& object, const T& data) { - translateGnssClock(object, data.clock); -} - -template<> -void GnssMeasurementCallback::translateGnssClock( - JavaObject& object, const IGnssMeasurementCallback_V1_0::GnssClock& clock) { - uint32_t flags = static_cast(clock.gnssClockFlags); - if (flags & static_cast(GnssClockFlags::HAS_LEAP_SECOND)) { - SET(LeapSecond, static_cast(clock.leapSecond)); - } - - if (flags & static_cast(GnssClockFlags::HAS_TIME_UNCERTAINTY)) { - SET(TimeUncertaintyNanos, clock.timeUncertaintyNs); - } - - if (flags & static_cast(GnssClockFlags::HAS_FULL_BIAS)) { - SET(FullBiasNanos, clock.fullBiasNs); - } - - if (flags & static_cast(GnssClockFlags::HAS_BIAS)) { - SET(BiasNanos, clock.biasNs); - } - - if (flags & static_cast(GnssClockFlags::HAS_BIAS_UNCERTAINTY)) { - SET(BiasUncertaintyNanos, clock.biasUncertaintyNs); - } - - if (flags & static_cast(GnssClockFlags::HAS_DRIFT)) { - SET(DriftNanosPerSecond, clock.driftNsps); - } - - if (flags & static_cast(GnssClockFlags::HAS_DRIFT_UNCERTAINTY)) { - SET(DriftUncertaintyNanosPerSecond, clock.driftUncertaintyNsps); - } - - SET(TimeNanos, clock.timeNs); - SET(HardwareClockDiscontinuityCount, clock.hwClockDiscontinuityCount); -} - -template<> -void GnssMeasurementCallback::translateGnssClock( - JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssClock& clock) { - JNIEnv* env = getJniEnv(); - SET(ReferenceConstellationTypeForIsb, - static_cast(clock.referenceSignalTypeForIsb.constellation)); - SET(ReferenceCarrierFrequencyHzForIsb, clock.referenceSignalTypeForIsb.carrierFrequencyHz); - - jstring referenceCodeTypeForIsb = - env->NewStringUTF(clock.referenceSignalTypeForIsb.codeType.c_str()); - SET(ReferenceCodeTypeForIsb, referenceCodeTypeForIsb); - - translateGnssClock(object, clock.v1_0); - - if (referenceCodeTypeForIsb) { - env->DeleteLocalRef(referenceCodeTypeForIsb); - } -} - -template<> -void GnssMeasurementCallback::translateGnssClock( - JavaObject& object, const IGnssMeasurementCallback_V2_0::GnssData& data) { - auto elapsedRealtime = data.elapsedRealtime; - uint16_t flags = static_cast(elapsedRealtime.flags); - if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) { - SET(ElapsedRealtimeNanos, static_cast(elapsedRealtime.timestampNs)); - } - if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) { - SET(ElapsedRealtimeUncertaintyNanos, static_cast(elapsedRealtime.timeUncertaintyNs)); - } - translateGnssClock(object, data.clock); -} - -template<> -void GnssMeasurementCallback::translateGnssClock( - JavaObject& object, const IGnssMeasurementCallback_V2_1::GnssData& data) { - auto elapsedRealtime = data.elapsedRealtime; - uint16_t flags = static_cast(elapsedRealtime.flags); - if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) { - SET(ElapsedRealtimeNanos, static_cast(elapsedRealtime.timestampNs)); - } - if (flags & ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) { - SET(ElapsedRealtimeUncertaintyNanos, static_cast(elapsedRealtime.timeUncertaintyNs)); - } - translateGnssClock(object, data.clock); -} - -template -jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env, - const T* measurements, - size_t count) { - if (count == 0) { - return nullptr; - } - - jobjectArray gnssMeasurementArray = env->NewObjectArray( - count, - class_gnssMeasurement, - nullptr /* initialElement */); - - for (uint16_t i = 0; i < count; ++i) { - JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor); - translateSingleGnssMeasurement(&(measurements[i]), object); - jobject gnssMeasurement = object.get(); - env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement); - env->DeleteLocalRef(gnssMeasurement); - } - - return gnssMeasurementArray; -} - -void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock, - jobjectArray measurementArray) { - jobject gnssMeasurementsEvent = env->NewObject(class_gnssMeasurementsEvent, - method_gnssMeasurementsEventCtor, - clock, - measurementArray); - - env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, - gnssMeasurementsEvent); - checkAndClearExceptionFromCallback(env, __FUNCTION__); - env->DeleteLocalRef(gnssMeasurementsEvent); -} - /* * MeasurementCorrectionsCallback implements callback methods of interface * IMeasurementCorrectionsCallback.hal. @@ -2073,10 +1523,6 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus", "(II)V"); method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V"); - method_reportMeasurementData = env->GetMethodID( - clazz, - "reportMeasurementData", - "(Landroid/location/GnssMeasurementsEvent;)V"); method_reportNavigationMessages = env->GetMethodID( clazz, "reportNavigationMessage", @@ -2150,17 +1596,6 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz method_correctionPlaneAltDeg = env->GetMethodID(refPlaneClass, "getAltitudeMeters", "()D"); method_correctionPlaneAzimDeg = env->GetMethodID(refPlaneClass, "getAzimuthDegrees", "()D"); - jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent"); - class_gnssMeasurementsEvent= (jclass) env->NewGlobalRef(gnssMeasurementsEventClass); - method_gnssMeasurementsEventCtor = env->GetMethodID( - class_gnssMeasurementsEvent, - "", - "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); - - jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); - class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass); - method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "", "()V"); - jclass gnssAntennaInfoBuilder = env->FindClass("android/location/GnssAntennaInfo$Builder"); class_gnssAntennaInfoBuilder = (jclass)env->NewGlobalRef(gnssAntennaInfoBuilder); method_gnssAntennaInfoBuilderCtor = @@ -2206,10 +1641,6 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz class_gnssNavigationMessage = (jclass) env->NewGlobalRef(gnssNavigationMessageClass); method_gnssNavigationMessageCtor = env->GetMethodID(class_gnssNavigationMessage, "", "()V"); - jclass gnssClockClass = env->FindClass("android/location/GnssClock"); - class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass); - method_gnssClockCtor = env->GetMethodID(class_gnssClock, "", "()V"); - jclass arrayListClass = env->FindClass("java/util/ArrayList"); class_arrayList = (jclass)env->NewGlobalRef(arrayListClass); method_arrayListCtor = env->GetMethodID(class_arrayList, "", "()V"); @@ -2219,6 +1650,7 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass); gnss::GnssConfiguration_class_init_once(env); + gnss::GnssMeasurement_class_init_once(env, clazz); } /* Initialization needed at system boot and whenever GNSS service dies. */ @@ -2227,7 +1659,7 @@ static void android_location_GnssNative_init_once(JNIEnv* env, jobject obj, /* * Save a pointer to JVM. */ - jint jvmStatus = env->GetJavaVM(&sJvm); + jint jvmStatus = env->GetJavaVM(&android::ScopedJniThreadAttach::sJvm); if (jvmStatus != JNI_OK) { LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); } @@ -2324,40 +1756,30 @@ static void android_location_GnssNative_init_once(JNIEnv* env, jobject obj, gnssMeasurementIface = nullptr; if (gnssHal_V2_1 != nullptr) { auto gnssMeasurement = gnssHal_V2_1->getExtensionGnssMeasurement_2_1(); - if (!gnssMeasurement.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement_V2_1"); - } else { - gnssMeasurementIface_V2_1 = gnssMeasurement; - gnssMeasurementIface_V2_0 = gnssMeasurementIface_V2_1; - gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0; - gnssMeasurementIface = gnssMeasurementIface_V1_1; + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_1")) { + gnssMeasurementIface = + std::make_unique(gnssMeasurement); } } if (gnssHal_V2_0 != nullptr && gnssMeasurementIface == nullptr) { auto gnssMeasurement = gnssHal_V2_0->getExtensionGnssMeasurement_2_0(); - if (!gnssMeasurement.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement_V2_0"); - } else { - gnssMeasurementIface_V2_0 = gnssMeasurement; - gnssMeasurementIface_V1_1 = gnssMeasurementIface_V2_0; - gnssMeasurementIface = gnssMeasurementIface_V1_1; + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V2_0")) { + gnssMeasurementIface = + std::make_unique(gnssMeasurement); } } if (gnssHal_V1_1 != nullptr && gnssMeasurementIface == nullptr) { - auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); - if (!gnssMeasurement.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement_V1_1"); - } else { - gnssMeasurementIface_V1_1 = gnssMeasurement; - gnssMeasurementIface = gnssMeasurementIface_V1_1; - } + auto gnssMeasurement = gnssHal_V1_1->getExtensionGnssMeasurement_1_1(); + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_1")) { + gnssMeasurementIface = + std::make_unique(gnssMeasurement); + } } if (gnssMeasurementIface == nullptr) { auto gnssMeasurement = gnssHal->getExtensionGnssMeasurement(); - if (!gnssMeasurement.isOk()) { - ALOGD("Unable to get a handle to GnssMeasurement"); - } else { - gnssMeasurementIface = gnssMeasurement; + if (checkHidlReturn(gnssMeasurement, "Unable to get a handle to GnssMeasurement_V1_0")) { + gnssMeasurementIface = + std::make_unique(gnssMeasurement); } } @@ -3274,36 +2696,8 @@ static jboolean android_location_GnssMeasurementsProvider_start_measurement_coll return JNI_FALSE; } - sp cbIface = new GnssMeasurementCallback(); - Return result = - IGnssMeasurement_V1_0::GnssMeasurementStatus::ERROR_GENERIC; - if (gnssMeasurementIface_V2_1 != nullptr) { - result = gnssMeasurementIface_V2_1->setCallback_2_1(cbIface, enableFullTracking); - } else if (gnssMeasurementIface_V2_0 != nullptr) { - result = gnssMeasurementIface_V2_0->setCallback_2_0(cbIface, enableFullTracking); - } else if (gnssMeasurementIface_V1_1 != nullptr) { - result = gnssMeasurementIface_V1_1->setCallback_1_1(cbIface, enableFullTracking); - } else { - if (enableFullTracking == JNI_TRUE) { - ALOGW("Full tracking mode not supported in 1.0 GNSS HAL."); - } - result = gnssMeasurementIface->setCallback(cbIface); - } - - if (!checkHidlReturn(result, "IGnssMeasurement setCallback() failed.")) { - return JNI_FALSE; - } - - IGnssMeasurement_V1_0::GnssMeasurementStatus initRet = result; - if (initRet != IGnssMeasurement_V1_0::GnssMeasurementStatus::SUCCESS) { - ALOGE("An error has been found on GnssMeasurementInterface::init, status=%d", - static_cast(initRet)); - return JNI_FALSE; - } else { - ALOGD("gnss measurement infc has been enabled"); - } - - return JNI_TRUE; + return gnssMeasurementIface->setCallback(sp::make(mCallbacksObj), + enableFullTracking); } static jboolean android_location_GnssMeasurementsProvider_stop_measurement_collection( @@ -3314,8 +2708,7 @@ static jboolean android_location_GnssMeasurementsProvider_stop_measurement_colle return JNI_FALSE; } - auto result = gnssMeasurementIface->close(); - return checkHidlReturn(result, "IGnssMeasurement close() failed."); + return gnssMeasurementIface->close(); } static jboolean diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp index 8610c75e7e76e..3677641127f35 100644 --- a/services/core/jni/gnss/GnssConfiguration.cpp +++ b/services/core/jni/gnss/GnssConfiguration.cpp @@ -18,6 +18,7 @@ #define LOG_TAG "GnssConfigurationJni" #include "GnssConfiguration.h" +#include "Utils.h" using android::hardware::gnss::GnssConstellationType; using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType; @@ -42,29 +43,6 @@ namespace { jclass class_gnssConfiguration_halInterfaceVersion; jmethodID method_halInterfaceVersionCtor; -jboolean checkAidlStatus(const Status& status, const char* errorMessage) { - if (!status.isOk()) { - ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str()); - return JNI_FALSE; - } - return JNI_TRUE; -} - -template -inline void logHidlError(Return& result, const char* errorMessage) { - ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str()); -} - -template -jboolean checkHidlReturn(Return& result, const char* errorMessage) { - if (!result.isOk()) { - logHidlError(result, errorMessage); - return JNI_FALSE; - } else { - return JNI_TRUE; - } -} - jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) { return env->NewObject(class_gnssConfiguration_halInterfaceVersion, method_halInterfaceVersionCtor, major, minor); diff --git a/services/core/jni/gnss/GnssMeasurement.cpp b/services/core/jni/gnss/GnssMeasurement.cpp new file mode 100644 index 0000000000000..efa8ce7402237 --- /dev/null +++ b/services/core/jni/gnss/GnssMeasurement.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Define LOG_TAG before to overwrite the default value. +#define LOG_TAG "GnssMeasurementJni" + +#include "GnssMeasurement.h" +#include "Utils.h" + +using android::hardware::hidl_vec; +using android::hardware::Return; + +using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement; +using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement; +using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement; +using IGnssMeasurement_V2_1 = android::hardware::gnss::V2_1::IGnssMeasurement; + +namespace { +jboolean checkGnssMeasurementStatus(const IGnssMeasurement_V1_0::GnssMeasurementStatus& status) { + if (status != IGnssMeasurement_V1_0::GnssMeasurementStatus::SUCCESS) { + ALOGE("An error has been found on GnssMeasurementInterface::init, status=%d", + static_cast(status)); + return JNI_FALSE; + } else { + ALOGD("gnss measurement infc has been enabled"); + return JNI_TRUE; + } +} +} // anonymous namespace + +namespace android::gnss { + +// Implementation of GnssMeasurement_V1_0 + +GnssMeasurement_V1_0::GnssMeasurement_V1_0(const sp& iGnssMeasurement) + : mIGnssMeasurement_V1_0(iGnssMeasurement) {} + +jboolean GnssMeasurement_V1_0::setCallback(const sp& callback, + bool enableFullTracking) { + if (enableFullTracking == true) { + ALOGW("Full tracking is mode not supported in 1.0 GNSS HAL."); + } + auto status = mIGnssMeasurement_V1_0->setCallback(callback); + if (!checkHidlReturn(status, "IGnssMeasurement setCallback() failed.")) { + return JNI_FALSE; + } + + return checkGnssMeasurementStatus(status); +} + +jboolean GnssMeasurement_V1_0::close() { + auto result = mIGnssMeasurement_V1_0->close(); + return checkHidlReturn(result, "IGnssMeasurement close() failed."); +} + +// Implementation of GnssMeasurement_V1_1 + +GnssMeasurement_V1_1::GnssMeasurement_V1_1(const sp& iGnssMeasurement) + : GnssMeasurement_V1_0{iGnssMeasurement}, mIGnssMeasurement_V1_1(iGnssMeasurement) {} + +jboolean GnssMeasurement_V1_1::setCallback(const sp& callback, + bool enableFullTracking) { + auto status = mIGnssMeasurement_V1_1->setCallback_1_1(callback, enableFullTracking); + if (!checkHidlReturn(status, "IGnssMeasurement setCallback_V1_1() failed.")) { + return JNI_FALSE; + } + + return checkGnssMeasurementStatus(status); +} + +// Implementation of GnssMeasurement_V2_0 + +GnssMeasurement_V2_0::GnssMeasurement_V2_0(const sp& iGnssMeasurement) + : GnssMeasurement_V1_1{iGnssMeasurement}, mIGnssMeasurement_V2_0(iGnssMeasurement) {} + +jboolean GnssMeasurement_V2_0::setCallback(const sp& callback, + bool enableFullTracking) { + auto status = mIGnssMeasurement_V2_0->setCallback_2_0(callback, enableFullTracking); + if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_0() failed.")) { + return JNI_FALSE; + } + + return checkGnssMeasurementStatus(status); +} + +// Implementation of GnssMeasurement_V2_1 + +GnssMeasurement_V2_1::GnssMeasurement_V2_1(const sp& iGnssMeasurement) + : GnssMeasurement_V2_0{iGnssMeasurement}, mIGnssMeasurement_V2_1(iGnssMeasurement) {} + +jboolean GnssMeasurement_V2_1::setCallback(const sp& callback, + bool enableFullTracking) { + auto status = mIGnssMeasurement_V2_1->setCallback_2_1(callback, enableFullTracking); + if (!checkHidlReturn(status, "IGnssMeasurement setCallback_2_1() failed.")) { + return JNI_FALSE; + } + + return checkGnssMeasurementStatus(status); +} + +} // namespace android::gnss \ No newline at end of file diff --git a/services/core/jni/gnss/GnssMeasurement.h b/services/core/jni/gnss/GnssMeasurement.h new file mode 100644 index 0000000000000..0e41abb304436 --- /dev/null +++ b/services/core/jni/gnss/GnssMeasurement.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSMEASUREMENT_H +#define _ANDROID_SERVER_GNSS_GNSSMEASUREMENT_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include +#include +#include +#include +#include +#include "GnssMeasurementCallback.h" +#include "jni.h" + +namespace android::gnss { + +class GnssMeasurementInterface { +public: + virtual ~GnssMeasurementInterface() {} + virtual jboolean setCallback(const sp& callback, + bool enableFullTracking) = 0; + virtual jboolean close() = 0; +}; + +class GnssMeasurement_V1_0 : public GnssMeasurementInterface { +public: + GnssMeasurement_V1_0( + const sp& iGnssMeasurement); + jboolean setCallback(const sp& callback, + bool enableFullTracking) override; + jboolean close() override; + +private: + const sp mIGnssMeasurement_V1_0; +}; + +class GnssMeasurement_V1_1 : public GnssMeasurement_V1_0 { +public: + GnssMeasurement_V1_1( + const sp& iGnssMeasurement); + jboolean setCallback(const sp& callback, + bool enableFullTracking) override; + +private: + const sp mIGnssMeasurement_V1_1; +}; + +class GnssMeasurement_V2_0 : public GnssMeasurement_V1_1 { +public: + GnssMeasurement_V2_0( + const sp& iGnssMeasurement); + jboolean setCallback(const sp& callback, + bool enableFullTracking) override; + +private: + const sp mIGnssMeasurement_V2_0; +}; + +class GnssMeasurement_V2_1 : public GnssMeasurement_V2_0 { +public: + GnssMeasurement_V2_1( + const sp& iGnssMeasurement); + jboolean setCallback(const sp& callback, + bool enableFullTracking) override; + +private: + const sp mIGnssMeasurement_V2_1; +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSMEASUREMENT_H \ No newline at end of file diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp new file mode 100644 index 0000000000000..9f1826cc2290c --- /dev/null +++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GnssMeasCbJni" + +#include "GnssMeasurementCallback.h" + +namespace android::gnss { + +jclass class_gnssMeasurementsEvent; +jclass class_gnssMeasurement; +jclass class_gnssClock; + +jmethodID method_gnssMeasurementsEventCtor; +jmethodID method_gnssClockCtor; +jmethodID method_gnssMeasurementCtor; +jmethodID method_reportMeasurementData; + +void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz) { + method_reportMeasurementData = env->GetMethodID(clazz, "reportMeasurementData", + "(Landroid/location/GnssMeasurementsEvent;)V"); + jclass gnssMeasurementsEventClass = env->FindClass("android/location/GnssMeasurementsEvent"); + class_gnssMeasurementsEvent = (jclass)env->NewGlobalRef(gnssMeasurementsEventClass); + method_gnssMeasurementsEventCtor = + env->GetMethodID(class_gnssMeasurementsEvent, "", + "(Landroid/location/GnssClock;[Landroid/location/GnssMeasurement;)V"); + + jclass gnssMeasurementClass = env->FindClass("android/location/GnssMeasurement"); + class_gnssMeasurement = (jclass)env->NewGlobalRef(gnssMeasurementClass); + method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "", "()V"); + + jclass gnssClockClass = env->FindClass("android/location/GnssClock"); + class_gnssClock = (jclass)env->NewGlobalRef(gnssClockClass); + method_gnssClockCtor = env->GetMethodID(class_gnssClock, "", "()V"); +} + +hardware::Return GnssMeasurementCallback::gnssMeasurementCb_2_1( + const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssData& data) { + translateAndSetGnssData(data); + return hardware::Void(); +} + +hardware::Return GnssMeasurementCallback::gnssMeasurementCb_2_0( + const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssData& data) { + translateAndSetGnssData(data); + return hardware::Void(); +} + +hardware::Return GnssMeasurementCallback::gnssMeasurementCb( + const hardware::gnss::V1_1::IGnssMeasurementCallback::GnssData& data) { + translateAndSetGnssData(data); + return hardware::Void(); +} + +hardware::Return GnssMeasurementCallback::GnssMeasurementCb( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData& data) { + translateAndSetGnssData(data); + return hardware::Void(); +} + +template <> +size_t GnssMeasurementCallback::getMeasurementCount< + hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData>( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData& data) { + return data.measurementCount; +} + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V1_0::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssMeasurement* measurement, + JavaObject& object) { + uint32_t flags = static_cast(measurement->flags); + + SET(Svid, static_cast(measurement->svid)); + SET(ConstellationType, static_cast(measurement->constellation)); + SET(TimeOffsetNanos, measurement->timeOffsetNs); + SET(State, static_cast(measurement->state)); + SET(ReceivedSvTimeNanos, measurement->receivedSvTimeInNs); + SET(ReceivedSvTimeUncertaintyNanos, measurement->receivedSvTimeUncertaintyInNs); + SET(Cn0DbHz, measurement->cN0DbHz); + SET(PseudorangeRateMetersPerSecond, measurement->pseudorangeRateMps); + SET(PseudorangeRateUncertaintyMetersPerSecond, measurement->pseudorangeRateUncertaintyMps); + SET(AccumulatedDeltaRangeState, + (static_cast(measurement->accumulatedDeltaRangeState) & + ~ADR_STATE_HALF_CYCLE_REPORTED)); // Half Cycle state not reported from Hardware in V1_0 + SET(AccumulatedDeltaRangeMeters, measurement->accumulatedDeltaRangeM); + SET(AccumulatedDeltaRangeUncertaintyMeters, measurement->accumulatedDeltaRangeUncertaintyM); + + if (flags & static_cast(GnssMeasurementFlags::HAS_CARRIER_FREQUENCY)) { + SET(CarrierFrequencyHz, measurement->carrierFrequencyHz); + } + + // Intentionally not copying deprecated fields of carrierCycles, + // carrierPhase, carrierPhaseUncertainty + + SET(MultipathIndicator, static_cast(measurement->multipathIndicator)); + + if (flags & static_cast(GnssMeasurementFlags::HAS_SNR)) { + SET(SnrInDb, measurement->snrDb); + } + + if (flags & static_cast(GnssMeasurementFlags::HAS_AUTOMATIC_GAIN_CONTROL)) { + SET(AutomaticGainControlLevelInDb, measurement->agcLevelDb); + } +} + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V1_1::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V1_1::IGnssMeasurementCallback::GnssMeasurement* measurement_V1_1, + JavaObject& object) { + translateSingleGnssMeasurement(&(measurement_V1_1->v1_0), object); + + // Set the V1_1 flag, and mark that new field has valid information for Java Layer + SET(AccumulatedDeltaRangeState, + (static_cast(measurement_V1_1->accumulatedDeltaRangeState) | + ADR_STATE_HALF_CYCLE_REPORTED)); +} + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V2_0::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssMeasurement* measurement_V2_0, + JavaObject& object) { + JNIEnv* env = getJniEnv(); + translateSingleGnssMeasurement(&(measurement_V2_0->v1_1), object); + + jstring codeType = env->NewStringUTF(measurement_V2_0->codeType.c_str()); + SET(CodeType, codeType); + + // Overwrite with v2_0.state since v2_0->v1_1->v1_0.state is deprecated. + SET(State, static_cast(measurement_V2_0->state)); + + // Overwrite with v2_0.constellation since v2_0->v1_1->v1_0.constellation is deprecated. + SET(ConstellationType, static_cast(measurement_V2_0->constellation)); + + if (codeType) { + env->DeleteLocalRef(codeType); + } +} + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V2_1::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssMeasurement* measurement_V2_1, + JavaObject& object) { + translateSingleGnssMeasurement(&(measurement_V2_1->v2_0), object); + + SET(BasebandCn0DbHz, measurement_V2_1->basebandCN0DbHz); + + if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB) { + SET(FullInterSignalBiasNanos, measurement_V2_1->fullInterSignalBiasNs); + } + + if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_FULL_ISB_UNCERTAINTY) { + SET(FullInterSignalBiasUncertaintyNanos, + measurement_V2_1->fullInterSignalBiasUncertaintyNs); + } + + if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB) { + SET(SatelliteInterSignalBiasNanos, measurement_V2_1->satelliteInterSignalBiasNs); + } + + if (measurement_V2_1->flags & GnssMeasurementFlags::HAS_SATELLITE_ISB_UNCERTAINTY) { + SET(SatelliteInterSignalBiasUncertaintyNanos, + measurement_V2_1->satelliteInterSignalBiasUncertaintyNs); + } +} + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssClock& clock) { + uint32_t flags = static_cast(clock.gnssClockFlags); + if (flags & static_cast(GnssClockFlags::HAS_LEAP_SECOND)) { + SET(LeapSecond, static_cast(clock.leapSecond)); + } + + if (flags & static_cast(GnssClockFlags::HAS_TIME_UNCERTAINTY)) { + SET(TimeUncertaintyNanos, clock.timeUncertaintyNs); + } + + if (flags & static_cast(GnssClockFlags::HAS_FULL_BIAS)) { + SET(FullBiasNanos, clock.fullBiasNs); + } + + if (flags & static_cast(GnssClockFlags::HAS_BIAS)) { + SET(BiasNanos, clock.biasNs); + } + + if (flags & static_cast(GnssClockFlags::HAS_BIAS_UNCERTAINTY)) { + SET(BiasUncertaintyNanos, clock.biasUncertaintyNs); + } + + if (flags & static_cast(GnssClockFlags::HAS_DRIFT)) { + SET(DriftNanosPerSecond, clock.driftNsps); + } + + if (flags & static_cast(GnssClockFlags::HAS_DRIFT_UNCERTAINTY)) { + SET(DriftUncertaintyNanosPerSecond, clock.driftUncertaintyNsps); + } + + SET(TimeNanos, clock.timeNs); + SET(HardwareClockDiscontinuityCount, clock.hwClockDiscontinuityCount); +} + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, + const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssClock& clock) { + JNIEnv* env = getJniEnv(); + SET(ReferenceConstellationTypeForIsb, + static_cast(clock.referenceSignalTypeForIsb.constellation)); + SET(ReferenceCarrierFrequencyHzForIsb, clock.referenceSignalTypeForIsb.carrierFrequencyHz); + + jstring referenceCodeTypeForIsb = + env->NewStringUTF(clock.referenceSignalTypeForIsb.codeType.c_str()); + SET(ReferenceCodeTypeForIsb, referenceCodeTypeForIsb); + + translateGnssClock(object, clock.v1_0); + + if (referenceCodeTypeForIsb) { + env->DeleteLocalRef(referenceCodeTypeForIsb); + } +} + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssData& data) { + auto elapsedRealtime = data.elapsedRealtime; + uint16_t flags = static_cast(elapsedRealtime.flags); + if (flags & hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) { + SET(ElapsedRealtimeNanos, static_cast(elapsedRealtime.timestampNs)); + } + if (flags & hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) { + SET(ElapsedRealtimeUncertaintyNanos, + static_cast(elapsedRealtime.timeUncertaintyNs)); + } + translateGnssClock(object, data.clock); +} + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssData& data) { + auto elapsedRealtime = data.elapsedRealtime; + uint16_t flags = static_cast(elapsedRealtime.flags); + if (flags & hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) { + SET(ElapsedRealtimeNanos, static_cast(elapsedRealtime.timestampNs)); + } + if (flags & hardware::gnss::V2_0::ElapsedRealtimeFlags::HAS_TIME_UNCERTAINTY_NS) { + SET(ElapsedRealtimeUncertaintyNanos, + static_cast(elapsedRealtime.timeUncertaintyNs)); + } + translateGnssClock(object, data.clock); +} + +void GnssMeasurementCallback::setMeasurementData(JNIEnv* env, jobject clock, + jobjectArray measurementArray) { + jobject gnssMeasurementsEvent = + env->NewObject(class_gnssMeasurementsEvent, method_gnssMeasurementsEventCtor, clock, + measurementArray); + + env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gnssMeasurementsEvent); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + env->DeleteLocalRef(gnssMeasurementsEvent); +} + +} // namespace android::gnss diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h new file mode 100644 index 0000000000000..71c33a7e7dbb7 --- /dev/null +++ b/services/core/jni/gnss/GnssMeasurementCallback.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSMEASUREMENTCALLBACK_H +#define _ANDROID_SERVER_GNSS_GNSSMEASUREMENTCALLBACK_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include +#include +#include +#include +#include +#include "Utils.h" +#include "jni.h" + +namespace android::gnss { + +extern jclass class_gnssMeasurementsEvent; +extern jclass class_gnssMeasurement; +extern jclass class_gnssClock; + +extern jmethodID method_gnssMeasurementsEventCtor; +extern jmethodID method_gnssClockCtor; +extern jmethodID method_gnssMeasurementCtor; +extern jmethodID method_reportMeasurementData; + +void GnssMeasurement_class_init_once(JNIEnv* env, jclass& clazz); + +/* + * GnssMeasurementCallback implements the callback methods required for the + * GnssMeasurement interface. + */ +struct GnssMeasurementCallback : public hardware::gnss::V2_1::IGnssMeasurementCallback { + GnssMeasurementCallback(jobject& callbacksObj) : mCallbacksObj(callbacksObj) {} + hardware::Return gnssMeasurementCb_2_1( + const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssData& data) override; + hardware::Return gnssMeasurementCb_2_0( + const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssData& data) override; + hardware::Return gnssMeasurementCb( + const hardware::gnss::V1_1::IGnssMeasurementCallback::GnssData& data) override; + hardware::Return GnssMeasurementCb( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData& data) override; + +private: + template + void translateSingleGnssMeasurement(const T* measurement, JavaObject& object); + + template + jobjectArray translateAllGnssMeasurements(JNIEnv* env, const T* measurements, size_t count); + + template + void translateAndSetGnssData(const T& data); + + template + size_t getMeasurementCount(const T& data); + + template + void translateGnssClock(JavaObject& object, const T& data); + + void setMeasurementData(JNIEnv* env, jobject clock, jobjectArray measurementArray); + + jobject& mCallbacksObj; +}; + +template +void GnssMeasurementCallback::translateAndSetGnssData(const T& data) { + JNIEnv* env = getJniEnv(); + + JavaObject gnssClockJavaObject(env, class_gnssClock, method_gnssClockCtor); + translateGnssClock(gnssClockJavaObject, data); + jobject clock = gnssClockJavaObject.get(); + + size_t count = getMeasurementCount(data); + jobjectArray measurementArray = + translateAllGnssMeasurements(env, data.measurements.data(), count); + setMeasurementData(env, clock, measurementArray); + + env->DeleteLocalRef(clock); + env->DeleteLocalRef(measurementArray); +} + +template <> +size_t GnssMeasurementCallback::getMeasurementCount< + hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData>( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssData& data); + +template +size_t GnssMeasurementCallback::getMeasurementCount(const T& data) { + return data.measurements.size(); +} + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V1_0::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssMeasurement* measurement, + JavaObject& object); + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V1_1::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V1_1::IGnssMeasurementCallback::GnssMeasurement* measurement_V1_1, + JavaObject& object); + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V2_0::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssMeasurement* measurement_V2_0, + JavaObject& object); + +// Preallocate object as: JavaObject object(env, "android/location/GnssMeasurement"); +template <> +void GnssMeasurementCallback::translateSingleGnssMeasurement< + hardware::gnss::V2_1::IGnssMeasurementCallback::GnssMeasurement>( + const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssMeasurement* measurement_V2_1, + JavaObject& object); + +template +void GnssMeasurementCallback::translateGnssClock(JavaObject& object, const T& data) { + translateGnssClock(object, data.clock); +} + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V1_0::IGnssMeasurementCallback::GnssClock& clock); + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssClock& clock); + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V2_0::IGnssMeasurementCallback::GnssData& data); + +template <> +void GnssMeasurementCallback::translateGnssClock( + JavaObject& object, const hardware::gnss::V2_1::IGnssMeasurementCallback::GnssData& data); + +template +jobjectArray GnssMeasurementCallback::translateAllGnssMeasurements(JNIEnv* env, + const T* measurements, + size_t count) { + if (count == 0) { + return nullptr; + } + + jobjectArray gnssMeasurementArray = + env->NewObjectArray(count, class_gnssMeasurement, nullptr /* initialElement */); + + for (uint16_t i = 0; i < count; ++i) { + JavaObject object(env, class_gnssMeasurement, method_gnssMeasurementCtor); + translateSingleGnssMeasurement(&(measurements[i]), object); + jobject gnssMeasurement = object.get(); + env->SetObjectArrayElement(gnssMeasurementArray, i, gnssMeasurement); + env->DeleteLocalRef(gnssMeasurement); + } + + return gnssMeasurementArray; +} + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSMEASUREMENTCALLBACK_H \ No newline at end of file diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp new file mode 100644 index 0000000000000..20e14a919fce4 --- /dev/null +++ b/services/core/jni/gnss/Utils.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GnssUtilsJni" + +#include "Utils.h" + +namespace android { + +namespace { + +thread_local std::unique_ptr tJniThreadAttacher; + +} // anonymous namespace + +// Define Java method signatures for all known types. +template <> +const char* const JavaMethodHelper::signature_ = "(B)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(B)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(S)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(S)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(I)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(I)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(J)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(J)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(F)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(D)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(Z)V"; +template <> +const char* const JavaMethodHelper::signature_ = "(Ljava/lang/String;)V"; +template <> +const char* const JavaMethodHelper::signature_ = "([D)V"; + +jboolean checkAidlStatus(const android::binder::Status& status, const char* errorMessage) { + if (!status.isOk()) { + ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str()); + return JNI_FALSE; + } + return JNI_TRUE; +} + +jboolean checkHidlReturn(hardware::Return& result, const char* errorMessage) { + if (!result.isOk()) { + logHidlError(result, errorMessage); + return JNI_FALSE; + } else if (!result) { + ALOGE("%s", errorMessage); + return JNI_FALSE; + } else { + return JNI_TRUE; + } +} + +void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor) + : env_(env), clazz_(clazz) { + object_ = env_->NewObject(clazz_, defaultCtor); +} + +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char* sz_arg_1) + : env_(env), clazz_(clazz) { + jstring szArg = env->NewStringUTF(sz_arg_1); + object_ = env_->NewObject(clazz_, stringCtor, szArg); + if (szArg) { + env_->DeleteLocalRef(szArg); + } +} + +JavaObject::JavaObject(JNIEnv* env, jclass clazz, jobject object) + : env_(env), clazz_(clazz), object_(object) {} + +template <> +void JavaObject::callSetter(const char* method_name, uint8_t* value, size_t size) { + jbyteArray array = env_->NewByteArray(size); + env_->SetByteArrayRegion(array, 0, size, reinterpret_cast(value)); + jmethodID method = env_->GetMethodID(clazz_, method_name, "([B)V"); + env_->CallVoidMethod(object_, method, array); + env_->DeleteLocalRef(array); +} + +JNIEnv* getJniEnv() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + /* + * If env is nullptr, the thread is not already attached to + * JNI. It is attached below and the destructor for ScopedJniThreadAttach + * will detach it on thread exit. + */ + if (env == nullptr) { + tJniThreadAttacher.reset(new ScopedJniThreadAttach()); + env = tJniThreadAttacher->getEnv(); + } + + return env; +} + +} // namespace android diff --git a/services/core/jni/gnss/Utils.h b/services/core/jni/gnss/Utils.h new file mode 100644 index 0000000000000..32f53d05d5d50 --- /dev/null +++ b/services/core/jni/gnss/Utils.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_SERVER_GNSS_UTILS_H +#define _ANDROID_SERVER_GNSS_UTILS_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include +#include +#include +#include +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" +#include "jni.h" + +namespace android { + +namespace { + +// Must match the value from GnssMeasurement.java +const uint32_t ADR_STATE_HALF_CYCLE_REPORTED = (1 << 4); + +} // anonymous namespace + +extern jobject mCallbacksObj; + +jboolean checkHidlReturn(hardware::Return& result, const char* errorMessage); + +jboolean checkAidlStatus(const android::binder::Status& status, const char* errorMessage); + +void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); + +template +void logHidlError(hardware::Return& result, const char* errorMessage) { + ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str()); +} + +template +jboolean checkHidlReturn(hardware::Return& result, const char* errorMessage) { + if (!result.isOk()) { + logHidlError(result, errorMessage); + return JNI_FALSE; + } else { + return JNI_TRUE; + } +} + +template +class JavaMethodHelper { +public: + // Helper function to call setter on a Java object. + static void callJavaMethod(JNIEnv* env, jclass clazz, jobject object, const char* method_name, + T value); + +private: + static const char* const signature_; +}; + +// Define Java method signatures for all known types. +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; +template <> +const char* const JavaMethodHelper::signature_; + +template +void JavaMethodHelper::callJavaMethod(JNIEnv* env, jclass clazz, jobject object, + const char* method_name, T value) { + jmethodID method = env->GetMethodID(clazz, method_name, signature_); + env->CallVoidMethod(object, method, value); +} + +class JavaObject { +public: + JavaObject(JNIEnv* env, jclass clazz, jmethodID defaultCtor); + JavaObject(JNIEnv* env, jclass clazz, jmethodID stringCtor, const char* sz_arg_1); + JavaObject(JNIEnv* env, jclass clazz, jobject object); + + virtual ~JavaObject() = default; + + template + void callSetter(const char* method_name, T value); + template + void callSetter(const char* method_name, T* value, size_t size); + jobject get() { return object_; } + +private: + JNIEnv* env_; + jclass clazz_; + jobject object_; +}; + +template +void JavaObject::callSetter(const char* method_name, T value) { + JavaMethodHelper::callJavaMethod(env_, clazz_, object_, method_name, value); +} + +#define SET(setter, value) object.callSetter("set" #setter, (value)) + +class ScopedJniThreadAttach { +public: + static JavaVM* sJvm; + + ScopedJniThreadAttach() { + /* + * attachResult will also be JNI_OK if the thead was already attached to + * JNI before the call to AttachCurrentThread(). + */ + jint attachResult = sJvm->AttachCurrentThread(&mEnv, nullptr); + LOG_ALWAYS_FATAL_IF(attachResult != JNI_OK, "Unable to attach thread. Error %d", + attachResult); + } + + ~ScopedJniThreadAttach() { + jint detachResult = sJvm->DetachCurrentThread(); + /* + * Return if the thread was already detached. Log error for any other + * failure. + */ + if (detachResult == JNI_EDETACHED) { + return; + } + + LOG_ALWAYS_FATAL_IF(detachResult != JNI_OK, "Unable to detach thread. Error %d", + detachResult); + } + + JNIEnv* getEnv() { + /* + * Checking validity of mEnv in case the thread was detached elsewhere. + */ + LOG_ALWAYS_FATAL_IF(AndroidRuntime::getJNIEnv() != mEnv); + return mEnv; + } + +private: + JNIEnv* mEnv = nullptr; +}; + +JNIEnv* getJniEnv(); + +} // namespace android + +#endif // _ANDROID_SERVER_GNSS_UTILS_H