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