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