Merge "TV Input 2.0 JNI"

This commit is contained in:
Hongguang Chen
2022-12-06 07:23:56 +00:00
committed by Android (Google) Code Review
10 changed files with 1009 additions and 568 deletions

View File

@@ -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: {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,3 @@
include /media/java/android/media/tv/OWNERS
yixiaoluo@google.com

View File

@@ -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

View File

@@ -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;

View File

@@ -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;