1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "jni_helper.h"
17 #include "ipc_debug.h"
18 #include "jni_help.h"
19 #include "ohos_rpc_remote_object.h"
20 #include "log_tags.h"
21 
22 namespace OHOS {
23 using namespace OHOS::HiviewDFX;
24 
25 struct JFileDescriptor {
26     jclass klazz;
27     jmethodID fileDescriptorCtor;
28     jfieldID descriptorField;
29 } g_jFileDescriptor;
30 
31 static constexpr HiLogLabel LABEL = { LOG_CORE, LOG_ID_IPC_OTHER, "IPCJniHelper" };
32 
33 JavaVM *JNIEnvHelper::javaVm_ = nullptr;
34 // The JavaVM is a representation of the virtual machine on the JNI layer,
35 // one process has only one JavaVM, and all the threads share a JavaVM.
36 // JNIEnv is in effect only on the thread that it is created,
37 // cannot be passed across threads, different threads are independent of each other.
38 // In order to implement cross-threading calls, we need to transform between JNIENV and JavaVM
JNIEnvHelper()39 JNIEnvHelper::JNIEnvHelper() : env_ { nullptr }, nativeThread_ { false }
40 {
41     if (javaVm_->GetEnv(reinterpret_cast<void **>(&env_), JNI_VERSION_1_4) == JNI_EDETACHED) {
42         javaVm_->AttachCurrentThread(reinterpret_cast<void **>(&env_), nullptr);
43         nativeThread_ = true;
44     }
45 }
46 
nativeInit(JavaVM * vm)47 void JNIEnvHelper::nativeInit(JavaVM *vm)
48 {
49     if (javaVm_ != nullptr) {
50         ZLOGE(LABEL, "Failed to init vm, javaVm_ has been initialized");
51         return;
52     }
53 
54     javaVm_ = vm;
55 }
56 
~JNIEnvHelper()57 JNIEnvHelper::~JNIEnvHelper()
58 {
59     if (nativeThread_) {
60         javaVm_->DetachCurrentThread();
61     }
62 }
63 
Get()64 JNIEnv *JNIEnvHelper::Get()
65 {
66     return env_;
67 }
68 
operator ->()69 JNIEnv *JNIEnvHelper::operator->()
70 {
71     return env_;
72 }
73 
JniHelperThrowException(JNIEnv * env,const char * className,const char * msg)74 void JniHelperThrowException(JNIEnv *env, const char *className, const char *msg)
75 {
76     jclass clazz = env->FindClass(className);
77     if (!clazz) {
78         ZLOGE(LABEL, "Unable to find exception class:%{public}s", className);
79         /* ClassNotFoundException now pending */
80         return;
81     }
82 
83     if (env->ThrowNew(clazz, msg) != JNI_OK) {
84         ZLOGE(LABEL, "Failed throwing className:%{public}s msg:%{public}s", className, msg);
85         /* an exception, most likely OOM, will now be pending */
86     }
87 
88     env->DeleteLocalRef(clazz);
89 }
90 
JniHelperThrowNullPointerException(JNIEnv * env,const char * msg)91 void JniHelperThrowNullPointerException(JNIEnv *env, const char *msg)
92 {
93     JniHelperThrowException(env, "java/lang/NullPointerException", msg);
94 }
95 
JniHelperThrowIllegalStateException(JNIEnv * env,const char * msg)96 void JniHelperThrowIllegalStateException(JNIEnv *env, const char *msg)
97 {
98     JniHelperThrowException(env, "java/lang/IllegalStateException", msg);
99 }
100 
101 /*
102  * Get an int file descriptor from a java.io.FileDescriptor
103  */
JniHelperJavaIoGetFdFromFileDescriptor(JNIEnv * env,jobject fileDescriptor)104 int JniHelperJavaIoGetFdFromFileDescriptor(JNIEnv *env, jobject fileDescriptor)
105 {
106     return env->GetIntField(fileDescriptor, g_jFileDescriptor.descriptorField);
107 }
108 
109 /*
110  * Set the descriptor of a java.io.FileDescriptor
111  */
JniHelperJavaIoSetFdToFileDescriptor(JNIEnv * env,jobject fileDescriptor,int value)112 void JniHelperJavaIoSetFdToFileDescriptor(JNIEnv *env, jobject fileDescriptor, int value)
113 {
114     env->SetIntField(fileDescriptor, g_jFileDescriptor.descriptorField, value);
115 }
116 
117 /*
118  * JNI may throws exception when native code call java scenario.
119  * we should convert local exception to native status code.
120  * Check and clear local exception.
121  */
JniHelperCheckAndClearLocalException(JNIEnv * env)122 jboolean JniHelperCheckAndClearLocalException(JNIEnv *env)
123 {
124     jthrowable exception = env->ExceptionOccurred();
125     if (exception != nullptr) {
126         ZLOGE(LABEL, "clean up JNI local ref");
127         // clean up JNI local ref -- we don't return to Java code
128         env->ExceptionDescribe(); // for debug perpose.
129         env->ExceptionClear();
130         env->DeleteLocalRef(exception);
131         return JNI_TRUE;
132     }
133 
134     return JNI_FALSE;
135 }
136 
137 /*
138  * Create a java.io.FileDescriptor given an integer fd
139  */
JniHelperJavaIoCreateFileDescriptor(JNIEnv * env,int fd)140 jobject JniHelperJavaIoCreateFileDescriptor(JNIEnv *env, int fd)
141 {
142     jobject descriptor = env->NewObject(g_jFileDescriptor.klazz, g_jFileDescriptor.fileDescriptorCtor);
143     JniHelperJavaIoSetFdToFileDescriptor(env, descriptor, fd);
144     return descriptor;
145 }
146 
JniHelperRegisterNativeMethods(JNIEnv * env)147 int JniHelperRegisterNativeMethods(JNIEnv *env)
148 {
149     if (env == nullptr) {
150         ZLOGE(LABEL, "env is null");
151         return JNI_ERR;
152     }
153     g_jFileDescriptor.klazz = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
154     if (g_jFileDescriptor.klazz == nullptr) {
155         return JNI_ERR;
156     }
157 
158     g_jFileDescriptor.fileDescriptorCtor = env->GetMethodID(g_jFileDescriptor.klazz, "<init>", "()V");
159     if (g_jFileDescriptor.fileDescriptorCtor == nullptr) {
160         env->DeleteGlobalRef(g_jFileDescriptor.klazz);
161         return JNI_ERR;
162     }
163 
164     g_jFileDescriptor.descriptorField = env->GetFieldID(g_jFileDescriptor.klazz, "descriptor", "I");
165     if (g_jFileDescriptor.descriptorField == nullptr) {
166         env->DeleteGlobalRef(g_jFileDescriptor.klazz);
167         return JNI_ERR;
168     }
169 
170     return JNI_OK;
171 }
172 
JNIHelperGetJavaRemoteObject(JNIEnv * env,const sptr<IRemoteObject> & target)173 jobject JNIHelperGetJavaRemoteObject(JNIEnv *env, const sptr<IRemoteObject> &target)
174 {
175     if (env == nullptr) {
176         return nullptr;
177     }
178     return Java_ohos_rpc_getJavaRemoteObject(env, target);
179 }
180 } // namespace OHOS
181