1 /*
2  * Copyright 2024 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 <android/log.h>
18 #include <camera/NdkCameraError.h>
19 #include <camera/NdkCameraManager.h>
20 #include <camera/NdkCameraMetadata.h>
21 #include <jni.h>
22 #include <sys/types.h>
23 
24 #include <array>
25 #include <cstdint>
26 #include <functional>
27 #include <iterator>
28 #include <memory>
29 #include <mutex>
30 #include <string>
31 #include <thread>
32 #include <vector>
33 
34 #define TAG "VirtualCameraNdkTest"
35 #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
36 
37 namespace android {
38 namespace virtualdevice {
39 namespace cts {
40 
41 constexpr int kDefaultDeviceId = 0;
42 constexpr int kInfoDeviceIdTag = ACAMERA_INFO_START + 5;
43 static jclass gNativeCameraManagerClass = nullptr;
44 static jclass gAvailabilityCallbackClass = nullptr;
45 
toObjectArray(JNIEnv * env,const std::vector<std::string> & v)46 jobjectArray toObjectArray(JNIEnv* env, const std::vector<std::string>& v) {
47     jobjectArray array = env->NewObjectArray(v.size(), env->FindClass("java/lang/String"),
48                                              /*initialElement=*/nullptr);
49     for (int i = 0; i < v.size(); ++i) {
50         env->SetObjectArrayElement(array, i, env->NewStringUTF(v[i].c_str()));
51     }
52     return array;
53 }
54 
toNativeString(JNIEnv * env,jstring string)55 std::string toNativeString(JNIEnv* env, jstring string) {
56     const char* cstr = env->GetStringUTFChars(string, /*isCopy=*/nullptr);
57     std::string str(cstr);
58     env->ReleaseStringUTFChars(string, cstr);
59     return str;
60 }
61 
62 template <typename jniEnvCallable>
executeInJvm(JavaVM * jvm,jniEnvCallable callable)63 static void executeInJvm(JavaVM* jvm, jniEnvCallable callable) {
64     if (jvm == nullptr) {
65         LOGE("Cannot execute call in JVM callback, jvm is null");
66         return;
67     }
68 
69     JNIEnv* env = nullptr;
70     bool needsToBeDetached = false;
71     int status = jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
72     if (status == JNI_EDETACHED) {
73         // We are called from non-jni thread, we need to attach JNI env to call java
74         // and remember to detach it later.
75         int err = jvm->AttachCurrentThread(&env, /*thr_ags=*/nullptr);
76         if (err != 0 || env == nullptr) {
77             LOGE("Failed to attach native thread for java callback, error %d", err);
78             return;
79         }
80         needsToBeDetached = true;
81     } else if (status != JNI_OK) {
82         LOGE("Cannot get JNI Env -> error %d:", status);
83         return;
84     }
85 
86     callable(env);
87 
88     if (needsToBeDetached) {
89         jvm->DetachCurrentThread();
90     }
91 }
92 
93 class AvailabilityCallbackProxy {
94 public:
AvailabilityCallbackProxy(JNIEnv * env,jobject javaCallback)95     AvailabilityCallbackProxy(JNIEnv* env, jobject javaCallback) {
96         env->GetJavaVM(&mJvm);
97         mJavaCallback = env->NewGlobalRef(javaCallback);
98 
99         mCameraManagerCallbacks.context = reinterpret_cast<void*>(this);
100         mCameraManagerCallbacks.onCameraAvailable =
101                 AvailabilityCallbackProxy::onCameraAvailableCallback;
102         mCameraManagerCallbacks.onCameraUnavailable =
103                 AvailabilityCallbackProxy::onCameraUnavailableCallback;
104     }
105 
~AvailabilityCallbackProxy()106     ~AvailabilityCallbackProxy() {
107         std::lock_guard<std::mutex> lock(mLock);
108         if (mJavaCallback == nullptr) {
109             return;
110         }
111         executeInJvm(mJvm, [this](JNIEnv* env) { env->DeleteGlobalRef(mJavaCallback); });
112     }
113 
getCallbacksPtr() const114     const ACameraManager_AvailabilityCallbacks* getCallbacksPtr() const {
115         return &mCameraManagerCallbacks;
116     }
117 
118 private:
invokeCallback(const std::string & cameraId,const bool available) const119     void invokeCallback(const std::string& cameraId, const bool available) const {
120         std::lock_guard<std::mutex> lock(mLock);
121         if (mJavaCallback == nullptr) {
122             return;
123         }
124 
125         executeInJvm(mJvm, [&cameraId, available, this](JNIEnv* env) {
126             jmethodID method =
127                     env->GetMethodID(gAvailabilityCallbackClass,
128                                      available ? "onCameraAvailable" : "onCameraUnavailable",
129                                      "(Ljava/lang/String;)V");
130             env->CallVoidMethod(mJavaCallback, method, env->NewStringUTF(cameraId.c_str()));
131         });
132     }
133 
onCameraAvailableCallback(void * availabilityCallbackProxy,const char * cameraId)134     static void onCameraAvailableCallback(void* availabilityCallbackProxy, const char* cameraId) {
135         auto proxy = reinterpret_cast<AvailabilityCallbackProxy*>(availabilityCallbackProxy);
136         proxy->invokeCallback(cameraId, /*available=*/true);
137     }
138 
onCameraUnavailableCallback(void * availabilityCallbackProxy,const char * cameraId)139     static void onCameraUnavailableCallback(void* availabilityCallbackProxy, const char* cameraId) {
140         auto proxy = reinterpret_cast<AvailabilityCallbackProxy*>(availabilityCallbackProxy);
141         proxy->invokeCallback(cameraId, /*available=*/false);
142     }
143 
144     mutable std::mutex mLock;
145     JavaVM* mJvm;
146     jobject mJavaCallback;
147     ACameraManager_AvailabilityCallbacks mCameraManagerCallbacks;
148 };
149 
150 class CameraManager {
151 public:
CameraManager()152     CameraManager() : mCameraManager(ACameraManager_create(), ACameraManager_delete){};
153 
~CameraManager()154     ~CameraManager() {
155         std::lock_guard<std::mutex> lock(mLock);
156         for (int i = 0; i < mAvailabilityCallbacks.size(); i++) {
157             ACameraManager_unregisterAvailabilityCallback(mCameraManager.get(),
158                                                           mAvailabilityCallbacks[i]
159                                                                   ->getCallbacksPtr());
160         }
161     }
162 
getCameraIds() const163     std::vector<std::string> getCameraIds() const {
164         ACameraIdList* cameraIdList;
165         camera_status_t status =
166                 ACameraManager_getCameraIdList(mCameraManager.get(), &cameraIdList);
167         if (status != ACAMERA_OK || cameraIdList == nullptr) {
168             return {};
169         }
170 
171         std::vector<std::string> cameraIds;
172         cameraIds.reserve(cameraIdList->numCameras);
173         std::copy(cameraIdList->cameraIds, cameraIdList->cameraIds + cameraIdList->numCameras,
174                   std::back_inserter(cameraIds));
175         ACameraManager_deleteCameraIdList(cameraIdList);
176         return cameraIds;
177     }
178 
getCameraCharacteristics(const std::string & cameraId) const179     std::unique_ptr<ACameraMetadata, void (*)(ACameraMetadata*)> getCameraCharacteristics(
180             const std::string& cameraId) const {
181         ACameraMetadata* characteristics = nullptr;
182         ACameraManager_getCameraCharacteristics(mCameraManager.get(), cameraId.c_str(),
183                                                 &characteristics);
184         return {characteristics, ACameraMetadata_free};
185     }
186 
getCameraDeviceId(const std::string & cameraId) const187     int getCameraDeviceId(const std::string& cameraId) const {
188         auto characteristics = getCameraCharacteristics(cameraId);
189         if (characteristics == nullptr) {
190             return kDefaultDeviceId;
191         }
192 
193         ACameraMetadata_const_entry entry;
194         camera_status_t status =
195                 ACameraMetadata_getConstEntry(characteristics.get(), kInfoDeviceIdTag, &entry);
196         if (status != ACAMERA_OK) {
197             return kDefaultDeviceId;
198         }
199         return entry.data.i32[0];
200     }
201 
registerAvailabilityCallback(std::unique_ptr<AvailabilityCallbackProxy> callbackProxy)202     void registerAvailabilityCallback(std::unique_ptr<AvailabilityCallbackProxy> callbackProxy) {
203         ACameraManager_registerAvailabilityCallback(mCameraManager.get(),
204                                                     callbackProxy->getCallbacksPtr());
205         std::lock_guard<std::mutex> lock(mLock);
206         mAvailabilityCallbacks.emplace_back(std::move(callbackProxy));
207     }
208 
209 private:
210     const std::unique_ptr<ACameraManager, void (*)(ACameraManager*)> mCameraManager;
211 
212     std::mutex mLock;
213     std::vector<std::unique_ptr<AvailabilityCallbackProxy>> mAvailabilityCallbacks;
214 };
215 
getCameraManager()216 std::unique_ptr<ACameraManager, void (*)(ACameraManager*)> getCameraManager() {
217     return {ACameraManager_create(), ACameraManager_delete};
218 }
219 
createCameraManager(JNIEnv *,jclass)220 jlong createCameraManager(JNIEnv*, jclass) {
221     return reinterpret_cast<jlong>(new CameraManager);
222 }
223 
releaseCameraManager(JNIEnv *,jclass,jlong cameraManagerPtr)224 void releaseCameraManager(JNIEnv*, jclass, jlong cameraManagerPtr) {
225     CameraManager* ptr = reinterpret_cast<CameraManager*>(cameraManagerPtr);
226     if (ptr != nullptr) {
227         delete ptr;
228     }
229 }
230 
getCameraIds(JNIEnv * env,jclass,jlong cameraManagerPtr)231 jobjectArray getCameraIds(JNIEnv* env, jclass, jlong cameraManagerPtr) {
232     CameraManager* ptr = reinterpret_cast<CameraManager*>(cameraManagerPtr);
233     if (ptr == nullptr) {
234         return nullptr;
235     }
236     return toObjectArray(env, ptr->getCameraIds());
237 }
238 
getDeviceId(JNIEnv * env,jclass,jlong cameraManagerPtr,jstring cameraId)239 jint getDeviceId(JNIEnv* env, jclass, jlong cameraManagerPtr, jstring cameraId) {
240     CameraManager* ptr = reinterpret_cast<CameraManager*>(cameraManagerPtr);
241     if (ptr == nullptr) {
242         return kDefaultDeviceId;
243     }
244     return ptr->getCameraDeviceId(toNativeString(env, cameraId));
245 }
246 
registerAvailabilityCallback(JNIEnv * env,jclass,jlong cameraManagerPtr,jobject availabilityCallback)247 void registerAvailabilityCallback(JNIEnv* env, jclass, jlong cameraManagerPtr,
248                                   jobject availabilityCallback) {
249     CameraManager* ptr = reinterpret_cast<CameraManager*>(cameraManagerPtr);
250     if (ptr == nullptr || availabilityCallback == nullptr) {
251         return;
252     }
253 
254     ptr->registerAvailabilityCallback(
255             std::make_unique<AvailabilityCallbackProxy>(env, availabilityCallback));
256 }
257 
258 // ------------------------------------------------------------------------------------------------
259 
260 static std::array<JNINativeMethod, 5>
261         gMethods{JNINativeMethod{"createCameraManager", "()J", (void*)createCameraManager},
262                  JNINativeMethod{"releaseCameraManager", "(J)V", (void*)releaseCameraManager},
263                  JNINativeMethod{"getCameraIds", "(J)[Ljava/lang/String;", (void*)getCameraIds},
264                  JNINativeMethod{"getDeviceId", "(JLjava/lang/String;)I", (void*)getDeviceId},
265                  JNINativeMethod{"registerAvailabilityCallback",
266                                  "(JLandroid/virtualdevice/cts/camera/util/"
267                                  "NativeCameraManager$AvailabilityCallback;)V",
268                                  (void*)registerAvailabilityCallback}};
269 
register_android_virtualdevice_cts_camera_util_NativeCameraTestActivity(JNIEnv * env)270 int register_android_virtualdevice_cts_camera_util_NativeCameraTestActivity(JNIEnv* env) {
271     jclass clazz = env->FindClass("android/virtualdevice/cts/camera/util/NativeCameraManager");
272     gNativeCameraManagerClass = reinterpret_cast<jclass>(env->NewGlobalRef(clazz));
273 
274     gAvailabilityCallbackClass = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(
275             "android/virtualdevice/cts/camera/util/NativeCameraManager$AvailabilityCallback")));
276     return env->RegisterNatives(clazz, gMethods.data(), gMethods.size());
277     return 0;
278 }
279 
280 } // namespace cts
281 } // namespace virtualdevice
282 } // namespace android
283