1 /*
2  * Copyright 2021 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 #include "EvsServiceContext.h"
18 #ifdef __TEST__
19 #include "MockEvsServiceFactory.h"
20 #endif
21 #include <android-base/logging.h>
22 #include <nativehelper/JNIHelp.h>
23 
24 #include <jni.h>
25 
26 namespace {
27 
28 using ::android::automotive::evs::EvsServiceContext;
29 #ifdef __TEST__
30 using ::android::automotive::evs::MockEvsServiceFactory;
31 using ::android::automotive::evs::MockLinkUnlinkToDeath;
32 #endif
33 
34 // EvsHalWrapperImpl class
35 constexpr const char kCarEvsServiceClassName[] = "com/android/car/evs/EvsHalWrapperImpl";
36 
37 /*
38  * Connects to the Extended View System service
39  */
connectToHalServiceIfNecessary(JNIEnv * env,jobject thiz,jlong handle)40 jboolean connectToHalServiceIfNecessary(JNIEnv* env, jobject thiz, jlong handle) {
41     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
42     if (!ctxt) {
43         LOG(ERROR) << "The service context is invalid.";
44         return JNI_FALSE;
45     }
46 
47     if (ctxt->isAvailable()) {
48         LOG(DEBUG) << "Service is connected already.";
49         return JNI_TRUE;
50     }
51 
52     LOG(DEBUG) << "Connecting to EVS service";
53 
54     // Initializes a new service context with a death handler
55     if (!ctxt->initialize(env, thiz)) {
56         LOG(ERROR) << "Failed to initialize a service context";
57         return JNI_FALSE;
58     }
59 
60     return JNI_TRUE;
61 }
62 
63 /*
64  * Disconnects from the Extended View System service
65  */
disconnectFromHalService(JNIEnv *,jobject,jlong handle)66 void disconnectFromHalService(JNIEnv*, jobject, jlong handle) {
67     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
68     if (ctxt == nullptr || !ctxt->isAvailable()) {
69         LOG(DEBUG) << "Ignores a disconnecting service request with an invalid handle.";
70         return;
71     }
72 
73     // We simply delete a service handle.
74     ctxt->deinitialize();
75 }
76 
77 /*
78  * Returns a consumed frame buffer to EVS service
79  */
returnFrameBuffer(JNIEnv *,jobject,jlong handle,jint bufferId)80 void returnFrameBuffer(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle, jint bufferId) {
81     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
82     if (!ctxt) {
83         LOG(ERROR) << __FUNCTION__ << ": EVS service context is not available.";
84         return;
85     }
86 
87     ctxt->doneWithFrame(bufferId);
88 }
89 
90 /*
91  * Open the target camera device for the service
92  */
openCamera(JNIEnv * env,jobject,jlong handle,jstring cameraId)93 jboolean openCamera(JNIEnv* env, jobject /*thiz*/, jlong handle, jstring cameraId) {
94     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
95     if (!ctxt) {
96         LOG(ERROR) << __FUNCTION__ << ": EVS service context is not available.";
97         return JNI_FALSE;
98     }
99 
100     // Attempts to open the target camera device
101     const char* id = env->GetStringUTFChars(cameraId, NULL);
102     if (!id || !ctxt->openCamera(id)) {
103         LOG(ERROR) << "Failed to open a camera device";
104         return JNI_FALSE;
105     }
106 
107     env->ReleaseStringUTFChars(cameraId, id);
108     return JNI_TRUE;
109 }
110 
111 /*
112  * Close the target camera device
113  */
closeCamera(JNIEnv *,jobject,jlong handle)114 void closeCamera(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) {
115     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
116     if (!ctxt) {
117         LOG(WARNING) << __FUNCTION__ << ": EVS service context is not available.";
118     }
119 
120     ctxt->closeCamera();
121 }
122 
123 /*
124  * Request to start a video stream
125  */
startVideoStream(JNIEnv *,jobject,jlong handle)126 jboolean startVideoStream(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) {
127     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
128     if (!ctxt) {
129         LOG(ERROR) << __FUNCTION__ << ": EVS service context is not available.";
130         return JNI_FALSE;
131     }
132 
133     return ctxt->startVideoStream() ? JNI_TRUE : JNI_FALSE;
134 }
135 
136 /*
137  * Request to stop a video stream
138  */
stopVideoStream(JNIEnv *,jobject,jlong handle)139 void stopVideoStream(JNIEnv* /*env*/, jobject /*thiz*/, jlong handle) {
140     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
141     if (!ctxt) {
142         LOG(WARNING) << __FUNCTION__ << ": EVS service context is not available.";
143         return;
144     }
145 
146     ctxt->stopVideoStream();
147 }
148 
149 /*
150  * Static method to create the service context
151  */
createServiceHandle(JNIEnv * env,jclass clazz)152 jlong createServiceHandle(JNIEnv* env, jclass clazz) {
153     JavaVM* vm = nullptr;
154     env->GetJavaVM(&vm);
155     if (vm == nullptr) {
156         jniThrowException(env, "java/lang/IllegalStateException",
157                           "Can't initialize the EvsServiceContext because the JavaVM is invalid");
158     }
159 
160     return reinterpret_cast<jlong>(EvsServiceContext::create(vm, clazz));
161 }
162 
createServiceHandleForTest(JNIEnv * env,jclass clazz)163 jlong createServiceHandleForTest([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz) {
164 #ifdef __TEST__
165     JavaVM* vm = nullptr;
166     env->GetJavaVM(&vm);
167     if (vm == nullptr) {
168         jniThrowException(env, "java/lang/IllegalStateException",
169                           "Can't initialize the EvsServiceContext because the JavaVM is invalid");
170     }
171 
172     return reinterpret_cast<jlong>(
173             EvsServiceContext::create(vm, clazz, std::make_unique<MockEvsServiceFactory>(),
174                                       std::make_unique<MockLinkUnlinkToDeath>()));
175 #else
176     return 0L;
177 #endif
178 }
179 
triggerBinderDied(JNIEnv * env,jobject thiz,jlong handle)180 void triggerBinderDied([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jobject thiz,
181                        [[maybe_unused]] jlong handle) {
182 #ifdef __TEST__
183     EvsServiceContext* ctxt = reinterpret_cast<EvsServiceContext*>(handle);
184     if (!ctxt) {
185         LOG(WARNING) << __FUNCTION__ << ": EVS service context is not available.";
186         return;
187     }
188 
189     ctxt->triggerBinderDied();
190 #endif
191 }
192 
193 /*
194  * Static method to destroy the service context
195  */
destroyServiceHandle(JNIEnv *,jclass,jlong handle)196 void destroyServiceHandle(JNIEnv* /*env*/, jclass /*clazz*/, jlong handle) {
197     delete reinterpret_cast<EvsServiceContext*>(handle);
198 }
199 
200 }  // namespace
201 
202 namespace android {
203 
initializeCarEvsService(JavaVM * vm)204 jint initializeCarEvsService(JavaVM* vm) {
205     JNIEnv* env;
206     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
207         LOG(ERROR) << __FUNCTION__ << ": Failed to get the environment.";
208         return JNI_ERR;
209     }
210 
211     // Registers native methods
212     static const JNINativeMethod methods[] = {
213             {"nativeConnectToHalServiceIfNecessary", "(J)Z",
214              reinterpret_cast<void*>(connectToHalServiceIfNecessary)},
215             {"nativeDisconnectFromHalService", "(J)V",
216              reinterpret_cast<void*>(disconnectFromHalService)},
217             {"nativeOpenCamera", "(JLjava/lang/String;)Z", reinterpret_cast<void*>(openCamera)},
218             {"nativeCloseCamera", "(J)V", reinterpret_cast<void*>(closeCamera)},
219             {"nativeRequestToStartVideoStream", "(J)Z", reinterpret_cast<void*>(startVideoStream)},
220             {"nativeRequestToStopVideoStream", "(J)V", reinterpret_cast<void*>(stopVideoStream)},
221             {"nativeDoneWithFrame", "(JI)V", reinterpret_cast<void*>(returnFrameBuffer)},
222             {"nativeTriggerBinderDied", "(J)V", reinterpret_cast<void*>(triggerBinderDied)},
223             {"nativeCreateServiceHandle", "()J", reinterpret_cast<void*>(createServiceHandle)},
224             {"nativeCreateServiceHandleForTest", "()J",
225              reinterpret_cast<void*>(createServiceHandleForTest)},
226             {"nativeDestroyServiceHandle", "(J)V", reinterpret_cast<void*>(destroyServiceHandle)},
227     };
228     jniRegisterNativeMethods(env, kCarEvsServiceClassName, methods, NELEM(methods));
229 
230     return JNI_VERSION_1_6;
231 }
232 
233 }  // namespace android
234