blob: f338eef493014057571d4dd0c6d85d74e264478c [file] [log] [blame]
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "EvsServiceContext.h"
#include <android-base/logging.h>
#include <android/hardware_buffer.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <nativehelper/JNIHelp.h>
#include <vndk/hardware_buffer.h>
using android::hardware::automotive::evs::V1_0::EvsResult;
using namespace android::hardware::automotive::evs::V1_1;
namespace {
jmethodID getMethodIDOrDie(JNIEnv* env, jclass clazz, const char* name, const char* signature) {
jmethodID res = env->GetMethodID(clazz, name, signature);
LOG_ALWAYS_FATAL_IF(res == nullptr, "Unable to find method %s with signature %s", name,
signature);
return res;
}
} // namespace
namespace android {
namespace automotive {
namespace evs {
// "default" is reserved for the latest version of EVS manager.
const char* EvsServiceContext::kServiceName = "default";
EvsServiceContext::EvsServiceContext(JavaVM* vm, jclass clazz) :
mVm(vm), mCallbackThread(vm), mCarEvsServiceObj(nullptr) {
JNIEnv* env = nullptr;
vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
if (env != nullptr) {
// Registers post-native handlers
mDeathHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeDeathHandler", "()V");
mEventHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeEventHandler", "(I)V");
mFrameHandlerMethodId = getMethodIDOrDie(env, clazz, "postNativeFrameHandler",
"(ILandroid/hardware/HardwareBuffer;)V");
} else {
jniThrowException(env, "java/lang/IllegalArgumentException",
"Failed to get JNIEnv from a given VM instance.");
}
}
EvsServiceContext::~EvsServiceContext() {
{
std::lock_guard<std::mutex> lock(mLock);
if (mService != nullptr) {
mService->unlinkToDeath(mDeathRecipient);
}
mService = nullptr;
mCamera = nullptr;
mStreamHandler = nullptr;
}
// Stops the callback thread
mCallbackThread.stop();
// Deletes a global reference to the CarEvsService object
JNIEnv* env = nullptr;
if (mVm != nullptr) {
mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4);
if (env != nullptr) {
env->DeleteGlobalRef(mCarEvsServiceObj);
}
}
}
bool EvsServiceContext::initialize(JNIEnv* env, jobject thiz) {
sp<IEvsEnumerator> service = IEvsEnumerator::tryGetService(EvsServiceContext::kServiceName);
if (!service) {
// TODO(b/177923058): it may be desired to retry a few times if the
// connection fails.
LOG(ERROR) << "Failed to connect to EVS service.";
return false;
}
sp<EvsDeathRecipient> deathRecipient = new EvsDeathRecipient(service, this);
auto ret = service->linkToDeath(deathRecipient, /*cookie=*/0);
if (!ret.isOk() || ret == false) {
LOG(ERROR) << "Failed to register a death recipient; the service may die.";
return false;
}
{
std::lock_guard<std::mutex> lock(mLock);
mService = service;
mDeathRecipient = deathRecipient;
if (!mCarEvsServiceObj) {
mCarEvsServiceObj = env->NewGlobalRef(thiz);
}
}
return true;
}
bool EvsServiceContext::openCamera(const char* id) {
if (!isAvailable()) {
LOG(ERROR) << "Has not connected to EVS service yet.";
return false;
}
if (isCameraOpened()) {
LOG(DEBUG) << "Camera " << id << " is has opened already.";
return true;
}
sp<IEvsCamera> camera = IEvsCamera::castFrom(mService->openCamera(id));
if (!camera) {
LOG(ERROR) << "Failed to open a camera " << id;
return false;
}
sp<StreamHandler> streamHandler =
new StreamHandler(camera, this, EvsServiceContext::kMaxNumFramesInFlight);
if (!streamHandler) {
LOG(ERROR) << "Failed to initialize a stream streamHandler.";
return false;
}
{
std::lock_guard<std::mutex> lock(mLock);
mCamera = camera;
mStreamHandler = streamHandler;
}
return true;
}
void EvsServiceContext::closeCamera() {
if (!isCameraOpened()) {
LOG(DEBUG) << "Camera has not opened yet.";
return;
}
mService->closeCamera(mCamera);
}
bool EvsServiceContext::startVideoStream() {
if (!isCameraOpened()) {
LOG(ERROR) << "Camera has not opened yet.";
return JNI_FALSE;
}
return mStreamHandler->startStream();
}
void EvsServiceContext::stopVideoStream() {
if (!isCameraOpened()) {
LOG(DEBUG) << "Camera has not opened; a request to stop a video steram is ignored.";
return;
}
if (!mStreamHandler->asyncStopStream()) {
LOG(WARNING) << "Failed to stop a video stream. EVS service may die.";
}
}
void EvsServiceContext::acquireCameraAndDisplay() {
// Acquires the display ownership. Because EVS awards this to the single
// client, no other clients can use EvsDisplay as long as CarEvsManager
// alives.
mDisplay = mService->openDisplay_1_1(EvsServiceContext::kExclusiveMainDisplayId);
if (!mDisplay) {
LOG(WARNING) << "Failed to acquire the display ownership. "
<< "CarEvsManager may not be able to render "
<< "the contents on the screen.";
return;
}
// Attempts to become a primary owner
auto ret = mCamera->forceMaster(mDisplay);
if (!ret.isOk() || ret != EvsResult::OK) {
LOG(WARNING) << "Failed to own a camera device.";
}
}
void EvsServiceContext::doneWithFrame(int bufferId) {
BufferDesc bufferToReturn;
{
std::lock_guard<std::mutex> lock(mLock);
auto it = mBufferRecords.find(bufferId);
if (it == mBufferRecords.end()) {
LOG(WARNING) << "Unknown buffer is requested to return.";
return;
}
bufferToReturn = it->second;
mBufferRecords.erase(it);
}
mStreamHandler->doneWithFrame(bufferToReturn);
// If this is the first frame since current video stream started, we'd claim
// the exclusive ownership of the camera and the display and keep for the rest
// of the lifespan.
std::call_once(mDisplayAcquired, &EvsServiceContext::acquireCameraAndDisplay, this);
}
/*
* Forwards EVS stream events to the client. This method will run in the
* context of EvsCallbackThread.
*/
void EvsServiceContext::onNewEvent(EvsEventDesc event) {
mCallbackThread.enqueue([event, this](JNIEnv* env) {
// Gives an event callback
env->CallVoidMethod(mCarEvsServiceObj, mEventHandlerMethodId,
static_cast<jint>(event.aType));
});
}
/*
* Forwards EVS frames to the client. This method will run in the context of
* EvsCallbackThread.
*/
void EvsServiceContext::onNewFrame(BufferDesc bufferDesc) {
mCallbackThread.enqueue([bufferDesc, this](JNIEnv* env) {
const auto createFromAHardwareBuffer =
android::android_hardware_HardwareBuffer_createFromAHardwareBuffer;
// Clones a AHardwareBuffer
AHardwareBuffer_Desc const* desc =
reinterpret_cast<AHardwareBuffer_Desc const*>(&bufferDesc.buffer.description);
AHardwareBuffer* rawBuffer;
auto status =
AHardwareBuffer_createFromHandle(desc, bufferDesc.buffer.nativeHandle,
AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
&rawBuffer);
if (status != android::NO_ERROR) {
LOG(ERROR) << "Failed to create a raw hardware buffer from a native handle.";
mStreamHandler->doneWithFrame(bufferDesc);
} else {
{
std::lock_guard<std::mutex> lock(mLock);
mBufferRecords.try_emplace(bufferDesc.bufferId, bufferDesc);
}
// Calls back
jobject hwBuffer = createFromAHardwareBuffer(env, rawBuffer);
if (!hwBuffer) {
LOG(WARNING) << "Failed to create HardwareBuffer from AHardwareBuffer.";
mStreamHandler->doneWithFrame(bufferDesc);
} else {
env->CallVoidMethod(mCarEvsServiceObj, mFrameHandlerMethodId, bufferDesc.bufferId,
hwBuffer);
}
env->DeleteLocalRef(hwBuffer);
AHardwareBuffer_release(rawBuffer);
}
});
}
/*
* Handles an unexpected death of EVS service. This method will run in the
* context of EvsCallbackThread.
*/
void EvsServiceContext::onServiceDied(const wp<hidl::base::V1_0::IBase>& who) {
mCallbackThread.enqueue([who, this](JNIEnv* env) {
if (!isEqual(who)) {
// We're not interested in this death notification.
return;
}
// Drops invalidated service handles. We will re-initialize them when
// we try to reconnect. The buffer record would be cleared safely
// because all buffer references get invalidated upon the death of the
// native EVS service.
{
std::lock_guard<std::mutex> lock(mLock);
mCamera = nullptr;
mService = nullptr;
mStreamHandler = nullptr;
mBufferRecords.clear();
}
LOG(ERROR) << "The native EVS service has died.";
// EVS service has died but CarEvsManager instance still alives.
// Only a service handle needs to be destroyed; this will be
// re-created when CarEvsManager successfully connects to EVS service
// when it comes back.
env->CallVoidMethod(mCarEvsServiceObj, mDeathHandlerMethodId);
});
}
} // namespace evs
} // namespace automotive
} // namespace android