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 <aidl/android/hardware/automotive/evs/EvsResult.h>
20 #include <aidl/android/hardware/common/NativeHandle.h>
21 #include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
22 #include <aidl/android/hardware/graphics/common/PixelFormat.h>
23 #include <android-base/logging.h>
24 #include <android-base/scopeguard.h>
25 #include <android/binder_ibinder.h>
26 #include <android/binder_manager.h>
27 #include <android/hardware_buffer_jni.h> // for AHardwareBuffer_toHardwareBuffer
28 #include <cutils/native_handle.h>
29 #include <nativehelper/JNIHelp.h>
30 #include <vndk/hardware_buffer.h> // for AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE
31
32 namespace {
33
34 using ::aidl::android::hardware::automotive::evs::BufferDesc;
35 using ::aidl::android::hardware::automotive::evs::CameraDesc;
36 using ::aidl::android::hardware::automotive::evs::EvsEventDesc;
37 using ::aidl::android::hardware::automotive::evs::EvsResult;
38 using ::aidl::android::hardware::automotive::evs::IEvsCamera;
39 using ::aidl::android::hardware::automotive::evs::IEvsEnumerator;
40 using ::aidl::android::hardware::automotive::evs::Stream;
41 using ::aidl::android::hardware::automotive::evs::StreamType;
42 using ::aidl::android::hardware::common::NativeHandle;
43 using ::aidl::android::hardware::graphics::common::HardwareBuffer;
44 using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
45
getMethodIDOrDie(JNIEnv * env,jclass clazz,const char * name,const char * signature)46 jmethodID getMethodIDOrDie(JNIEnv* env, jclass clazz, const char* name, const char* signature) {
47 jmethodID res = env->GetMethodID(clazz, name, signature);
48 if (res == nullptr) {
49 LOG(FATAL) << "Unable to find method " << name << " with signature = " << signature;
50 }
51
52 return res;
53 }
54
selectStreamConfiguration(const std::vector<Stream> & list)55 Stream selectStreamConfiguration(const std::vector<Stream>& list) {
56 for (const auto& cfg : list) {
57 // TODO(b/223905367): this logic simply selects the first output stream
58 // configuration that generates RGBA8888 data stream.
59 if (cfg.streamType == StreamType::OUTPUT && cfg.format == AidlPixelFormat::RGBA_8888) {
60 LOG(INFO) << "Selected stream configuration: width = " << cfg.width
61 << ", height = " << cfg.height
62 << ", format = " << static_cast<int>(cfg.format);
63 return std::move(cfg);
64 }
65 }
66
67 return {};
68 }
69
makeFromAidl(const NativeHandle & handle)70 native_handle_t* makeFromAidl(const NativeHandle& handle) {
71 // Create native_handle_t from
72 // ::aidl::android::hardware::common::NativeHandle. See also
73 // ::android::makeFromAidl() and native_handle_create().
74 const auto numFds = handle.fds.size();
75 const auto numInts = handle.ints.size();
76
77 if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||
78 numInts > NATIVE_HANDLE_MAX_INTS) {
79 return nullptr;
80 }
81
82 const auto mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));
83 native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));
84 if (h == nullptr) {
85 return nullptr;
86 }
87
88 h->version = sizeof(native_handle_t);
89 h->numFds = numFds;
90 h->numInts = numInts;
91 for (auto i = 0; i < handle.fds.size(); ++i) {
92 h->data[i] = handle.fds[i].get();
93 }
94 memcpy(h->data + handle.fds.size(), handle.ints.data(), handle.ints.size() * sizeof(int));
95
96 return h;
97 }
98
99 // "default" is reserved for the latest version of EVS manager.
100 constexpr const char kEvsManagerServiceName[] =
101 "android.hardware.automotive.evs.IEvsEnumerator/default";
102
103 } // namespace
104
105 namespace android::automotive::evs {
106
EvsServiceContext(JavaVM * vm,jclass clazz)107 EvsServiceContext::EvsServiceContext(JavaVM* vm, jclass clazz) :
108 mVm(vm), mCallbackThread(vm), mCarEvsServiceObj(nullptr) {
109 JNIEnv* env = nullptr;
110 vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
111 if (env != nullptr) {
112 // Registers post-native handlers
113 mDeathHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeDeathHandler", "()V");
114 mEventHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeEventHandler", "(I)V");
115 mFrameHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeFrameHandler",
116 "(ILandroid/hardware/HardwareBuffer;)V");
117 } else {
118 jniThrowException(env, "java/lang/IllegalArgumentException",
119 "Failed to get JNIEnv from a given VM instance.");
120 }
121 }
122
~EvsServiceContext()123 EvsServiceContext::~EvsServiceContext() {
124 {
125 std::lock_guard<std::mutex> lock(mLock);
126 if (mService) {
127 ::AIBinder_DeathRecipient_delete(mDeathRecipient.get());
128 }
129 mService = nullptr;
130 mCamera = nullptr;
131 mStreamHandler = nullptr;
132 }
133
134 // Stops the callback thread
135 mCallbackThread.stop();
136
137 // Deletes a global reference to the CarEvsService object
138 JNIEnv* env = nullptr;
139 if (mVm != nullptr) {
140 mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
141 if (env != nullptr) {
142 env->DeleteGlobalRef(mCarEvsServiceObj);
143 }
144 }
145 }
146
initialize(JNIEnv * env,jobject thiz)147 bool EvsServiceContext::initialize(JNIEnv* env, jobject thiz) {
148 bool isDeclared = ::AServiceManager_isDeclared(kEvsManagerServiceName);
149 if (!isDeclared) {
150 LOG(ERROR) << kEvsManagerServiceName << " is not available.";
151 return false;
152 }
153
154 AIBinder* binder = ::AServiceManager_checkService(kEvsManagerServiceName);
155 if (binder == nullptr) {
156 LOG(ERROR) << "IEvsEnumerator is not ready yet.";
157 return false;
158 }
159
160 std::shared_ptr<IEvsEnumerator> service = IEvsEnumerator::fromBinder(::ndk::SpAIBinder(binder));
161 if (!service) {
162 LOG(ERROR) << "Failed to connect to EVS service.";
163 return false;
164 }
165
166 auto deathRecipient = ::AIBinder_DeathRecipient_new(EvsServiceContext::onEvsServiceBinderDied);
167 auto status = ::ndk::ScopedAStatus::fromStatus(
168 ::AIBinder_linkToDeath(service->asBinder().get(), deathRecipient, this));
169 if (!status.isOk()) {
170 LOG(WARNING) << "Failed to register a death recipient; continuing anyway: "
171 << status.getMessage();
172 }
173
174 {
175 std::lock_guard<std::mutex> lock(mLock);
176 mService = service;
177 mDeathRecipient = ::ndk::ScopedAIBinder_DeathRecipient(deathRecipient);
178 if (!mCarEvsServiceObj) {
179 mCarEvsServiceObj = env->NewGlobalRef(thiz);
180 }
181
182 // Reset a stored camera id and a display handle
183 mCameraIdInUse.clear();
184 mDisplay = nullptr;
185 }
186
187 // Fetch a list of available camera devices
188 status = service->getCameraList(&mCameraList);
189 if (!status.isOk()) {
190 LOG(ERROR) << "Failed to load a camera list, error = " << status.getServiceSpecificError();
191 return false;
192 } else if (mCameraList.size() < 1) {
193 LOG(ERROR) << "No camera device is available";
194 return false;
195 }
196
197 LOG(INFO) << mCameraList.size() << " camera devices are listed.";
198 return true;
199 }
200
openCamera(const char * id)201 bool EvsServiceContext::openCamera(const char* id) {
202 if (!isAvailable()) {
203 LOG(ERROR) << "Has not connected to EVS service yet.";
204 return false;
205 }
206
207 if (isCameraOpened()) {
208 if (mCameraIdInUse == id) {
209 LOG(DEBUG) << "Camera " << id << " is has opened already.";
210 return true;
211 } else {
212 std::lock_guard<std::mutex> lock(mLock);
213 if (mService) {
214 // Close a current camera device.
215 if (!mService->closeCamera(mCamera).isOk()) {
216 LOG(WARNING) << "Failed to close a current camera device";
217 }
218 }
219 }
220 }
221
222 auto it = std::find_if(mCameraList.begin(), mCameraList.end(),
223 [target = std::string(id)](const CameraDesc& desc) {
224 return target == desc.id;
225 });
226 if (it == mCameraList.end()) {
227 LOG(ERROR) << id << " is not available";
228 return false;
229 }
230
231 std::vector<Stream> availableStreams;
232 {
233 std::lock_guard<std::mutex> lock(mLock);
234 if (!mService) {
235 return false;
236 }
237 mService->getStreamList(*it, &availableStreams);
238
239 Stream streamConfig = selectStreamConfiguration(availableStreams);
240 std::shared_ptr<IEvsCamera> camObj;
241 if (!mService || !mService->openCamera(id, streamConfig, &camObj).isOk() || !camObj) {
242 LOG(ERROR) << "Failed to open a camera " << id;
243 return false;
244 }
245
246 std::shared_ptr<StreamHandler> streamHandler =
247 ::ndk::SharedRefBase::make<StreamHandler>(camObj, this,
248 EvsServiceContext::kMaxNumFramesInFlight);
249 if (!streamHandler) {
250 LOG(ERROR) << "Failed to initialize a stream streamHandler.";
251 if (!mService->closeCamera(camObj).isOk()) {
252 LOG(ERROR) << "Failed to close a temporary camera device";
253 }
254 return false;
255 }
256
257 mCamera = std::move(camObj);
258 mStreamHandler = std::move(streamHandler);
259 mCameraIdInUse = id;
260 }
261
262 return true;
263 }
264
closeCamera()265 void EvsServiceContext::closeCamera() {
266 if (!isCameraOpened()) {
267 LOG(DEBUG) << "Camera has not opened yet.";
268 return;
269 }
270
271 {
272 std::lock_guard<std::mutex> lock(mLock);
273 if (!mService->closeCamera(mCamera).isOk()) {
274 LOG(WARNING) << "Failed to close a current camera device.";
275 }
276 }
277
278 // Reset a camera reference and id in use.
279 mCamera.reset();
280 mCameraIdInUse.clear();
281 }
282
startVideoStream()283 bool EvsServiceContext::startVideoStream() {
284 if (!isCameraOpened()) {
285 LOG(ERROR) << "Camera has not opened yet.";
286 return JNI_FALSE;
287 }
288
289 return mStreamHandler->startStream();
290 }
291
stopVideoStream()292 void EvsServiceContext::stopVideoStream() {
293 if (!isCameraOpened()) {
294 LOG(DEBUG) << "Camera has not opened; a request to stop a video steram is ignored.";
295 return;
296 }
297
298 if (!mStreamHandler->asyncStopStream()) {
299 LOG(WARNING) << "Failed to stop a video stream. EVS service may die.";
300 }
301 }
302
acquireCameraAndDisplayLocked()303 void EvsServiceContext::acquireCameraAndDisplayLocked() {
304 if (!mCamera) {
305 LOG(DEBUG) << "A target camera is not available.";
306 return;
307 }
308
309 // Acquires the display ownership. Because EVS awards this to the single
310 // client, no other clients can use EvsDisplay as long as CarEvsManager
311 // alives.
312 ::ndk::ScopedAStatus status =
313 mService->openDisplay(EvsServiceContext::kExclusiveMainDisplayId, &mDisplay);
314 if (!status.isOk() || !mDisplay) {
315 LOG(WARNING) << "Failed to acquire the display ownership. "
316 << "CarEvsManager may not be able to render "
317 << "the contents on the screen.";
318 return;
319 }
320
321 // Attempts to become a primary owner
322 status = mCamera->forcePrimaryClient(mDisplay);
323 if (!status.isOk() ||
324 static_cast<EvsResult>(status.getServiceSpecificError()) != EvsResult::OK) {
325 LOG(WARNING) << "Failed to own a camera device: " << status.getMessage();
326 }
327 }
328
doneWithFrame(int bufferId)329 void EvsServiceContext::doneWithFrame(int bufferId) {
330 {
331 std::lock_guard<std::mutex> lock(mLock);
332 if (!mStreamHandler) {
333 LOG(DEBUG) << "A stream handler is not available.";
334 return;
335 }
336
337 auto it = mBufferRecords.find(bufferId);
338 if (it == mBufferRecords.end()) {
339 LOG(WARNING) << "Unknown buffer is requested to return.";
340 return;
341 }
342
343 mBufferRecords.erase(it);
344
345 // If this is the first frame since current video stream started, we'd claim
346 // the exclusive ownership of the camera and the display and keep for the rest
347 // of the lifespan.
348 if (!mDisplay) {
349 acquireCameraAndDisplayLocked();
350 }
351 }
352 mStreamHandler->doneWithFrame(bufferId);
353 }
354
355 /*
356 * Forwards EVS stream events to the client. This method will run in the
357 * context of EvsCallbackThread.
358 */
onNewEvent(const EvsEventDesc & event)359 void EvsServiceContext::onNewEvent(const EvsEventDesc& event) {
360 mCallbackThread.enqueue([event, this](JNIEnv* env) {
361 // Gives an event callback
362 env->CallVoidMethod(mCarEvsServiceObj, mEventHandlerMethodId,
363 static_cast<jint>(event.aType));
364 });
365 }
366
367 /*
368 * Forwards EVS frames to the client. This method will run in the context of
369 * EvsCallbackThread.
370 */
onNewFrame(const BufferDesc & bufferDesc)371 bool EvsServiceContext::onNewFrame(const BufferDesc& bufferDesc) {
372 // Create AHardwareBuffer from ::aidl::android::hardware::automotive::evs::BufferDesc
373 native_handle_t* nativeHandle = makeFromAidl(bufferDesc.buffer.handle);
374 const auto handleGuard = ::android::base::make_scope_guard([nativeHandle] {
375 // We only need to free an allocated memory because a source buffer is
376 // owned by EVS HAL implementation.
377 free(nativeHandle);
378 });
379
380 if (nativeHandle == nullptr ||
381 !std::all_of(nativeHandle->data + 0, nativeHandle->data + nativeHandle->numFds,
382 [](int fd) { return fd >= 0; })) {
383 LOG(ERROR) << " android::makeFromAidl returned an invalid native handle";
384 return false;
385 }
386
387 const AHardwareBuffer_Desc desc{
388 .width = static_cast<uint32_t>(bufferDesc.buffer.description.width),
389 .height = static_cast<uint32_t>(bufferDesc.buffer.description.height),
390 .layers = static_cast<uint32_t>(bufferDesc.buffer.description.layers),
391 .format = static_cast<uint32_t>(bufferDesc.buffer.description.format),
392 .usage = static_cast<uint64_t>(bufferDesc.buffer.description.usage),
393 .stride = static_cast<uint32_t>(bufferDesc.buffer.description.stride),
394 };
395
396 AHardwareBuffer* ahwb = nullptr;
397 const auto status =
398 AHardwareBuffer_createFromHandle(&desc, nativeHandle,
399 AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
400 &ahwb);
401 if (status != android::NO_ERROR) {
402 LOG(ERROR) << "Failed to create a raw hardware buffer from a native handle, "
403 << "status = " << statusToString(status);
404 mStreamHandler->doneWithFrame(bufferDesc.bufferId);
405 return false;
406 }
407
408 mCallbackThread.enqueue([ahwb, bufferId = bufferDesc.bufferId, this](JNIEnv* env) {
409 {
410 std::lock_guard lock(mLock);
411 mBufferRecords.insert(bufferId);
412 }
413
414 // Forward AHardwareBuffer to the client
415 jobject hwBuffer = AHardwareBuffer_toHardwareBuffer(env, ahwb);
416 if (!hwBuffer) {
417 LOG(WARNING) << "Failed to create HardwareBuffer from AHardwareBuffer.";
418 mStreamHandler->doneWithFrame(bufferId);
419 } else {
420 env->CallVoidMethod(mCarEvsServiceObj, mFrameHandlerMethodId, bufferId, hwBuffer);
421 env->DeleteLocalRef(hwBuffer);
422 }
423
424 // We're done
425 AHardwareBuffer_release(ahwb);
426 });
427
428 return true;
429 }
430
431 /*
432 * Handles an unexpected death of EVS service. This method will run in the
433 * context of EvsCallbackThread.
434 */
onEvsServiceDiedImpl()435 void EvsServiceContext::onEvsServiceDiedImpl() {
436 mCallbackThread.enqueue([this](JNIEnv* env) {
437 // Drops invalidated service handles. We will re-initialize them when
438 // we try to reconnect. The buffer record would be cleared safely
439 // because all buffer references get invalidated upon the death of the
440 // native EVS service.
441 {
442 std::lock_guard<std::mutex> lock(mLock);
443 mCamera = nullptr;
444 mService = nullptr;
445 mStreamHandler = nullptr;
446 mBufferRecords.clear();
447 mCameraIdInUse.clear();
448 }
449
450 LOG(ERROR) << "The native EVS service has died.";
451 // EVS service has died but CarEvsManager instance still alives.
452 // Only a service handle needs to be destroyed; this will be
453 // re-created when CarEvsManager successfully connects to EVS service
454 // when it comes back.
455 env->CallVoidMethod(mCarEvsServiceObj, mDeathHandlerMethodId);
456 });
457 }
458
onEvsServiceBinderDied(void * cookie)459 void EvsServiceContext::onEvsServiceBinderDied(void* cookie) {
460 auto thiz = static_cast<EvsServiceContext*>(cookie);
461 if (!thiz) {
462 LOG(WARNING) << "A death of the EVS service is detected but ignored "
463 << "because of the invalid service context.";
464 return;
465 }
466
467 thiz->onEvsServiceDiedImpl();
468 }
469
470 } // namespace android::automotive::evs
471