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