summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hongguang Chen <hgchen@google.com> 2022-12-06 07:23:56 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-12-06 07:23:56 +0000
commit8a978a12c11e35c72d92e1ed44dc6d01f8cf12df (patch)
tree916bcc46f80a795a30c7c1784cb3ad84512a2e68
parent34f0af63c482b2a2e519e6828c74a70492abbff3 (diff)
parent412ff42f26f3914680187a269ef2b348374d3bae (diff)
Merge "TV Input 2.0 JNI"
-rw-r--r--services/core/jni/Android.bp6
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp574
-rw-r--r--services/core/jni/tvinput/BufferProducerThread.cpp147
-rw-r--r--services/core/jni/tvinput/BufferProducerThread.h56
-rw-r--r--services/core/jni/tvinput/JTvInputHal.cpp378
-rw-r--r--services/core/jni/tvinput/JTvInputHal.h185
-rw-r--r--services/core/jni/tvinput/OWNERS3
-rw-r--r--services/core/jni/tvinput/TvInputHal_hidl.cpp138
-rw-r--r--services/core/jni/tvinput/TvInputHal_hidl.h35
-rw-r--r--services/core/jni/tvinput/jstruct.h55
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;