1 /*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #pragma once
18
19 #include <jni.h>
20
21 #include <mutex>
22
23 #include <android_runtime/AndroidRuntime.h>
24 #include <log/log_main.h>
25 #include <utils/RefBase.h>
26
27 namespace android {
28
29 // TODO (b/218351957) remove static lock
30 // This lock is intentionally static, it is local to each TU which is
31 // responsible for a single Java class.
32 static std::mutex sFieldSpLock;
33
34 // getFieldSp and setFieldSp are used to logically represent an owning reference
35 // to a native object from across the JNI. The data (underlying ptr) is stored
36 // in a field in the Java object as a long. Setting a Java field to a sp
37 // involves incrementing the reference count to prevent object destruction.
38 // Resetting the field decrements the reference count to avoid memory leaks.
39
40 template <typename T>
getFieldSp(JNIEnv * env,jobject thiz,jfieldID id)41 sp<T> getFieldSp(JNIEnv* env, jobject thiz, jfieldID id) {
42 const std::lock_guard l{sFieldSpLock};
43 return sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id)));
44 }
45
46 template <typename T>
setFieldSp(JNIEnv * env,jobject thiz,const sp<T> & newSp,jfieldID id)47 sp<T> setFieldSp(JNIEnv* env, jobject thiz, const sp<T>& newSp, jfieldID id) {
48 const std::lock_guard l{sFieldSpLock};
49 sp<T> old = sp<T>::fromExisting(reinterpret_cast<T*>(env->GetLongField(thiz, id)));
50 if (newSp) {
51 newSp->incStrong((void*)setFieldSp<T>);
52 }
53 if (old) {
54 old->decStrong((void*)setFieldSp<T>);
55 }
56 env->SetLongField(thiz, id, (jlong)newSp.get());
57 return old;
58 }
59
getJNIEnvOrDie()60 inline JNIEnv* getJNIEnvOrDie() {
61 const auto env = AndroidRuntime::getJNIEnv();
62 LOG_ALWAYS_FATAL_IF(env == nullptr,
63 "Thread JNI reference is null. Thread not prepared for Java.");
64 return env;
65 }
66
67 class GlobalRef {
68 public:
GlobalRef(jobject object)69 GlobalRef(jobject object) : GlobalRef(object, AndroidRuntime::getJNIEnv()) {}
70
GlobalRef(jobject object,JNIEnv * env)71 GlobalRef(jobject object, JNIEnv* env) {
72 LOG_ALWAYS_FATAL_IF(env == nullptr, "Invalid JNIEnv when attempting to create a GlobalRef");
73 mGlobalRef = env->NewGlobalRef(object);
74 LOG_ALWAYS_FATAL_IF(env->IsSameObject(object, nullptr) == JNI_TRUE,
75 "Creating GlobalRef from null object");
76 }
77
GlobalRef(const GlobalRef & other)78 GlobalRef(const GlobalRef& other) : GlobalRef(other.mGlobalRef) {}
79
GlobalRef(GlobalRef && other)80 GlobalRef(GlobalRef&& other) : mGlobalRef(other.mGlobalRef) { other.mGlobalRef = nullptr; }
81
82 // Logically const
83 GlobalRef& operator=(const GlobalRef& other) = delete;
84
85 GlobalRef& operator=(GlobalRef&& other) = delete;
86
~GlobalRef()87 ~GlobalRef() {
88 if (mGlobalRef == nullptr) return; // No reference to decrement
89 getJNIEnvOrDie()->DeleteGlobalRef(mGlobalRef);
90 }
91
92 // Valid as long as this wrapper is in scope.
get()93 jobject get() const {
94 return mGlobalRef;
95 }
96
97 private:
98 // Logically const. Not actually const so we can move from GlobalRef
99 jobject mGlobalRef;
100 };
101
102 } // namespace android
103