diff options
| author | 2022-12-06 07:23:56 +0000 | |
|---|---|---|
| committer | 2022-12-06 07:23:56 +0000 | |
| commit | 8a978a12c11e35c72d92e1ed44dc6d01f8cf12df (patch) | |
| tree | 916bcc46f80a795a30c7c1784cb3ad84512a2e68 | |
| parent | 34f0af63c482b2a2e519e6828c74a70492abbff3 (diff) | |
| parent | 412ff42f26f3914680187a269ef2b348374d3bae (diff) | |
Merge "TV Input 2.0 JNI"
| -rw-r--r-- | services/core/jni/Android.bp | 6 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_tv_TvInputHal.cpp | 574 | ||||
| -rw-r--r-- | services/core/jni/tvinput/BufferProducerThread.cpp | 147 | ||||
| -rw-r--r-- | services/core/jni/tvinput/BufferProducerThread.h | 56 | ||||
| -rw-r--r-- | services/core/jni/tvinput/JTvInputHal.cpp | 378 | ||||
| -rw-r--r-- | services/core/jni/tvinput/JTvInputHal.h | 185 | ||||
| -rw-r--r-- | services/core/jni/tvinput/OWNERS | 3 | ||||
| -rw-r--r-- | services/core/jni/tvinput/TvInputHal_hidl.cpp | 138 | ||||
| -rw-r--r-- | services/core/jni/tvinput/TvInputHal_hidl.h | 35 | ||||
| -rw-r--r-- | services/core/jni/tvinput/jstruct.h | 55 |
10 files changed, 1009 insertions, 568 deletions
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index f431250cf7e9..e66168815cd0 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -31,6 +31,9 @@ cc_library_static { "BroadcastRadio/convert.cpp", "BroadcastRadio/regions.cpp", "stats/SurfaceFlingerPuller.cpp", + "tvinput/BufferProducerThread.cpp", + "tvinput/JTvInputHal.cpp", + "tvinput/TvInputHal_hidl.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", "com_android_server_am_BatteryStatsService.cpp", "com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp", @@ -150,6 +153,7 @@ cc_defaults { "libdataloader", "libincfs", "android.hardware.audio.common@2.0", + "android.media.audio.common.types-V1-ndk", "android.hardware.broadcastradio@1.0", "android.hardware.broadcastradio@1.1", "android.hardware.contexthub@1.0", @@ -176,6 +180,7 @@ cc_defaults { "android.hardware.power.stats-V1-ndk", "android.hardware.thermal@1.0", "android.hardware.tv.input@1.0", + "android.hardware.tv.input-V1-ndk", "android.hardware.vibrator-V2-cpp", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", @@ -197,6 +202,7 @@ cc_defaults { static_libs: [ "android.hardware.broadcastradio@common-utils-1x-lib", + "libaidlcommonsupport", ], product_variables: { diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index a5311f33eb36..a8d2f4e9be5b 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -18,577 +18,15 @@ //#define LOG_NDEBUG 0 -#include "android_os_MessageQueue.h" -#include "android_runtime/AndroidRuntime.h" -#include "android_runtime/android_view_Surface.h" -#include <nativehelper/JNIHelp.h> -#include "jni.h" +#include "tvinput/JTvInputHal.h" -#include <android/hardware/tv/input/1.0/ITvInputCallback.h> -#include <android/hardware/tv/input/1.0/ITvInput.h> -#include <android/hardware/tv/input/1.0/types.h> -#include <gui/Surface.h> -#include <utils/Errors.h> -#include <utils/KeyedVector.h> -#include <utils/Log.h> -#include <utils/Looper.h> -#include <utils/NativeHandle.h> -#include <hardware/tv_input.h> - -using ::android::hardware::audio::common::V2_0::AudioDevice; -using ::android::hardware::tv::input::V1_0::ITvInput; -using ::android::hardware::tv::input::V1_0::ITvInputCallback; -using ::android::hardware::tv::input::V1_0::Result; -using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo; -using ::android::hardware::tv::input::V1_0::TvInputEvent; -using ::android::hardware::tv::input::V1_0::TvInputEventType; -using ::android::hardware::tv::input::V1_0::TvInputType; -using ::android::hardware::tv::input::V1_0::TvStreamConfig; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::hidl_vec; -using ::android::hardware::hidl_string; +gTvInputHalClassInfoType gTvInputHalClassInfo; +gTvStreamConfigClassInfoType gTvStreamConfigClassInfo; +gTvStreamConfigBuilderClassInfoType gTvStreamConfigBuilderClassInfo; +gTvInputHardwareInfoBuilderClassInfoType gTvInputHardwareInfoBuilderClassInfo; namespace android { -static struct { - jmethodID deviceAvailable; - jmethodID deviceUnavailable; - jmethodID streamConfigsChanged; - jmethodID firstFrameCaptured; -} gTvInputHalClassInfo; - -static struct { - jclass clazz; -} gTvStreamConfigClassInfo; - -static struct { - jclass clazz; - - jmethodID constructor; - jmethodID streamId; - jmethodID type; - jmethodID maxWidth; - jmethodID maxHeight; - jmethodID generation; - jmethodID build; -} gTvStreamConfigBuilderClassInfo; - -static struct { - jclass clazz; - - jmethodID constructor; - jmethodID deviceId; - jmethodID type; - jmethodID hdmiPortId; - jmethodID cableConnectionStatus; - jmethodID audioType; - jmethodID audioAddress; - jmethodID build; -} gTvInputHardwareInfoBuilderClassInfo; - -//////////////////////////////////////////////////////////////////////////////// - -class BufferProducerThread : public Thread { -public: - BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream); - - virtual status_t readyToRun(); - - void setSurface(const sp<Surface>& surface); - void onCaptured(uint32_t seq, bool succeeded); - void shutdown(); - -private: - Mutex mLock; - Condition mCondition; - sp<Surface> mSurface; - tv_input_device_t* mDevice; - int mDeviceId; - tv_stream_t mStream; - sp<ANativeWindowBuffer_t> mBuffer; - enum { - CAPTURING, - CAPTURED, - RELEASED, - } mBufferState; - uint32_t mSeq; - bool mShutdown; - - virtual bool threadLoop(); - - void setSurfaceLocked(const sp<Surface>& surface); -}; - -BufferProducerThread::BufferProducerThread( - tv_input_device_t* device, int deviceId, const tv_stream_t* stream) - : Thread(false), - mDevice(device), - mDeviceId(deviceId), - mBuffer(NULL), - mBufferState(RELEASED), - mSeq(0u), - mShutdown(false) { - memcpy(&mStream, stream, sizeof(mStream)); -} - -status_t BufferProducerThread::readyToRun() { - sp<ANativeWindow> anw(mSurface); - status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage); - if (err != NO_ERROR) { - return err; - } - err = native_window_set_buffers_dimensions( - anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height); - if (err != NO_ERROR) { - return err; - } - err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format); - if (err != NO_ERROR) { - return err; - } - return NO_ERROR; -} - -void BufferProducerThread::setSurface(const sp<Surface>& surface) { - Mutex::Autolock autoLock(&mLock); - setSurfaceLocked(surface); -} - -void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) { - if (surface == mSurface) { - return; - } - - if (mBufferState == CAPTURING) { - mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq); - } - while (mBufferState == CAPTURING) { - status_t err = mCondition.waitRelative(mLock, s2ns(1)); - if (err != NO_ERROR) { - ALOGE("error %d while wating for buffer state to change.", err); - break; - } - } - mBuffer.clear(); - mBufferState = RELEASED; - - mSurface = surface; - mCondition.broadcast(); -} - -void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) { - Mutex::Autolock autoLock(&mLock); - if (seq != mSeq) { - ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq); - } - if (mBufferState != CAPTURING) { - ALOGW("mBufferState != CAPTURING : instead %d", mBufferState); - } - if (succeeded) { - mBufferState = CAPTURED; - } else { - mBuffer.clear(); - mBufferState = RELEASED; - } - mCondition.broadcast(); -} - -void BufferProducerThread::shutdown() { - Mutex::Autolock autoLock(&mLock); - mShutdown = true; - setSurfaceLocked(NULL); - requestExitAndWait(); -} - -bool BufferProducerThread::threadLoop() { - Mutex::Autolock autoLock(&mLock); - - status_t err = NO_ERROR; - if (mSurface == NULL) { - err = mCondition.waitRelative(mLock, s2ns(1)); - // It's OK to time out here. - if (err != NO_ERROR && err != TIMED_OUT) { - ALOGE("error %d while wating for non-null surface to be set", err); - return false; - } - return true; - } - sp<ANativeWindow> anw(mSurface); - while (mBufferState == CAPTURING) { - err = mCondition.waitRelative(mLock, s2ns(1)); - if (err != NO_ERROR) { - ALOGE("error %d while wating for buffer state to change.", err); - return false; - } - } - if (mBufferState == CAPTURED && anw != NULL) { - err = anw->queueBuffer(anw.get(), mBuffer.get(), -1); - if (err != NO_ERROR) { - ALOGE("error %d while queueing buffer to surface", err); - return false; - } - mBuffer.clear(); - mBufferState = RELEASED; - } - if (mBuffer == NULL && !mShutdown && anw != NULL) { - ANativeWindowBuffer_t* buffer = NULL; - err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer); - if (err != NO_ERROR) { - ALOGE("error %d while dequeueing buffer to surface", err); - return false; - } - mBuffer = buffer; - mBufferState = CAPTURING; - mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id, - buffer->handle, ++mSeq); - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// - -class JTvInputHal { -public: - ~JTvInputHal(); - - static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper); - - int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface); - int removeStream(int deviceId, int streamId); - const hidl_vec<TvStreamConfig> getStreamConfigs(int deviceId); - - void onDeviceAvailable(const TvInputDeviceInfo& info); - void onDeviceUnavailable(int deviceId); - void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus); - void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); - -private: - // Connection between a surface and a stream. - class Connection { - public: - Connection() {} - - sp<Surface> mSurface; - tv_stream_type_t mStreamType; - - // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE - sp<NativeHandle> mSourceHandle; - // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER - sp<BufferProducerThread> mThread; - }; - - class NotifyHandler : public MessageHandler { - public: - NotifyHandler(JTvInputHal* hal, const TvInputEvent& event); - - virtual void handleMessage(const Message& message); - - private: - TvInputEvent mEvent; - JTvInputHal* mHal; - }; - - class TvInputCallback : public ITvInputCallback { - public: - explicit TvInputCallback(JTvInputHal* hal); - Return<void> notify(const TvInputEvent& event) override; - private: - JTvInputHal* mHal; - }; - - JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper); - - Mutex mLock; - Mutex mStreamLock; - jweak mThiz; - sp<Looper> mLooper; - - KeyedVector<int, KeyedVector<int, Connection> > mConnections; - - sp<ITvInput> mTvInput; - sp<ITvInputCallback> mTvInputCallback; -}; - -JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, - const sp<Looper>& looper) { - mThiz = env->NewWeakGlobalRef(thiz); - mTvInput = tvInput; - mLooper = looper; - mTvInputCallback = new TvInputCallback(this); - mTvInput->setCallback(mTvInputCallback); -} - -JTvInputHal::~JTvInputHal() { - mTvInput->setCallback(nullptr); - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->DeleteWeakGlobalRef(mThiz); - mThiz = NULL; -} - -JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) { - // TODO(b/31632518) - sp<ITvInput> tvInput = ITvInput::getService(); - if (tvInput == nullptr) { - ALOGE("Couldn't get tv.input service."); - return nullptr; - } - - return new JTvInputHal(env, thiz, tvInput, looper); -} - -int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { - Mutex::Autolock autoLock(&mStreamLock); - KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); - if (connections.indexOfKey(streamId) < 0) { - connections.add(streamId, Connection()); - } - Connection& connection = connections.editValueFor(streamId); - if (connection.mSurface == surface) { - // Nothing to do - return NO_ERROR; - } - // Clear the surface in the connection. - if (connection.mSurface != NULL) { - if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) { - if (Surface::isValid(connection.mSurface)) { - connection.mSurface->setSidebandStream(NULL); - } - } - connection.mSurface.clear(); - } - if (connection.mSourceHandle == NULL && connection.mThread == NULL) { - // Need to configure stream - Result result = Result::UNKNOWN; - hidl_vec<TvStreamConfig> list; - mTvInput->getStreamConfigurations(deviceId, - [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) { - result = res; - if (res == Result::OK) { - list = configs; - } - }); - if (result != Result::OK) { - ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result); - return UNKNOWN_ERROR; - } - int configIndex = -1; - for (size_t i = 0; i < list.size(); ++i) { - if (list[i].streamId == streamId) { - configIndex = i; - break; - } - } - if (configIndex == -1) { - ALOGE("Cannot find a config with given stream ID: %d", streamId); - return BAD_VALUE; - } - connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE; - - result = Result::UNKNOWN; - const native_handle_t* sidebandStream; - mTvInput->openStream(deviceId, streamId, - [&result, &sidebandStream](Result res, const native_handle_t* handle) { - result = res; - if (res == Result::OK) { - if (handle) { - sidebandStream = native_handle_clone(handle); - } else { - result = Result::UNKNOWN; - } - } - }); - if (result != Result::OK) { - ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId, - result); - return UNKNOWN_ERROR; - } - connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true); - } - connection.mSurface = surface; - if (connection.mSurface != nullptr) { - connection.mSurface->setSidebandStream(connection.mSourceHandle); - } - return NO_ERROR; -} - -int JTvInputHal::removeStream(int deviceId, int streamId) { - Mutex::Autolock autoLock(&mStreamLock); - KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); - if (connections.indexOfKey(streamId) < 0) { - return BAD_VALUE; - } - Connection& connection = connections.editValueFor(streamId); - if (connection.mSurface == NULL) { - // Nothing to do - return NO_ERROR; - } - if (Surface::isValid(connection.mSurface)) { - connection.mSurface->setSidebandStream(NULL); - } - connection.mSurface.clear(); - if (connection.mThread != NULL) { - connection.mThread->shutdown(); - connection.mThread.clear(); - } - if (mTvInput->closeStream(deviceId, streamId) != Result::OK) { - ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId); - return BAD_VALUE; - } - if (connection.mSourceHandle != NULL) { - connection.mSourceHandle.clear(); - } - return NO_ERROR; -} - -const hidl_vec<TvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) { - Result result = Result::UNKNOWN; - hidl_vec<TvStreamConfig> list; - mTvInput->getStreamConfigurations(deviceId, - [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) { - result = res; - if (res == Result::OK) { - list = configs; - } - }); - if (result != Result::OK) { - ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result); - } - return list; -} - -void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfo& info) { - { - Mutex::Autolock autoLock(&mLock); - mConnections.add(info.deviceId, KeyedVector<int, Connection>()); - } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - - jobject builder = env->NewObject( - gTvInputHardwareInfoBuilderClassInfo.clazz, - gTvInputHardwareInfoBuilderClassInfo.constructor); - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId); - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); - if (info.type == TvInputType::HDMI) { - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId); - } - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus, - info.cableConnectionStatus); - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType); - if (info.audioType != AudioDevice::NONE) { - uint8_t buffer[info.audioAddress.size() + 1]; - memcpy(buffer, info.audioAddress.data(), info.audioAddress.size()); - buffer[info.audioAddress.size()] = '\0'; - jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char *>(buffer)); - env->CallObjectMethod( - builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress); - env->DeleteLocalRef(audioAddress); - } - - jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); - - env->CallVoidMethod( - mThiz, - gTvInputHalClassInfo.deviceAvailable, - infoObject); - - env->DeleteLocalRef(builder); - env->DeleteLocalRef(infoObject); -} - -void JTvInputHal::onDeviceUnavailable(int deviceId) { - { - Mutex::Autolock autoLock(&mLock); - KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); - for (size_t i = 0; i < connections.size(); ++i) { - removeStream(deviceId, connections.keyAt(i)); - } - connections.clear(); - mConnections.removeItem(deviceId); - } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mThiz, - gTvInputHalClassInfo.deviceUnavailable, - deviceId); -} - -void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) { - { - Mutex::Autolock autoLock(&mLock); - KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); - for (size_t i = 0; i < connections.size(); ++i) { - removeStream(deviceId, connections.keyAt(i)); - } - connections.clear(); - } - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId, - cableConnectionStatus); -} - -void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) { - sp<BufferProducerThread> thread; - { - Mutex::Autolock autoLock(&mLock); - KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); - Connection& connection = connections.editValueFor(streamId); - if (connection.mThread == NULL) { - ALOGE("capture thread not existing."); - return; - } - thread = connection.mThread; - } - thread->onCaptured(seq, succeeded); - if (seq == 0) { - JNIEnv* env = AndroidRuntime::getJNIEnv(); - env->CallVoidMethod( - mThiz, - gTvInputHalClassInfo.firstFrameCaptured, - deviceId, - streamId); - } -} - -JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) { - mHal = hal; - mEvent = event; -} - -void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { - switch (mEvent.type) { - case TvInputEventType::DEVICE_AVAILABLE: { - mHal->onDeviceAvailable(mEvent.deviceInfo); - } break; - case TvInputEventType::DEVICE_UNAVAILABLE: { - mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId); - } break; - case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: { - int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus); - mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus); - } break; - default: - ALOGE("Unrecognizable event"); - } -} - -JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) { - mHal = hal; -} - -Return<void> JTvInputHal::TvInputCallback::notify(const TvInputEvent& event) { - mHal->mLooper->sendMessage(new NotifyHandler(mHal, event), static_cast<int>(event.type)); - return Void(); -} - -//////////////////////////////////////////////////////////////////////////////// - static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); @@ -617,7 +55,7 @@ static int nativeRemoveStream(JNIEnv* env, jclass clazz, static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId, jint generation) { JTvInputHal* tvInputHal = (JTvInputHal*)ptr; - const hidl_vec<TvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId); + const std::vector<AidlTvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId); jobjectArray result = env->NewObjectArray(configs.size(), gTvStreamConfigClassInfo.clazz, NULL); for (size_t i = 0; i < configs.size(); ++i) { diff --git a/services/core/jni/tvinput/BufferProducerThread.cpp b/services/core/jni/tvinput/BufferProducerThread.cpp new file mode 100644 index 000000000000..f39dcee36ec2 --- /dev/null +++ b/services/core/jni/tvinput/BufferProducerThread.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2022 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 "BufferProducerThread.h" + +namespace android { + +BufferProducerThread::BufferProducerThread(tv_input_device_t* device, int deviceId, + const tv_stream_t* stream) + : Thread(false), + mDevice(device), + mDeviceId(deviceId), + mBuffer(NULL), + mBufferState(RELEASED), + mSeq(0u), + mShutdown(false) { + memcpy(&mStream, stream, sizeof(mStream)); +} + +status_t BufferProducerThread::readyToRun() { + sp<ANativeWindow> anw(mSurface); + status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage); + if (err != NO_ERROR) { + return err; + } + err = native_window_set_buffers_dimensions(anw.get(), mStream.buffer_producer.width, + mStream.buffer_producer.height); + if (err != NO_ERROR) { + return err; + } + err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format); + if (err != NO_ERROR) { + return err; + } + return NO_ERROR; +} + +void BufferProducerThread::setSurface(const sp<Surface>& surface) { + Mutex::Autolock autoLock(&mLock); + setSurfaceLocked(surface); +} + +void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) { + if (surface == mSurface) { + return; + } + + if (mBufferState == CAPTURING) { + mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq); + } + while (mBufferState == CAPTURING) { + status_t err = mCondition.waitRelative(mLock, s2ns(1)); + if (err != NO_ERROR) { + ALOGE("error %d while wating for buffer state to change.", err); + break; + } + } + mBuffer.clear(); + mBufferState = RELEASED; + + mSurface = surface; + mCondition.broadcast(); +} + +void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) { + Mutex::Autolock autoLock(&mLock); + if (seq != mSeq) { + ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq); + } + if (mBufferState != CAPTURING) { + ALOGW("mBufferState != CAPTURING : instead %d", mBufferState); + } + if (succeeded) { + mBufferState = CAPTURED; + } else { + mBuffer.clear(); + mBufferState = RELEASED; + } + mCondition.broadcast(); +} + +void BufferProducerThread::shutdown() { + Mutex::Autolock autoLock(&mLock); + mShutdown = true; + setSurfaceLocked(NULL); + requestExitAndWait(); +} + +bool BufferProducerThread::threadLoop() { + Mutex::Autolock autoLock(&mLock); + + status_t err = NO_ERROR; + if (mSurface == NULL) { + err = mCondition.waitRelative(mLock, s2ns(1)); + // It's OK to time out here. + if (err != NO_ERROR && err != TIMED_OUT) { + ALOGE("error %d while wating for non-null surface to be set", err); + return false; + } + return true; + } + sp<ANativeWindow> anw(mSurface); + while (mBufferState == CAPTURING) { + err = mCondition.waitRelative(mLock, s2ns(1)); + if (err != NO_ERROR) { + ALOGE("error %d while wating for buffer state to change.", err); + return false; + } + } + if (mBufferState == CAPTURED && anw != NULL) { + err = anw->queueBuffer(anw.get(), mBuffer.get(), -1); + if (err != NO_ERROR) { + ALOGE("error %d while queueing buffer to surface", err); + return false; + } + mBuffer.clear(); + mBufferState = RELEASED; + } + if (mBuffer == NULL && !mShutdown && anw != NULL) { + ANativeWindowBuffer_t* buffer = NULL; + err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer); + if (err != NO_ERROR) { + ALOGE("error %d while dequeueing buffer to surface", err); + return false; + } + mBuffer = buffer; + mBufferState = CAPTURING; + mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id, buffer->handle, ++mSeq); + } + + return true; +} + +} // namespace android diff --git a/services/core/jni/tvinput/BufferProducerThread.h b/services/core/jni/tvinput/BufferProducerThread.h new file mode 100644 index 000000000000..2e0fa917e87e --- /dev/null +++ b/services/core/jni/tvinput/BufferProducerThread.h @@ -0,0 +1,56 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#include <gui/Surface.h> +#include <hardware/tv_input.h> +#include <utils/Thread.h> + +namespace android { + +class BufferProducerThread : public Thread { +public: + BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream); + + virtual status_t readyToRun(); + + void setSurface(const sp<Surface>& surface); + void onCaptured(uint32_t seq, bool succeeded); + void shutdown(); + +private: + Mutex mLock; + Condition mCondition; + sp<Surface> mSurface; + tv_input_device_t* mDevice; + int mDeviceId; + tv_stream_t mStream; + sp<ANativeWindowBuffer_t> mBuffer; + enum { + CAPTURING, + CAPTURED, + RELEASED, + } mBufferState; + uint32_t mSeq; + bool mShutdown; + + virtual bool threadLoop(); + + void setSurfaceLocked(const sp<Surface>& surface); +}; + +} // namespace android diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp new file mode 100644 index 000000000000..0f6ef036c46b --- /dev/null +++ b/services/core/jni/tvinput/JTvInputHal.cpp @@ -0,0 +1,378 @@ +/* + * Copyright 2022 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 "JTvInputHal.h" + +namespace android { + +JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrapper> tvInput, + const sp<Looper>& looper) { + mThiz = env->NewWeakGlobalRef(thiz); + mTvInput = tvInput; + mLooper = looper; + mTvInputCallback = ::ndk::SharedRefBase::make<TvInputCallback>(this); + mTvInput->setCallback(mTvInputCallback); +} + +JTvInputHal::~JTvInputHal() { + mTvInput->setCallback(nullptr); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mThiz); + mThiz = NULL; +} + +JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) { + sp<HidlITvInput> hidlITvInput = HidlITvInput::getService(); + if (hidlITvInput != nullptr) { + ALOGD("tv.input service is HIDL."); + return new JTvInputHal(env, thiz, + std::shared_ptr<ITvInputWrapper>(new ITvInputWrapper(hidlITvInput)), + looper); + } + ::ndk::SpAIBinder binder(AServiceManager_waitForService(TV_INPUT_AIDL_SERVICE_NAME)); + std::shared_ptr<AidlITvInput> aidlITvInput = AidlITvInput::fromBinder(binder); + if (aidlITvInput == nullptr) { + ALOGE("Couldn't get tv.input service."); + return nullptr; + } + return new JTvInputHal(env, thiz, + std::shared_ptr<ITvInputWrapper>(new ITvInputWrapper(aidlITvInput)), + looper); +} + +int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) { + Mutex::Autolock autoLock(&mStreamLock); + KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); + if (connections.indexOfKey(streamId) < 0) { + connections.add(streamId, Connection()); + } + Connection& connection = connections.editValueFor(streamId); + if (connection.mSurface == surface) { + // Nothing to do + return NO_ERROR; + } + // Clear the surface in the connection. + if (connection.mSurface != NULL) { + if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) { + if (Surface::isValid(connection.mSurface)) { + connection.mSurface->setSidebandStream(NULL); + } + } + connection.mSurface.clear(); + } + if (connection.mSourceHandle == NULL && connection.mThread == NULL) { + // Need to configure stream + ::ndk::ScopedAStatus status; + std::vector<AidlTvStreamConfig> list; + status = mTvInput->getStreamConfigurations(deviceId, &list); + if (!status.isOk()) { + ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, + status.getServiceSpecificError()); + return UNKNOWN_ERROR; + } + int configIndex = -1; + for (size_t i = 0; i < list.size(); ++i) { + if (list[i].streamId == streamId) { + configIndex = i; + break; + } + } + if (configIndex == -1) { + ALOGE("Cannot find a config with given stream ID: %d", streamId); + return BAD_VALUE; + } + connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE; + + AidlNativeHandle sidebandStream; + status = mTvInput->openStream(deviceId, streamId, &sidebandStream); + if (!status.isOk()) { + ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId, + status.getServiceSpecificError()); + return UNKNOWN_ERROR; + } + connection.mSourceHandle = NativeHandle::create(makeFromAidl(sidebandStream), true); + } + connection.mSurface = surface; + if (connection.mSurface != nullptr) { + connection.mSurface->setSidebandStream(connection.mSourceHandle); + } + return NO_ERROR; +} + +int JTvInputHal::removeStream(int deviceId, int streamId) { + Mutex::Autolock autoLock(&mStreamLock); + KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); + if (connections.indexOfKey(streamId) < 0) { + return BAD_VALUE; + } + Connection& connection = connections.editValueFor(streamId); + if (connection.mSurface == NULL) { + // Nothing to do + return NO_ERROR; + } + if (Surface::isValid(connection.mSurface)) { + connection.mSurface->setSidebandStream(NULL); + } + connection.mSurface.clear(); + if (connection.mThread != NULL) { + connection.mThread->shutdown(); + connection.mThread.clear(); + } + if (!mTvInput->closeStream(deviceId, streamId).isOk()) { + ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId); + return BAD_VALUE; + } + if (connection.mSourceHandle != NULL) { + connection.mSourceHandle.clear(); + } + return NO_ERROR; +} + +const std::vector<AidlTvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) { + std::vector<AidlTvStreamConfig> list; + ::ndk::ScopedAStatus status = mTvInput->getStreamConfigurations(deviceId, &list); + if (!status.isOk()) { + ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, + status.getServiceSpecificError()); + return std::vector<AidlTvStreamConfig>(); + } + return list; +} + +void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfoWrapper& info) { + { + Mutex::Autolock autoLock(&mLock); + mConnections.add(info.deviceId, KeyedVector<int, Connection>()); + } + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject builder = env->NewObject(gTvInputHardwareInfoBuilderClassInfo.clazz, + gTvInputHardwareInfoBuilderClassInfo.constructor); + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId); + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); + if (info.type == TvInputType::HDMI) { + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, + info.portId); + } + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus, + info.cableConnectionStatus); + if (info.isHidl) { + hidlSetUpAudioInfo(env, builder, info); + } else { + AidlAudioDeviceType audioType = info.aidlAudioDevice.type.type; + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, audioType); + if (audioType != AidlAudioDeviceType::NONE) { + std::stringstream ss; + switch (info.aidlAudioDevice.address.getTag()) { + case AidlAudioDeviceAddress::id: + ss << info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::id>(); + break; + case AidlAudioDeviceAddress::mac: { + std::vector<uint8_t> addrList = + info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::mac>(); + for (int i = 0; i < addrList.size(); i++) { + if (i != 0) { + ss << ':'; + } + ss << std::uppercase << std::setfill('0') << std::setw(2) << std::hex + << static_cast<int32_t>(addrList[i]); + } + } break; + case AidlAudioDeviceAddress::ipv4: { + std::vector<uint8_t> addrList = + info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::ipv4>(); + for (int i = 0; i < addrList.size(); i++) { + if (i != 0) { + ss << '.'; + } + ss << static_cast<int32_t>(addrList[i]); + } + } break; + case AidlAudioDeviceAddress::ipv6: { + std::vector<int32_t> addrList = + info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::ipv6>(); + for (int i = 0; i < addrList.size(); i++) { + if (i != 0) { + ss << ':'; + } + ss << std::uppercase << std::setfill('0') << std::setw(4) << std::hex + << addrList[i]; + } + } break; + case AidlAudioDeviceAddress::alsa: { + std::vector<int32_t> addrList = + info.aidlAudioDevice.address.get<AidlAudioDeviceAddress::alsa>(); + ss << "card=" << addrList[0] << ";device=" << addrList[1]; + } break; + } + std::string bufferStr = ss.str(); + jstring audioAddress = env->NewStringUTF(bufferStr.c_str()); + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, + audioAddress); + env->DeleteLocalRef(audioAddress); + } + } + + jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); + + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.deviceAvailable, infoObject); + + env->DeleteLocalRef(builder); + env->DeleteLocalRef(infoObject); +} + +void JTvInputHal::onDeviceUnavailable(int deviceId) { + { + Mutex::Autolock autoLock(&mLock); + KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); + for (size_t i = 0; i < connections.size(); ++i) { + removeStream(deviceId, connections.keyAt(i)); + } + connections.clear(); + mConnections.removeItem(deviceId); + } + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.deviceUnavailable, deviceId); +} + +void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) { + { + Mutex::Autolock autoLock(&mLock); + KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); + for (size_t i = 0; i < connections.size(); ++i) { + removeStream(deviceId, connections.keyAt(i)); + } + connections.clear(); + } + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId, + cableConnectionStatus); +} + +void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) { + sp<BufferProducerThread> thread; + { + Mutex::Autolock autoLock(&mLock); + KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); + Connection& connection = connections.editValueFor(streamId); + if (connection.mThread == NULL) { + ALOGE("capture thread not existing."); + return; + } + thread = connection.mThread; + } + thread->onCaptured(seq, succeeded); + if (seq == 0) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mThiz, gTvInputHalClassInfo.firstFrameCaptured, deviceId, streamId); + } +} + +JTvInputHal::TvInputDeviceInfoWrapper +JTvInputHal::TvInputDeviceInfoWrapper::createDeviceInfoWrapper( + const AidlTvInputDeviceInfo& aidlTvInputDeviceInfo) { + TvInputDeviceInfoWrapper deviceInfo; + deviceInfo.isHidl = false; + deviceInfo.deviceId = aidlTvInputDeviceInfo.deviceId; + deviceInfo.type = aidlTvInputDeviceInfo.type; + deviceInfo.portId = aidlTvInputDeviceInfo.portId; + deviceInfo.cableConnectionStatus = aidlTvInputDeviceInfo.cableConnectionStatus; + deviceInfo.aidlAudioDevice = aidlTvInputDeviceInfo.audioDevice; + return deviceInfo; +} + +JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWrapper( + const AidlTvInputEvent& aidlTvInputEvent) { + TvInputEventWrapper event; + event.type = aidlTvInputEvent.type; + event.deviceInfo = + TvInputDeviceInfoWrapper::createDeviceInfoWrapper(aidlTvInputEvent.deviceInfo); + return event; +} + +JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEventWrapper& event) { + mHal = hal; + mEvent = event; +} + +void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { + switch (mEvent.type) { + case TvInputEventType::DEVICE_AVAILABLE: { + mHal->onDeviceAvailable(mEvent.deviceInfo); + } break; + case TvInputEventType::DEVICE_UNAVAILABLE: { + mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId); + } break; + case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: { + int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus); + mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus); + } break; + default: + ALOGE("Unrecognizable event"); + } +} + +JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) { + mHal = hal; +} + +::ndk::ScopedAStatus JTvInputHal::TvInputCallback::notify(const AidlTvInputEvent& event) { + mHal->mLooper->sendMessage(new NotifyHandler(mHal, + TvInputEventWrapper::createEventWrapper(event)), + static_cast<int>(event.type)); + return ::ndk::ScopedAStatus::ok(); +} + +JTvInputHal::ITvInputWrapper::ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput) + : mIsHidl(false), mAidlTvInput(aidlTvInput) {} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::setCallback( + const std::shared_ptr<TvInputCallback>& in_callback) { + if (mIsHidl) { + return hidlSetCallback(in_callback); + } else { + return mAidlTvInput->setCallback(in_callback); + } +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::getStreamConfigurations( + int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return) { + if (mIsHidl) { + return hidlGetStreamConfigurations(in_deviceId, _aidl_return); + } else { + return mAidlTvInput->getStreamConfigurations(in_deviceId, _aidl_return); + } +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::openStream(int32_t in_deviceId, + int32_t in_streamId, + AidlNativeHandle* _aidl_return) { + if (mIsHidl) { + return hidlOpenStream(in_deviceId, in_streamId, _aidl_return); + } else { + return mAidlTvInput->openStream(in_deviceId, in_streamId, _aidl_return); + } +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::closeStream(int32_t in_deviceId, + int32_t in_streamId) { + if (mIsHidl) { + return hidlCloseStream(in_deviceId, in_streamId); + } else { + return mAidlTvInput->closeStream(in_deviceId, in_streamId); + } +} + +} // namespace android diff --git a/services/core/jni/tvinput/JTvInputHal.h b/services/core/jni/tvinput/JTvInputHal.h new file mode 100644 index 000000000000..269729491ecb --- /dev/null +++ b/services/core/jni/tvinput/JTvInputHal.h @@ -0,0 +1,185 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#define TV_INPUT_AIDL_SERVICE_NAME "android.hardware.tv.input.ITvInput/default" + +#include <aidl/android/hardware/tv/input/BnTvInputCallback.h> +#include <aidl/android/hardware/tv/input/CableConnectionStatus.h> +#include <aidl/android/hardware/tv/input/ITvInput.h> +#include <aidl/android/media/audio/common/AudioDevice.h> +#include <aidlcommonsupport/NativeHandle.h> +#include <android/binder_manager.h> +#include <nativehelper/JNIHelp.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/Looper.h> +#include <utils/NativeHandle.h> + +#include <iomanip> + +#include "BufferProducerThread.h" +#include "TvInputHal_hidl.h" +#include "android_os_MessageQueue.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/android_view_Surface.h" +#include "tvinput/jstruct.h" + +using ::aidl::android::hardware::tv::input::BnTvInputCallback; +using ::aidl::android::hardware::tv::input::CableConnectionStatus; +using ::aidl::android::hardware::tv::input::TvInputEventType; +using ::aidl::android::hardware::tv::input::TvInputType; + +using AidlAudioDevice = ::aidl::android::media::audio::common::AudioDevice; +using AidlAudioDeviceAddress = ::aidl::android::media::audio::common::AudioDeviceAddress; +using AidlAudioDeviceType = ::aidl::android::media::audio::common::AudioDeviceType; +using AidlITvInput = ::aidl::android::hardware::tv::input::ITvInput; +using AidlNativeHandle = ::aidl::android::hardware::common::NativeHandle; +using AidlTvInputDeviceInfo = ::aidl::android::hardware::tv::input::TvInputDeviceInfo; +using AidlTvInputEvent = ::aidl::android::hardware::tv::input::TvInputEvent; +using AidlTvStreamConfig = ::aidl::android::hardware::tv::input::TvStreamConfig; + +extern gTvInputHalClassInfoType gTvInputHalClassInfo; +extern gTvStreamConfigClassInfoType gTvStreamConfigClassInfo; +extern gTvStreamConfigBuilderClassInfoType gTvStreamConfigBuilderClassInfo; +extern gTvInputHardwareInfoBuilderClassInfoType gTvInputHardwareInfoBuilderClassInfo; + +namespace android { + +class JTvInputHal { +public: + ~JTvInputHal(); + + static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper); + + int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface); + int removeStream(int deviceId, int streamId); + const std::vector<AidlTvStreamConfig> getStreamConfigs(int deviceId); + +private: + // Connection between a surface and a stream. + class Connection { + public: + Connection() {} + + sp<Surface> mSurface; + tv_stream_type_t mStreamType; + + // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE + sp<NativeHandle> mSourceHandle; + // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER + sp<BufferProducerThread> mThread; + }; + + class TvInputDeviceInfoWrapper { + public: + TvInputDeviceInfoWrapper() {} + + static TvInputDeviceInfoWrapper createDeviceInfoWrapper( + const AidlTvInputDeviceInfo& aidlTvInputDeviceInfo); + static TvInputDeviceInfoWrapper createDeviceInfoWrapper( + const HidlTvInputDeviceInfo& hidlTvInputDeviceInfo); + + bool isHidl; + int deviceId; + TvInputType type; + int portId; + CableConnectionStatus cableConnectionStatus; + AidlAudioDevice aidlAudioDevice; + HidlAudioDevice hidlAudioType; + ::android::hardware::hidl_array<uint8_t, 32> hidlAudioAddress; + }; + + class TvInputEventWrapper { + public: + TvInputEventWrapper() {} + + static TvInputEventWrapper createEventWrapper(const AidlTvInputEvent& aidlTvInputEvent); + static TvInputEventWrapper createEventWrapper(const HidlTvInputEvent& hidlTvInputEvent); + + TvInputEventType type; + TvInputDeviceInfoWrapper deviceInfo; + }; + + class NotifyHandler : public MessageHandler { + public: + NotifyHandler(JTvInputHal* hal, const TvInputEventWrapper& event); + + void handleMessage(const Message& message) override; + + private: + TvInputEventWrapper mEvent; + JTvInputHal* mHal; + }; + + class TvInputCallback : public HidlITvInputCallback, public BnTvInputCallback { + public: + explicit TvInputCallback(JTvInputHal* hal); + ::ndk::ScopedAStatus notify(const AidlTvInputEvent& event) override; + Return<void> notify(const HidlTvInputEvent& event) override; + + private: + JTvInputHal* mHal; + }; + + class ITvInputWrapper { + public: + ITvInputWrapper(std::shared_ptr<AidlITvInput>& aidlTvInput); + ITvInputWrapper(sp<HidlITvInput>& hidlTvInput); + + ::ndk::ScopedAStatus setCallback(const std::shared_ptr<TvInputCallback>& in_callback); + ::ndk::ScopedAStatus getStreamConfigurations(int32_t in_deviceId, + std::vector<AidlTvStreamConfig>* _aidl_return); + ::ndk::ScopedAStatus openStream(int32_t in_deviceId, int32_t in_streamId, + AidlNativeHandle* _aidl_return); + ::ndk::ScopedAStatus closeStream(int32_t in_deviceId, int32_t in_streamId); + + private: + ::ndk::ScopedAStatus hidlSetCallback(const std::shared_ptr<TvInputCallback>& in_callback); + ::ndk::ScopedAStatus hidlGetStreamConfigurations( + int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return); + ::ndk::ScopedAStatus hidlOpenStream(int32_t in_deviceId, int32_t in_streamId, + AidlNativeHandle* _aidl_return); + ::ndk::ScopedAStatus hidlCloseStream(int32_t in_deviceId, int32_t in_streamId); + + bool mIsHidl; + sp<HidlITvInput> mHidlTvInput; + std::shared_ptr<AidlITvInput> mAidlTvInput; + }; + + JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrapper> tvInput, + const sp<Looper>& looper); + + void hidlSetUpAudioInfo(JNIEnv* env, jobject& builder, const TvInputDeviceInfoWrapper& info); + void onDeviceAvailable(const TvInputDeviceInfoWrapper& info); + void onDeviceUnavailable(int deviceId); + void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus); + void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded); + + Mutex mLock; + Mutex mStreamLock; + jweak mThiz; + sp<Looper> mLooper; + + KeyedVector<int, KeyedVector<int, Connection> > mConnections; + + std::shared_ptr<ITvInputWrapper> mTvInput; + std::shared_ptr<TvInputCallback> mTvInputCallback; +}; + +} // namespace android diff --git a/services/core/jni/tvinput/OWNERS b/services/core/jni/tvinput/OWNERS new file mode 100644 index 000000000000..adc5827929dc --- /dev/null +++ b/services/core/jni/tvinput/OWNERS @@ -0,0 +1,3 @@ +include /media/java/android/media/tv/OWNERS + +yixiaoluo@google.com diff --git a/services/core/jni/tvinput/TvInputHal_hidl.cpp b/services/core/jni/tvinput/TvInputHal_hidl.cpp new file mode 100644 index 000000000000..37cf8445920f --- /dev/null +++ b/services/core/jni/tvinput/TvInputHal_hidl.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2022 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 "JTvInputHal.h" + +// Implement all HIDL related functions here. + +namespace android { + +void JTvInputHal::hidlSetUpAudioInfo(JNIEnv* env, jobject& builder, + const TvInputDeviceInfoWrapper& info) { + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioType, + info.hidlAudioType); + if (info.hidlAudioType != HidlAudioDevice::NONE) { + uint8_t buffer[info.hidlAudioAddress.size() + 1]; + memcpy(buffer, info.hidlAudioAddress.data(), info.hidlAudioAddress.size()); + buffer[info.hidlAudioAddress.size()] = '\0'; + jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char*>(buffer)); + env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, + audioAddress); + env->DeleteLocalRef(audioAddress); + } +} + +JTvInputHal::TvInputDeviceInfoWrapper +JTvInputHal::TvInputDeviceInfoWrapper::createDeviceInfoWrapper( + const HidlTvInputDeviceInfo& hidlTvInputDeviceInfo) { + TvInputDeviceInfoWrapper deviceInfo; + deviceInfo.isHidl = true; + deviceInfo.deviceId = hidlTvInputDeviceInfo.deviceId; + deviceInfo.type = TvInputType(static_cast<int32_t>(hidlTvInputDeviceInfo.type)); + deviceInfo.portId = hidlTvInputDeviceInfo.portId; + deviceInfo.cableConnectionStatus = CableConnectionStatus( + static_cast<int32_t>(hidlTvInputDeviceInfo.cableConnectionStatus)); + deviceInfo.hidlAudioType = hidlTvInputDeviceInfo.audioType; + deviceInfo.hidlAudioAddress = hidlTvInputDeviceInfo.audioAddress; + return deviceInfo; +} + +JTvInputHal::TvInputEventWrapper JTvInputHal::TvInputEventWrapper::createEventWrapper( + const HidlTvInputEvent& hidlTvInputEvent) { + TvInputEventWrapper event; + event.type = TvInputEventType(static_cast<int32_t>(hidlTvInputEvent.type)); + event.deviceInfo = + TvInputDeviceInfoWrapper::createDeviceInfoWrapper(hidlTvInputEvent.deviceInfo); + return event; +} + +Return<void> JTvInputHal::TvInputCallback::notify(const HidlTvInputEvent& event) { + mHal->mLooper->sendMessage(new NotifyHandler(mHal, + TvInputEventWrapper::createEventWrapper(event)), + static_cast<int>(event.type)); + return Void(); +} + +JTvInputHal::ITvInputWrapper::ITvInputWrapper(sp<HidlITvInput>& hidlTvInput) + : mIsHidl(true), mHidlTvInput(hidlTvInput) {} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlSetCallback( + const std::shared_ptr<TvInputCallback>& in_callback) { + mHidlTvInput->setCallback(in_callback == nullptr ? nullptr + : sp<TvInputCallback>(in_callback.get())); + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlGetStreamConfigurations( + int32_t in_deviceId, std::vector<AidlTvStreamConfig>* _aidl_return) { + Result result = Result::UNKNOWN; + hidl_vec<HidlTvStreamConfig> list; + mHidlTvInput->getStreamConfigurations(in_deviceId, + [&result, &list](Result res, + hidl_vec<HidlTvStreamConfig> configs) { + result = res; + if (res == Result::OK) { + list = configs; + } + }); + if (result != Result::OK) { + ALOGE("Couldn't get stream configs for device id:%d result:%d", in_deviceId, result); + return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result)); + } + for (size_t i = 0; i < list.size(); ++i) { + AidlTvStreamConfig config; + config.streamId = list[i].streamId; + config.maxVideoHeight = list[i].maxVideoHeight; + config.maxVideoWidth = list[i].maxVideoWidth; + _aidl_return->push_back(config); + } + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlOpenStream(int32_t in_deviceId, + int32_t in_streamId, + AidlNativeHandle* _aidl_return) { + Result result = Result::UNKNOWN; + native_handle_t* sidebandStream; + mHidlTvInput->openStream(in_deviceId, in_streamId, + [&result, &sidebandStream](Result res, const native_handle_t* handle) { + result = res; + if (res == Result::OK) { + if (handle) { + sidebandStream = native_handle_clone(handle); + } + } + }); + if (result != Result::OK) { + ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", in_deviceId, in_streamId, + result); + return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result)); + } + *_aidl_return = makeToAidl(sidebandStream); + native_handle_delete(sidebandStream); + return ::ndk::ScopedAStatus::ok(); +} + +::ndk::ScopedAStatus JTvInputHal::ITvInputWrapper::hidlCloseStream(int32_t in_deviceId, + int32_t in_streamId) { + Result result = mHidlTvInput->closeStream(in_deviceId, in_streamId); + if (result != Result::OK) { + return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(result)); + } + return ::ndk::ScopedAStatus::ok(); +} + +} // namespace android diff --git a/services/core/jni/tvinput/TvInputHal_hidl.h b/services/core/jni/tvinput/TvInputHal_hidl.h new file mode 100644 index 000000000000..948f86ba679c --- /dev/null +++ b/services/core/jni/tvinput/TvInputHal_hidl.h @@ -0,0 +1,35 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +// Include all HIDL related files/names here. + +#include <android/hardware/tv/input/1.0/ITvInput.h> +#include <android/hardware/tv/input/1.0/ITvInputCallback.h> +#include <android/hardware/tv/input/1.0/types.h> + +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::hardware::tv::input::V1_0::Result; + +using HidlAudioDevice = ::android::hardware::audio::common::V2_0::AudioDevice; +using HidlITvInput = ::android::hardware::tv::input::V1_0::ITvInput; +using HidlITvInputCallback = ::android::hardware::tv::input::V1_0::ITvInputCallback; +using HidlTvInputDeviceInfo = ::android::hardware::tv::input::V1_0::TvInputDeviceInfo; +using HidlTvInputEvent = ::android::hardware::tv::input::V1_0::TvInputEvent; +using HidlTvStreamConfig = ::android::hardware::tv::input::V1_0::TvStreamConfig; diff --git a/services/core/jni/tvinput/jstruct.h b/services/core/jni/tvinput/jstruct.h new file mode 100644 index 000000000000..0a4a64dbb40c --- /dev/null +++ b/services/core/jni/tvinput/jstruct.h @@ -0,0 +1,55 @@ +/* + * Copyright 2022 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. + */ + +#pragma once + +#include "jni.h" + +typedef struct { + jmethodID deviceAvailable; + jmethodID deviceUnavailable; + jmethodID streamConfigsChanged; + jmethodID firstFrameCaptured; +} gTvInputHalClassInfoType; + +typedef struct { + jclass clazz; +} gTvStreamConfigClassInfoType; + +typedef struct { + jclass clazz; + + jmethodID constructor; + jmethodID streamId; + jmethodID type; + jmethodID maxWidth; + jmethodID maxHeight; + jmethodID generation; + jmethodID build; +} gTvStreamConfigBuilderClassInfoType; + +typedef struct { + jclass clazz; + + jmethodID constructor; + jmethodID deviceId; + jmethodID type; + jmethodID hdmiPortId; + jmethodID cableConnectionStatus; + jmethodID audioType; + jmethodID audioAddress; + jmethodID build; +} gTvInputHardwareInfoBuilderClassInfoType; |