JNI attach/detach threads executing GNSS HAL callback methods
Bug: 31974439
Test: Verified that threads that are not attached to JNI
gets attached and on exit, gets detached.
Change-Id: Ib089720998131ceb107fa5b318a0fcbdf91076ec
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 09886db..5046265 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -59,6 +59,12 @@
static jmethodID method_reportMeasurementData;
static jmethodID method_reportNavigationMessages;
+/*
+ * Save a pointer to JavaVm to attach/detach threads executing
+ * callback methods that need to make JNI calls.
+ */
+static JavaVM* sJvm;
+
using android::OK;
using android::sp;
using android::status_t;
@@ -216,6 +222,62 @@
}
}
+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<ScopedJniThreadAttach> 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;
+}
+
/*
* GnssCallback class implements the callback methods for IGnss interface.
*/
@@ -247,7 +309,7 @@
Return<void> GnssCallback::gnssLocationCb(
const ::android::hardware::gnss::V1_0::GnssLocation& location) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportLocation,
location.gnssLocationFlags,
@@ -263,14 +325,14 @@
}
Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_reportStatus, status);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
}
Return<void> GnssCallback::gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
sGnssSvListSize = svStatus.numSvs;
if (sGnssSvListSize > static_cast<uint32_t>(
@@ -292,7 +354,7 @@
Return<void> GnssCallback::gnssNmeaCb(
int64_t timestamp, const ::android::hardware::hidl_string& nmea) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
/*
* The Java code will call back to read these values.
* We do this to avoid creating unnecessary String objects.
@@ -308,7 +370,7 @@
Return<void> GnssCallback::gnssSetCapabilitesCb(uint32_t capabilities) {
ALOGD("%s: %du\n", __func__, capabilities);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -325,7 +387,7 @@
}
Return<void> GnssCallback::gnssRequestTimeCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -334,7 +396,7 @@
Return<void> GnssCallback::gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) {
ALOGD("%s: yearOfHw=%d\n", __func__, info.yearOfHw);
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_setGnssYearOfHardware,
info.yearOfHw);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -350,7 +412,7 @@
* interface.
*/
Return<void> GnssXtraCallback::downloadRequestCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -385,7 +447,7 @@
const android::hardware::gnss::V1_0::GnssLocation& location,
GeofenceTransition transition,
hardware::gnss::V1_0::GnssUtcTime timestamp) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportGeofenceTransition,
@@ -408,7 +470,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(
GeofenceAvailability status,
const android::hardware::gnss::V1_0::GnssLocation& location) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj,
method_reportGeofenceStatus,
status,
@@ -426,7 +488,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
}
@@ -441,7 +503,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
}
@@ -455,7 +517,7 @@
Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
}
@@ -469,7 +531,7 @@
Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId,
GeofenceStatus status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
if (status != IGnssGeofenceCallback::GeofenceStatus::OPERATION_SUCCESS) {
ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
}
@@ -496,7 +558,7 @@
Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb(
const IGnssNavigationMessageCallback::GnssNavigationMessage& message) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
size_t dataLength = message.data.size();
@@ -545,7 +607,7 @@
Return<void> GnssMeasurementCallback::GnssMeasurementCb(
const IGnssMeasurementCallback::GnssData& data) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jobject clock;
jobjectArray measurementArray;
@@ -700,7 +762,7 @@
Return<void> GnssNiCallback::niNotifyCb(
const IGnssNiCallback::GnssNiNotification& notification) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jstring requestorId = env->NewStringUTF(notification.requestorId.c_str());
jstring text = env->NewStringUTF(notification.notificationMessage.c_str());
@@ -742,7 +804,7 @@
Return<void> AGnssCallback::agnssStatusIpV6Cb(
const IAGnssCallback::AGnssStatusIpV6& agps_status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = NULL;
bool isSupported = false;
@@ -778,7 +840,7 @@
Return<void> AGnssCallback::agnssStatusIpV4Cb(
const IAGnssCallback::AGnssStatusIpV4& agps_status) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = NULL;
uint32_t ipAddr = agps_status.ipV4Addr;
@@ -813,7 +875,7 @@
return NULL;
}
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
jbyteArray byteArray = env->NewByteArray(4);
if (byteArray == NULL) {
ALOGE("Unable to allocate byte array for IPv4 address");
@@ -837,14 +899,14 @@
};
Return<void> AGnssRilCallback::requestSetIdCb(IAGnssRilCallback::ID setIdFlag) {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestSetID, setIdFlag);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
}
Return<void> AGnssRilCallback::requestRefLocCb() {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
+ JNIEnv* env = getJniEnv();
env->CallVoidMethod(mCallbacksObj, method_requestRefLocation);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return Void();
@@ -885,6 +947,14 @@
"reportNavigationMessage",
"(Landroid/location/GnssNavigationMessage;)V");
+ /*
+ * Save a pointer to JVM.
+ */
+ jint jvmStatus = env->GetJavaVM(&sJvm);
+ if (jvmStatus != JNI_OK) {
+ LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
+ }
+
// TODO(b/31632518)
gnssHal = IGnss::getService("gnss");
if (gnssHal != nullptr) {