blob: e34a8e8a8cf87e62f5b5169b2ab31150b79415a2 [file] [log] [blame]
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "TvInputHal"
18
19//#define LOG_NDEBUG 0
20
Wonsik Kima6170742014-12-17 11:18:36 +090021#include "android_os_MessageQueue.h"
Wonsik Kimc22dbb62014-05-26 02:26:04 +000022#include "android_runtime/AndroidRuntime.h"
23#include "android_runtime/android_view_Surface.h"
24#include "JNIHelp.h"
25#include "jni.h"
26
27#include <gui/Surface.h>
28#include <utils/Errors.h>
29#include <utils/KeyedVector.h>
30#include <utils/Log.h>
Wonsik Kima6170742014-12-17 11:18:36 +090031#include <utils/Looper.h>
Wonsik Kimc22dbb62014-05-26 02:26:04 +000032#include <utils/NativeHandle.h>
33#include <hardware/tv_input.h>
34
35namespace android {
36
37static struct {
38 jmethodID deviceAvailable;
39 jmethodID deviceUnavailable;
40 jmethodID streamConfigsChanged;
Terry Heoc086a3d2014-06-18 14:26:44 +090041 jmethodID firstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000042} gTvInputHalClassInfo;
43
44static struct {
45 jclass clazz;
46} gTvStreamConfigClassInfo;
47
48static struct {
49 jclass clazz;
50
51 jmethodID constructor;
52 jmethodID streamId;
53 jmethodID type;
54 jmethodID maxWidth;
55 jmethodID maxHeight;
56 jmethodID generation;
57 jmethodID build;
58} gTvStreamConfigBuilderClassInfo;
59
Wonsik Kimd7c29182014-05-27 10:38:21 +090060static struct {
61 jclass clazz;
62
63 jmethodID constructor;
64 jmethodID deviceId;
65 jmethodID type;
Wonsik Kima358b532014-06-12 14:56:18 +090066 jmethodID hdmiPortId;
Wonsik Kimd7c29182014-05-27 10:38:21 +090067 jmethodID audioType;
68 jmethodID audioAddress;
69 jmethodID build;
70} gTvInputHardwareInfoBuilderClassInfo;
71
Wonsik Kimc22dbb62014-05-26 02:26:04 +000072////////////////////////////////////////////////////////////////////////////////
73
Wonsik Kim0b2691b2014-05-23 16:07:55 +090074class BufferProducerThread : public Thread {
75public:
76 BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
77
78 virtual status_t readyToRun();
79
80 void setSurface(const sp<Surface>& surface);
81 void onCaptured(uint32_t seq, bool succeeded);
82 void shutdown();
83
84private:
85 Mutex mLock;
86 Condition mCondition;
87 sp<Surface> mSurface;
88 tv_input_device_t* mDevice;
89 int mDeviceId;
90 tv_stream_t mStream;
91 sp<ANativeWindowBuffer_t> mBuffer;
92 enum {
93 CAPTURING,
94 CAPTURED,
95 RELEASED,
96 } mBufferState;
97 uint32_t mSeq;
98 bool mShutdown;
99
100 virtual bool threadLoop();
101
102 void setSurfaceLocked(const sp<Surface>& surface);
103};
104
105BufferProducerThread::BufferProducerThread(
106 tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
107 : Thread(false),
108 mDevice(device),
109 mDeviceId(deviceId),
110 mBuffer(NULL),
111 mBufferState(RELEASED),
112 mSeq(0u),
113 mShutdown(false) {
114 memcpy(&mStream, stream, sizeof(mStream));
115}
116
117status_t BufferProducerThread::readyToRun() {
118 sp<ANativeWindow> anw(mSurface);
119 status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
120 if (err != NO_ERROR) {
121 return err;
122 }
123 err = native_window_set_buffers_dimensions(
124 anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
125 if (err != NO_ERROR) {
126 return err;
127 }
128 err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
129 if (err != NO_ERROR) {
130 return err;
131 }
132 return NO_ERROR;
133}
134
135void BufferProducerThread::setSurface(const sp<Surface>& surface) {
136 Mutex::Autolock autoLock(&mLock);
137 setSurfaceLocked(surface);
138}
139
140void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
141 if (surface == mSurface) {
142 return;
143 }
144
145 if (mBufferState == CAPTURING) {
146 mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
147 }
148 while (mBufferState == CAPTURING) {
149 status_t err = mCondition.waitRelative(mLock, s2ns(1));
150 if (err != NO_ERROR) {
151 ALOGE("error %d while wating for buffer state to change.", err);
152 break;
153 }
154 }
155 mBuffer.clear();
156 mBufferState = RELEASED;
157
158 mSurface = surface;
159 mCondition.broadcast();
160}
161
162void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
163 Mutex::Autolock autoLock(&mLock);
164 if (seq != mSeq) {
165 ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
166 }
167 if (mBufferState != CAPTURING) {
168 ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
169 }
170 if (succeeded) {
171 mBufferState = CAPTURED;
172 } else {
173 mBuffer.clear();
174 mBufferState = RELEASED;
175 }
176 mCondition.broadcast();
177}
178
179void BufferProducerThread::shutdown() {
180 Mutex::Autolock autoLock(&mLock);
181 mShutdown = true;
182 setSurfaceLocked(NULL);
183 requestExitAndWait();
184}
185
186bool BufferProducerThread::threadLoop() {
187 Mutex::Autolock autoLock(&mLock);
188
189 status_t err = NO_ERROR;
190 if (mSurface == NULL) {
191 err = mCondition.waitRelative(mLock, s2ns(1));
192 // It's OK to time out here.
193 if (err != NO_ERROR && err != TIMED_OUT) {
194 ALOGE("error %d while wating for non-null surface to be set", err);
195 return false;
196 }
197 return true;
198 }
199 sp<ANativeWindow> anw(mSurface);
200 while (mBufferState == CAPTURING) {
201 err = mCondition.waitRelative(mLock, s2ns(1));
202 if (err != NO_ERROR) {
203 ALOGE("error %d while wating for buffer state to change.", err);
204 return false;
205 }
206 }
207 if (mBufferState == CAPTURED && anw != NULL) {
208 err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
209 if (err != NO_ERROR) {
210 ALOGE("error %d while queueing buffer to surface", err);
211 return false;
212 }
213 mBuffer.clear();
214 mBufferState = RELEASED;
215 }
216 if (mBuffer == NULL && !mShutdown && anw != NULL) {
217 ANativeWindowBuffer_t* buffer = NULL;
218 err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
219 if (err != NO_ERROR) {
220 ALOGE("error %d while dequeueing buffer to surface", err);
221 return false;
222 }
223 mBuffer = buffer;
224 mBufferState = CAPTURING;
225 mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
226 buffer->handle, ++mSeq);
227 }
228
229 return true;
230}
231
232////////////////////////////////////////////////////////////////////////////////
233
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000234class JTvInputHal {
235public:
236 ~JTvInputHal();
237
Wonsik Kima6170742014-12-17 11:18:36 +0900238 static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000239
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900240 int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900241 int removeStream(int deviceId, int streamId);
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700242 const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000243
Wonsik Kima6170742014-12-17 11:18:36 +0900244 void onDeviceAvailable(const tv_input_device_info_t& info);
245 void onDeviceUnavailable(int deviceId);
246 void onStreamConfigurationsChanged(int deviceId);
247 void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
248
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000249private:
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900250 // Connection between a surface and a stream.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000251 class Connection {
252 public:
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900253 Connection() {}
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000254
255 sp<Surface> mSurface;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900256 tv_stream_type_t mStreamType;
257
258 // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000259 sp<NativeHandle> mSourceHandle;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900260 // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
261 sp<BufferProducerThread> mThread;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000262 };
263
Wonsik Kima6170742014-12-17 11:18:36 +0900264 class NotifyHandler : public MessageHandler {
265 public:
266 NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event);
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900267 ~NotifyHandler();
Wonsik Kima6170742014-12-17 11:18:36 +0900268
269 virtual void handleMessage(const Message& message);
270
271 private:
272 tv_input_event_t mEvent;
273 JTvInputHal* mHal;
274 };
275
276 JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000277
278 static void notify(
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900279 tv_input_device_t* dev, tv_input_event_t* event, void* data);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000280
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900281 static void cloneTvInputEvent(
282 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent);
283
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900284 Mutex mLock;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000285 jweak mThiz;
286 tv_input_device_t* mDevice;
287 tv_input_callback_ops_t mCallback;
Wonsik Kima6170742014-12-17 11:18:36 +0900288 sp<Looper> mLooper;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000289
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900290 KeyedVector<int, KeyedVector<int, Connection> > mConnections;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000291};
292
Wonsik Kima6170742014-12-17 11:18:36 +0900293JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700294 const sp<Looper>& looper) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000295 mThiz = env->NewWeakGlobalRef(thiz);
296 mDevice = device;
297 mCallback.notify = &JTvInputHal::notify;
Wonsik Kima6170742014-12-17 11:18:36 +0900298 mLooper = looper;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000299
300 mDevice->initialize(mDevice, &mCallback, this);
301}
302
303JTvInputHal::~JTvInputHal() {
304 mDevice->common.close((hw_device_t*)mDevice);
305
306 JNIEnv* env = AndroidRuntime::getJNIEnv();
307 env->DeleteWeakGlobalRef(mThiz);
308 mThiz = NULL;
309}
310
Wonsik Kima6170742014-12-17 11:18:36 +0900311JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000312 tv_input_module_t* module = NULL;
313 status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
314 (hw_module_t const**)&module);
315 if (err) {
316 ALOGE("Couldn't load %s module (%s)",
317 TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
318 return 0;
319 }
320
321 tv_input_device_t* device = NULL;
322 err = module->common.methods->open(
323 (hw_module_t*)module,
324 TV_INPUT_DEFAULT_DEVICE,
325 (hw_device_t**)&device);
326 if (err) {
327 ALOGE("Couldn't open %s device (%s)",
328 TV_INPUT_DEFAULT_DEVICE, strerror(-err));
329 return 0;
330 }
331
Wonsik Kima6170742014-12-17 11:18:36 +0900332 return new JTvInputHal(env, thiz, device, looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000333}
334
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900335int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900336 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
337 if (connections.indexOfKey(streamId) < 0) {
338 connections.add(streamId, Connection());
339 }
340 Connection& connection = connections.editValueFor(streamId);
341 if (connection.mSurface == surface) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000342 // Nothing to do
343 return NO_ERROR;
344 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900345 // Clear the surface in the connection.
346 if (connection.mSurface != NULL) {
347 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
348 if (Surface::isValid(connection.mSurface)) {
349 connection.mSurface->setSidebandStream(NULL);
350 }
351 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000352 connection.mSurface.clear();
353 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900354 if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000355 // Need to configure stream
356 int numConfigs = 0;
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700357 const tv_stream_config_t* configs = NULL;
358 if (mDevice->get_stream_configurations(
359 mDevice, deviceId, &numConfigs, &configs) != 0) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000360 ALOGE("Couldn't get stream configs");
361 return UNKNOWN_ERROR;
362 }
363 int configIndex = -1;
364 for (int i = 0; i < numConfigs; ++i) {
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700365 if (configs[i].stream_id == streamId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000366 configIndex = i;
367 break;
368 }
369 }
370 if (configIndex == -1) {
371 ALOGE("Cannot find a config with given stream ID: %d", streamId);
372 return BAD_VALUE;
373 }
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700374 connection.mStreamType = configs[configIndex].type;
Wonsik Kim21aa3462014-07-29 16:39:00 +0900375
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000376 tv_stream_t stream;
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700377 stream.stream_id = configs[configIndex].stream_id;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900378 if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700379 stream.buffer_producer.width = configs[configIndex].max_video_width;
380 stream.buffer_producer.height = configs[configIndex].max_video_height;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900381 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000382 if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
383 ALOGE("Couldn't add stream");
384 return UNKNOWN_ERROR;
385 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900386 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
387 connection.mSourceHandle = NativeHandle::create(
388 stream.sideband_stream_source_handle, false);
389 } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
390 if (connection.mThread != NULL) {
391 connection.mThread->shutdown();
392 }
393 connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
Brian Carlstrom66f48312016-03-12 16:07:48 -0800394 connection.mThread->run("BufferProducerThread");
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900395 }
396 }
397 connection.mSurface = surface;
398 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000399 connection.mSurface->setSidebandStream(connection.mSourceHandle);
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900400 } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
401 connection.mThread->setSurface(surface);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000402 }
403 return NO_ERROR;
404}
405
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900406int JTvInputHal::removeStream(int deviceId, int streamId) {
407 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
408 if (connections.indexOfKey(streamId) < 0) {
409 return BAD_VALUE;
410 }
411 Connection& connection = connections.editValueFor(streamId);
412 if (connection.mSurface == NULL) {
413 // Nothing to do
414 return NO_ERROR;
415 }
416 if (Surface::isValid(connection.mSurface)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900417 connection.mSurface->setSidebandStream(NULL);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900418 }
Wonsik Kim6b1e6952015-10-30 10:36:56 +0900419 connection.mSurface.clear();
Wonsik Kimb82de362014-08-05 15:35:07 +0900420 if (connection.mThread != NULL) {
421 connection.mThread->shutdown();
422 connection.mThread.clear();
423 }
424 if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
425 ALOGE("Couldn't remove stream");
426 return BAD_VALUE;
427 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900428 if (connection.mSourceHandle != NULL) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900429 connection.mSourceHandle.clear();
430 }
431 return NO_ERROR;
432}
433
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700434const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
435 const tv_stream_config_t* configs = NULL;
436 if (mDevice->get_stream_configurations(
437 mDevice, deviceId, numConfigs, &configs) != 0) {
438 ALOGE("Couldn't get stream configs");
439 return NULL;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000440 }
441 return configs;
442}
443
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000444// static
445void JTvInputHal::notify(
446 tv_input_device_t* dev, tv_input_event_t* event, void* data) {
447 JTvInputHal* thiz = (JTvInputHal*)data;
Wonsik Kima6170742014-12-17 11:18:36 +0900448 thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000449}
450
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900451// static
452void JTvInputHal::cloneTvInputEvent(
453 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) {
454 memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t));
455 if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
456 srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
457 srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
458 srcEvent->device_info.audio_address != NULL){
459 char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1];
460 strcpy(audio_address, srcEvent->device_info.audio_address);
461 dstEvent->device_info.audio_address = audio_address;
462 }
463}
464
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000465void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900466 {
467 Mutex::Autolock autoLock(&mLock);
468 mConnections.add(info.device_id, KeyedVector<int, Connection>());
469 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000470 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900471
472 jobject builder = env->NewObject(
473 gTvInputHardwareInfoBuilderClassInfo.clazz,
474 gTvInputHardwareInfoBuilderClassInfo.constructor);
475 env->CallObjectMethod(
476 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
477 env->CallObjectMethod(
478 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
Wonsik Kima358b532014-06-12 14:56:18 +0900479 if (info.type == TV_INPUT_TYPE_HDMI) {
480 env->CallObjectMethod(
481 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
482 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900483 env->CallObjectMethod(
484 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
485 if (info.audio_type != AUDIO_DEVICE_NONE) {
486 jstring audioAddress = env->NewStringUTF(info.audio_address);
487 env->CallObjectMethod(
488 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
489 env->DeleteLocalRef(audioAddress);
490 }
491
492 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
493
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000494 env->CallVoidMethod(
495 mThiz,
496 gTvInputHalClassInfo.deviceAvailable,
Wonsik Kimd7c29182014-05-27 10:38:21 +0900497 infoObject);
498
499 env->DeleteLocalRef(builder);
500 env->DeleteLocalRef(infoObject);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000501}
502
503void JTvInputHal::onDeviceUnavailable(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900504 {
505 Mutex::Autolock autoLock(&mLock);
506 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
507 for (size_t i = 0; i < connections.size(); ++i) {
508 removeStream(deviceId, connections.keyAt(i));
509 }
510 connections.clear();
511 mConnections.removeItem(deviceId);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900512 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900513 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000514 env->CallVoidMethod(
515 mThiz,
516 gTvInputHalClassInfo.deviceUnavailable,
517 deviceId);
518}
519
520void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900521 {
522 Mutex::Autolock autoLock(&mLock);
523 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
524 for (size_t i = 0; i < connections.size(); ++i) {
525 removeStream(deviceId, connections.keyAt(i));
526 }
527 connections.clear();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900528 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900529 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000530 env->CallVoidMethod(
531 mThiz,
532 gTvInputHalClassInfo.streamConfigsChanged,
533 deviceId);
534}
535
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900536void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
537 sp<BufferProducerThread> thread;
538 {
539 Mutex::Autolock autoLock(&mLock);
540 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
541 Connection& connection = connections.editValueFor(streamId);
542 if (connection.mThread == NULL) {
543 ALOGE("capture thread not existing.");
544 return;
545 }
546 thread = connection.mThread;
547 }
548 thread->onCaptured(seq, succeeded);
Terry Heoc086a3d2014-06-18 14:26:44 +0900549 if (seq == 0) {
550 JNIEnv* env = AndroidRuntime::getJNIEnv();
551 env->CallVoidMethod(
552 mThiz,
553 gTvInputHalClassInfo.firstFrameCaptured,
554 deviceId,
555 streamId);
556 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900557}
558
Wonsik Kima6170742014-12-17 11:18:36 +0900559JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
560 mHal = hal;
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900561 cloneTvInputEvent(&mEvent, event);
562}
563
564JTvInputHal::NotifyHandler::~NotifyHandler() {
565 if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
566 mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
567 mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
568 mEvent.device_info.audio_address != NULL) {
569 delete mEvent.device_info.audio_address;
570 }
Wonsik Kima6170742014-12-17 11:18:36 +0900571}
572
573void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
574 switch (mEvent.type) {
575 case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
576 mHal->onDeviceAvailable(mEvent.device_info);
577 } break;
578 case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
579 mHal->onDeviceUnavailable(mEvent.device_info.device_id);
580 } break;
581 case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
582 mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
583 } break;
584 case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
585 mHal->onCaptured(mEvent.capture_result.device_id,
586 mEvent.capture_result.stream_id,
587 mEvent.capture_result.seq,
588 true /* succeeded */);
589 } break;
590 case TV_INPUT_EVENT_CAPTURE_FAILED: {
591 mHal->onCaptured(mEvent.capture_result.device_id,
592 mEvent.capture_result.stream_id,
593 mEvent.capture_result.seq,
594 false /* succeeded */);
595 } break;
596 default:
597 ALOGE("Unrecognizable event");
598 }
599}
600
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000601////////////////////////////////////////////////////////////////////////////////
602
Wonsik Kima6170742014-12-17 11:18:36 +0900603static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
604 sp<MessageQueue> messageQueue =
605 android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
606 return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000607}
608
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900609static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000610 jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
611 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900612 if (!jsurface) {
613 return BAD_VALUE;
614 }
615 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
Wonsik Kim6b1e6952015-10-30 10:36:56 +0900616 if (!Surface::isValid(surface)) {
617 return BAD_VALUE;
618 }
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900619 return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900620}
621
622static int nativeRemoveStream(JNIEnv* env, jclass clazz,
623 jlong ptr, jint deviceId, jint streamId) {
624 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
625 return tvInputHal->removeStream(deviceId, streamId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000626}
627
628static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
629 jlong ptr, jint deviceId, jint generation) {
630 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
631 int numConfigs = 0;
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700632 const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000633
634 jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
635 for (int i = 0; i < numConfigs; ++i) {
636 jobject builder = env->NewObject(
637 gTvStreamConfigBuilderClassInfo.clazz,
638 gTvStreamConfigBuilderClassInfo.constructor);
639 env->CallObjectMethod(
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700640 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000641 env->CallObjectMethod(
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700642 builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000643 env->CallObjectMethod(
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700644 builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000645 env->CallObjectMethod(
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700646 builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000647 env->CallObjectMethod(
648 builder, gTvStreamConfigBuilderClassInfo.generation, generation);
649
650 jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
651
652 env->SetObjectArrayElement(result, i, config);
653
654 env->DeleteLocalRef(config);
655 env->DeleteLocalRef(builder);
656 }
657 return result;
658}
659
660static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
661 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
662 delete tvInputHal;
663}
664
Daniel Micay76f6a862015-09-19 17:31:01 -0400665static const JNINativeMethod gTvInputHalMethods[] = {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000666 /* name, signature, funcPtr */
Wonsik Kima6170742014-12-17 11:18:36 +0900667 { "nativeOpen", "(Landroid/os/MessageQueue;)J",
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000668 (void*) nativeOpen },
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900669 { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
670 (void*) nativeAddOrUpdateStream },
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900671 { "nativeRemoveStream", "(JII)I",
672 (void*) nativeRemoveStream },
Jae Seod5cc4a22014-05-30 16:57:43 -0700673 { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000674 (void*) nativeGetStreamConfigs },
675 { "nativeClose", "(J)V",
676 (void*) nativeClose },
677};
678
679#define FIND_CLASS(var, className) \
680 var = env->FindClass(className); \
Chih-Hung Hsieh6c896162016-05-19 15:29:38 -0700681 LOG_FATAL_IF(! (var), "Unable to find class " className)
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000682
683#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
684 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
Chih-Hung Hsieh6c896162016-05-19 15:29:38 -0700685 LOG_FATAL_IF(! (var), "Unable to find method" methodName)
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000686
687int register_android_server_tv_TvInputHal(JNIEnv* env) {
688 int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
689 gTvInputHalMethods, NELEM(gTvInputHalMethods));
690 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
Bernhard Rosenkränzer4048a4b2014-11-23 22:24:32 +0100691 (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000692
693 jclass clazz;
694 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
695
696 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900697 gTvInputHalClassInfo.deviceAvailable, clazz,
698 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000699 GET_METHOD_ID(
700 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
701 GET_METHOD_ID(
702 gTvInputHalClassInfo.streamConfigsChanged, clazz,
703 "streamConfigsChangedFromNative", "(I)V");
Terry Heoc086a3d2014-06-18 14:26:44 +0900704 GET_METHOD_ID(
705 gTvInputHalClassInfo.firstFrameCaptured, clazz,
706 "firstFrameCapturedFromNative", "(II)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000707
Jae Seod5cc4a22014-05-30 16:57:43 -0700708 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000709 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
710
Jae Seod5cc4a22014-05-30 16:57:43 -0700711 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000712 gTvStreamConfigBuilderClassInfo.clazz =
713 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
714
715 GET_METHOD_ID(
716 gTvStreamConfigBuilderClassInfo.constructor,
717 gTvStreamConfigBuilderClassInfo.clazz,
718 "<init>", "()V");
719 GET_METHOD_ID(
720 gTvStreamConfigBuilderClassInfo.streamId,
721 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700722 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000723 GET_METHOD_ID(
724 gTvStreamConfigBuilderClassInfo.type,
725 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700726 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000727 GET_METHOD_ID(
728 gTvStreamConfigBuilderClassInfo.maxWidth,
729 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700730 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000731 GET_METHOD_ID(
732 gTvStreamConfigBuilderClassInfo.maxHeight,
733 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700734 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000735 GET_METHOD_ID(
736 gTvStreamConfigBuilderClassInfo.generation,
737 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700738 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000739 GET_METHOD_ID(
740 gTvStreamConfigBuilderClassInfo.build,
741 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700742 "build", "()Landroid/media/tv/TvStreamConfig;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000743
Wonsik Kimd7c29182014-05-27 10:38:21 +0900744 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
745 "android/media/tv/TvInputHardwareInfo$Builder");
746 gTvInputHardwareInfoBuilderClassInfo.clazz =
747 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
748
749 GET_METHOD_ID(
750 gTvInputHardwareInfoBuilderClassInfo.constructor,
751 gTvInputHardwareInfoBuilderClassInfo.clazz,
752 "<init>", "()V");
753 GET_METHOD_ID(
754 gTvInputHardwareInfoBuilderClassInfo.deviceId,
755 gTvInputHardwareInfoBuilderClassInfo.clazz,
756 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
757 GET_METHOD_ID(
758 gTvInputHardwareInfoBuilderClassInfo.type,
759 gTvInputHardwareInfoBuilderClassInfo.clazz,
760 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
761 GET_METHOD_ID(
Wonsik Kima358b532014-06-12 14:56:18 +0900762 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
763 gTvInputHardwareInfoBuilderClassInfo.clazz,
764 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
765 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900766 gTvInputHardwareInfoBuilderClassInfo.audioType,
767 gTvInputHardwareInfoBuilderClassInfo.clazz,
768 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
769 GET_METHOD_ID(
770 gTvInputHardwareInfoBuilderClassInfo.audioAddress,
771 gTvInputHardwareInfoBuilderClassInfo.clazz,
772 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
773 GET_METHOD_ID(
774 gTvInputHardwareInfoBuilderClassInfo.build,
775 gTvInputHardwareInfoBuilderClassInfo.clazz,
776 "build", "()Landroid/media/tv/TvInputHardwareInfo;");
777
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000778 return 0;
779}
780
781} /* namespace android */