| /* |
| * Copyright (C) 2022 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 "GnssCallbackJni" |
| |
| #include "GnssCallback.h" |
| |
| #include <hardware_legacy/power.h> |
| |
| #define WAKE_LOCK_NAME "GPS" |
| |
| namespace android::gnss { |
| |
| using android::hardware::gnss::V1_0::GnssLocationFlags; |
| using binder::Status; |
| using hardware::hidl_vec; |
| using hardware::Return; |
| using hardware::Void; |
| |
| using GnssLocationAidl = android::hardware::gnss::GnssLocation; |
| using GnssSignalType = android::hardware::gnss::GnssSignalType; |
| using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation; |
| using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation; |
| using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback; |
| using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback; |
| using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback; |
| using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback; |
| |
| jmethodID method_reportGnssServiceDied; |
| |
| namespace { |
| |
| jclass class_arrayList; |
| jclass class_gnssSignalType; |
| |
| jmethodID method_arrayListAdd; |
| jmethodID method_arrayListCtor; |
| jmethodID method_gnssSignalTypeCreate; |
| jmethodID method_reportLocation; |
| jmethodID method_reportStatus; |
| jmethodID method_reportSvStatus; |
| jmethodID method_reportNmea; |
| jmethodID method_setTopHalCapabilities; |
| jmethodID method_setSignalTypeCapabilities; |
| jmethodID method_setGnssYearOfHardware; |
| jmethodID method_setGnssHardwareModelName; |
| jmethodID method_requestLocation; |
| jmethodID method_requestUtcTime; |
| |
| // Returns true if location has lat/long information. |
| inline bool hasLatLong(const GnssLocationAidl& location) { |
| return (location.gnssLocationFlags & hardware::gnss::GnssLocation::HAS_LAT_LONG) != 0; |
| } |
| |
| // Returns true if location has lat/long information. |
| inline bool hasLatLong(const GnssLocation_V1_0& location) { |
| return (static_cast<uint32_t>(location.gnssLocationFlags) & GnssLocationFlags::HAS_LAT_LONG) != |
| 0; |
| } |
| |
| // Returns true if location has lat/long information. |
| inline bool hasLatLong(const GnssLocation_V2_0& location) { |
| return hasLatLong(location.v1_0); |
| } |
| |
| inline jboolean boolToJbool(bool value) { |
| return value ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| // Must match the value from GnssMeasurement.java |
| const uint32_t SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4); |
| |
| } // anonymous namespace |
| |
| bool isSvStatusRegistered = false; |
| bool isNmeaRegistered = false; |
| |
| void Gnss_class_init_once(JNIEnv* env, jclass& clazz) { |
| method_reportLocation = |
| env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V"); |
| method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); |
| method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "(I[I[F[F[F[F[F)V"); |
| method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); |
| |
| method_setTopHalCapabilities = env->GetMethodID(clazz, "setTopHalCapabilities", "(IZ)V"); |
| method_setSignalTypeCapabilities = |
| env->GetMethodID(clazz, "setSignalTypeCapabilities", "(Ljava/util/List;)V"); |
| method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V"); |
| method_setGnssHardwareModelName = |
| env->GetMethodID(clazz, "setGnssHardwareModelName", "(Ljava/lang/String;)V"); |
| |
| method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V"); |
| method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V"); |
| method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V"); |
| |
| jclass arrayListClass = env->FindClass("java/util/ArrayList"); |
| class_arrayList = (jclass)env->NewGlobalRef(arrayListClass); |
| method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V"); |
| method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z"); |
| |
| jclass gnssSignalTypeClass = env->FindClass("android/location/GnssSignalType"); |
| class_gnssSignalType = (jclass)env->NewGlobalRef(gnssSignalTypeClass); |
| method_gnssSignalTypeCreate = |
| env->GetStaticMethodID(class_gnssSignalType, "create", |
| "(IDLjava/lang/String;)Landroid/location/GnssSignalType;"); |
| } |
| |
| Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) { |
| ALOGD("%s: %du\n", __func__, capabilities); |
| bool isAdrCapabilityKnown = (getInterfaceVersion() >= 3) ? true : false; |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities, |
| isAdrCapabilityKnown); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| namespace { |
| |
| jobject translateSingleSignalType(JNIEnv* env, const GnssSignalType& signalType) { |
| jstring jstringCodeType = env->NewStringUTF(signalType.codeType.c_str()); |
| jobject signalTypeObject = |
| env->CallStaticObjectMethod(class_gnssSignalType, method_gnssSignalTypeCreate, |
| signalType.constellation, signalType.carrierFrequencyHz, |
| jstringCodeType); |
| env->DeleteLocalRef(jstringCodeType); |
| return signalTypeObject; |
| } |
| |
| } // anonymous namespace |
| |
| Status GnssCallbackAidl::gnssSetSignalTypeCapabilitiesCb( |
| const std::vector<GnssSignalType>& signalTypes) { |
| ALOGD("%s: %d signal types", __func__, (int)signalTypes.size()); |
| JNIEnv* env = getJniEnv(); |
| jobject arrayList = env->NewObject(class_arrayList, method_arrayListCtor); |
| for (auto& signalType : signalTypes) { |
| jobject signalTypeObject = translateSingleSignalType(env, signalType); |
| env->CallBooleanMethod(arrayList, method_arrayListAdd, signalTypeObject); |
| // Delete Local Refs |
| env->DeleteLocalRef(signalTypeObject); |
| } |
| env->CallVoidMethod(mCallbacksObj, method_setSignalTypeCapabilities, arrayList); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| env->DeleteLocalRef(arrayList); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssStatusCb(const GnssStatusValue status) { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssSvStatusCb(const std::vector<GnssSvInfo>& svInfoList) { |
| GnssCallbackHidl::gnssSvStatusCbImpl<std::vector<GnssSvInfo>, GnssSvInfo>(svInfoList); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssLocationCb(const hardware::gnss::GnssLocation& location) { |
| GnssCallbackHidl::gnssLocationCbImpl<hardware::gnss::GnssLocation>(location); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) { |
| // In AIDL v1, if no listener is registered, do not report nmea to the framework. |
| if (getInterfaceVersion() <= 1) { |
| if (!isNmeaRegistered) { |
| return Status::ok(); |
| } |
| } |
| JNIEnv* env = getJniEnv(); |
| /* |
| * The Java code will call back to read these values. |
| * We do this to avoid creating unnecessary String objects. |
| */ |
| GnssCallbackHidl::sNmeaString = nmea.c_str(); |
| GnssCallbackHidl::sNmeaStringLength = nmea.size(); |
| |
| env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssAcquireWakelockCb() { |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssReleaseWakelockCb() { |
| release_wake_lock(WAKE_LOCK_NAME); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssSetSystemInfoCb(const GnssSystemInfo& info) { |
| ALOGD("%s: yearOfHw=%d, name=%s\n", __func__, info.yearOfHw, info.name.c_str()); |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); |
| jstring jstringName = env->NewStringUTF(info.name.c_str()); |
| env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); |
| if (jstringName) { |
| env->DeleteLocalRef(jstringName); |
| } |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssRequestTimeCb() { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| Status GnssCallbackAidl::gnssRequestLocationCb(const bool independentFromGnss, |
| const bool isUserEmergency) { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), |
| boolToJbool(isUserEmergency)); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Status::ok(); |
| } |
| |
| // Implementation of IGnssCallbackHidl |
| |
| Return<void> GnssCallbackHidl::gnssNameCb(const android::hardware::hidl_string& name) { |
| ALOGD("%s: name=%s\n", __func__, name.c_str()); |
| |
| JNIEnv* env = getJniEnv(); |
| jstring jstringName = env->NewStringUTF(name.c_str()); |
| env->CallVoidMethod(mCallbacksObj, method_setGnssHardwareModelName, jstringName); |
| if (jstringName) { |
| env->DeleteLocalRef(jstringName); |
| } |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| |
| return Void(); |
| } |
| |
| const char* GnssCallbackHidl::sNmeaString = nullptr; |
| size_t GnssCallbackHidl::sNmeaStringLength = 0; |
| |
| template <class T> |
| Return<void> GnssCallbackHidl::gnssLocationCbImpl(const T& location) { |
| JNIEnv* env = getJniEnv(); |
| |
| jobject jLocation = translateGnssLocation(env, location); |
| |
| env->CallVoidMethod(mCallbacksObj, method_reportLocation, boolToJbool(hasLatLong(location)), |
| jLocation); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| env->DeleteLocalRef(jLocation); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssLocationCb(const GnssLocation_V1_0& location) { |
| return gnssLocationCbImpl<GnssLocation_V1_0>(location); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssLocationCb_2_0(const GnssLocation_V2_0& location) { |
| return gnssLocationCbImpl<GnssLocation_V2_0>(location); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssStatusCb(const IGnssCallback_V2_0::GnssStatusValue status) { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| template <> |
| uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag( |
| const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svStatus) { |
| return SVID_FLAGS_HAS_BASEBAND_CN0; |
| } |
| |
| template <> |
| uint32_t GnssCallbackHidl::getHasBasebandCn0DbHzFlag( |
| const std::vector<IGnssCallbackAidl::GnssSvInfo>& svStatus) { |
| return SVID_FLAGS_HAS_BASEBAND_CN0; |
| } |
| |
| template <> |
| double GnssCallbackHidl::getBasebandCn0DbHz( |
| const std::vector<IGnssCallbackAidl::GnssSvInfo>& svInfoList, size_t i) { |
| return svInfoList[i].basebandCN0DbHz; |
| } |
| |
| template <> |
| double GnssCallbackHidl::getBasebandCn0DbHz( |
| const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { |
| return svInfoList[i].basebandCN0DbHz; |
| } |
| |
| template <> |
| uint32_t GnssCallbackHidl::getGnssSvInfoListSize(const IGnssCallback_V1_0::GnssSvStatus& svStatus) { |
| return svStatus.numSvs; |
| } |
| |
| template <> |
| uint32_t GnssCallbackHidl::getConstellationType(const IGnssCallback_V1_0::GnssSvStatus& svStatus, |
| size_t i) { |
| return static_cast<uint32_t>(svStatus.gnssSvList.data()[i].constellation); |
| } |
| |
| template <> |
| uint32_t GnssCallbackHidl::getConstellationType( |
| const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList, size_t i) { |
| return static_cast<uint32_t>(svInfoList[i].v2_0.constellation); |
| } |
| |
| template <class T_list, class T_sv_info> |
| Return<void> GnssCallbackHidl::gnssSvStatusCbImpl(const T_list& svStatus) { |
| // In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework. |
| if (!isSvStatusRegistered) { |
| return Void(); |
| } |
| |
| JNIEnv* env = getJniEnv(); |
| |
| uint32_t listSize = getGnssSvInfoListSize(svStatus); |
| |
| jintArray svidWithFlagArray = env->NewIntArray(listSize); |
| jfloatArray cn0Array = env->NewFloatArray(listSize); |
| jfloatArray elevArray = env->NewFloatArray(listSize); |
| jfloatArray azimArray = env->NewFloatArray(listSize); |
| jfloatArray carrierFreqArray = env->NewFloatArray(listSize); |
| jfloatArray basebandCn0Array = env->NewFloatArray(listSize); |
| |
| jint* svidWithFlags = env->GetIntArrayElements(svidWithFlagArray, 0); |
| jfloat* cn0s = env->GetFloatArrayElements(cn0Array, 0); |
| jfloat* elev = env->GetFloatArrayElements(elevArray, 0); |
| jfloat* azim = env->GetFloatArrayElements(azimArray, 0); |
| jfloat* carrierFreq = env->GetFloatArrayElements(carrierFreqArray, 0); |
| jfloat* basebandCn0s = env->GetFloatArrayElements(basebandCn0Array, 0); |
| |
| /* |
| * Read GNSS SV info. |
| */ |
| for (size_t i = 0; i < listSize; ++i) { |
| enum ShiftWidth : uint8_t { SVID_SHIFT_WIDTH = 12, CONSTELLATION_TYPE_SHIFT_WIDTH = 8 }; |
| |
| const T_sv_info& info = getGnssSvInfoOfIndex(svStatus, i); |
| svidWithFlags[i] = (info.svid << SVID_SHIFT_WIDTH) | |
| (getConstellationType(svStatus, i) << CONSTELLATION_TYPE_SHIFT_WIDTH) | |
| static_cast<uint32_t>(info.svFlag); |
| cn0s[i] = info.cN0Dbhz; |
| elev[i] = info.elevationDegrees; |
| azim[i] = info.azimuthDegrees; |
| carrierFreq[i] = info.carrierFrequencyHz; |
| svidWithFlags[i] |= getHasBasebandCn0DbHzFlag(svStatus); |
| basebandCn0s[i] = getBasebandCn0DbHz(svStatus, i); |
| } |
| |
| env->ReleaseIntArrayElements(svidWithFlagArray, svidWithFlags, 0); |
| env->ReleaseFloatArrayElements(cn0Array, cn0s, 0); |
| env->ReleaseFloatArrayElements(elevArray, elev, 0); |
| env->ReleaseFloatArrayElements(azimArray, azim, 0); |
| env->ReleaseFloatArrayElements(carrierFreqArray, carrierFreq, 0); |
| env->ReleaseFloatArrayElements(basebandCn0Array, basebandCn0s, 0); |
| |
| env->CallVoidMethod(mCallbacksObj, method_reportSvStatus, static_cast<jint>(listSize), |
| svidWithFlagArray, cn0Array, elevArray, azimArray, carrierFreqArray, |
| basebandCn0Array); |
| |
| env->DeleteLocalRef(svidWithFlagArray); |
| env->DeleteLocalRef(cn0Array); |
| env->DeleteLocalRef(elevArray); |
| env->DeleteLocalRef(azimArray); |
| env->DeleteLocalRef(carrierFreqArray); |
| env->DeleteLocalRef(basebandCn0Array); |
| |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssNmeaCb(int64_t timestamp, |
| const ::android::hardware::hidl_string& nmea) { |
| // In HIDL, if no listener is registered, do not report nmea to the framework. |
| if (!isNmeaRegistered) { |
| return Void(); |
| } |
| JNIEnv* env = getJniEnv(); |
| /* |
| * The Java code will call back to read these values. |
| * We do this to avoid creating unnecessary String objects. |
| */ |
| sNmeaString = nmea.c_str(); |
| sNmeaStringLength = nmea.size(); |
| |
| env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssSetCapabilitesCb(uint32_t capabilities) { |
| ALOGD("%s: %du\n", __func__, capabilities); |
| |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities, |
| /* isAdrCapabilityKnown= */ false); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_0(uint32_t capabilities) { |
| return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) { |
| return GnssCallbackHidl::gnssSetCapabilitesCb(capabilities); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssAcquireWakelockCb() { |
| acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssReleaseWakelockCb() { |
| release_wake_lock(WAKE_LOCK_NAME); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssRequestTimeCb() { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssRequestLocationCb(const bool independentFromGnss) { |
| return GnssCallbackHidl::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */ |
| false); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssRequestLocationCb_2_0(const bool independentFromGnss, |
| const bool isUserEmergency) { |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), |
| boolToJbool(isUserEmergency)); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| Return<void> GnssCallbackHidl::gnssSetSystemInfoCb(const IGnssCallback_V2_0::GnssSystemInfo& info) { |
| ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw); |
| |
| JNIEnv* env = getJniEnv(); |
| env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware, info.yearOfHw); |
| checkAndClearExceptionFromCallback(env, __FUNCTION__); |
| return Void(); |
| } |
| |
| } // namespace android::gnss |