blob: 6c640ba55042d130ff2ba0d50962c7147e5f3a8b [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;
Wonsik Kim102d0b72015-11-26 18:51:09 +090057 jmethodID flags;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000058 jmethodID build;
59} gTvStreamConfigBuilderClassInfo;
60
Wonsik Kimd7c29182014-05-27 10:38:21 +090061static struct {
62 jclass clazz;
63
64 jmethodID constructor;
65 jmethodID deviceId;
66 jmethodID type;
Wonsik Kima358b532014-06-12 14:56:18 +090067 jmethodID hdmiPortId;
Wonsik Kimd7c29182014-05-27 10:38:21 +090068 jmethodID audioType;
69 jmethodID audioAddress;
70 jmethodID build;
71} gTvInputHardwareInfoBuilderClassInfo;
72
Wonsik Kimc22dbb62014-05-26 02:26:04 +000073////////////////////////////////////////////////////////////////////////////////
74
Wonsik Kim0b2691b2014-05-23 16:07:55 +090075class BufferProducerThread : public Thread {
76public:
77 BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
78
79 virtual status_t readyToRun();
80
81 void setSurface(const sp<Surface>& surface);
82 void onCaptured(uint32_t seq, bool succeeded);
83 void shutdown();
84
85private:
86 Mutex mLock;
87 Condition mCondition;
88 sp<Surface> mSurface;
89 tv_input_device_t* mDevice;
90 int mDeviceId;
91 tv_stream_t mStream;
92 sp<ANativeWindowBuffer_t> mBuffer;
93 enum {
94 CAPTURING,
95 CAPTURED,
96 RELEASED,
97 } mBufferState;
98 uint32_t mSeq;
99 bool mShutdown;
100
101 virtual bool threadLoop();
102
103 void setSurfaceLocked(const sp<Surface>& surface);
104};
105
106BufferProducerThread::BufferProducerThread(
107 tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
108 : Thread(false),
109 mDevice(device),
110 mDeviceId(deviceId),
111 mBuffer(NULL),
112 mBufferState(RELEASED),
113 mSeq(0u),
114 mShutdown(false) {
115 memcpy(&mStream, stream, sizeof(mStream));
116}
117
118status_t BufferProducerThread::readyToRun() {
119 sp<ANativeWindow> anw(mSurface);
120 status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
121 if (err != NO_ERROR) {
122 return err;
123 }
124 err = native_window_set_buffers_dimensions(
125 anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
126 if (err != NO_ERROR) {
127 return err;
128 }
129 err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
130 if (err != NO_ERROR) {
131 return err;
132 }
133 return NO_ERROR;
134}
135
136void BufferProducerThread::setSurface(const sp<Surface>& surface) {
137 Mutex::Autolock autoLock(&mLock);
138 setSurfaceLocked(surface);
139}
140
141void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
142 if (surface == mSurface) {
143 return;
144 }
145
146 if (mBufferState == CAPTURING) {
147 mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
148 }
149 while (mBufferState == CAPTURING) {
150 status_t err = mCondition.waitRelative(mLock, s2ns(1));
151 if (err != NO_ERROR) {
152 ALOGE("error %d while wating for buffer state to change.", err);
153 break;
154 }
155 }
156 mBuffer.clear();
157 mBufferState = RELEASED;
158
159 mSurface = surface;
160 mCondition.broadcast();
161}
162
163void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
164 Mutex::Autolock autoLock(&mLock);
165 if (seq != mSeq) {
166 ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
167 }
168 if (mBufferState != CAPTURING) {
169 ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
170 }
171 if (succeeded) {
172 mBufferState = CAPTURED;
173 } else {
174 mBuffer.clear();
175 mBufferState = RELEASED;
176 }
177 mCondition.broadcast();
178}
179
180void BufferProducerThread::shutdown() {
181 Mutex::Autolock autoLock(&mLock);
182 mShutdown = true;
183 setSurfaceLocked(NULL);
184 requestExitAndWait();
185}
186
187bool BufferProducerThread::threadLoop() {
188 Mutex::Autolock autoLock(&mLock);
189
190 status_t err = NO_ERROR;
191 if (mSurface == NULL) {
192 err = mCondition.waitRelative(mLock, s2ns(1));
193 // It's OK to time out here.
194 if (err != NO_ERROR && err != TIMED_OUT) {
195 ALOGE("error %d while wating for non-null surface to be set", err);
196 return false;
197 }
198 return true;
199 }
200 sp<ANativeWindow> anw(mSurface);
201 while (mBufferState == CAPTURING) {
202 err = mCondition.waitRelative(mLock, s2ns(1));
203 if (err != NO_ERROR) {
204 ALOGE("error %d while wating for buffer state to change.", err);
205 return false;
206 }
207 }
208 if (mBufferState == CAPTURED && anw != NULL) {
209 err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
210 if (err != NO_ERROR) {
211 ALOGE("error %d while queueing buffer to surface", err);
212 return false;
213 }
214 mBuffer.clear();
215 mBufferState = RELEASED;
216 }
217 if (mBuffer == NULL && !mShutdown && anw != NULL) {
218 ANativeWindowBuffer_t* buffer = NULL;
219 err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
220 if (err != NO_ERROR) {
221 ALOGE("error %d while dequeueing buffer to surface", err);
222 return false;
223 }
224 mBuffer = buffer;
225 mBufferState = CAPTURING;
226 mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
227 buffer->handle, ++mSeq);
228 }
229
230 return true;
231}
232
233////////////////////////////////////////////////////////////////////////////////
234
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000235class JTvInputHal {
236public:
237 ~JTvInputHal();
238
Wonsik Kima6170742014-12-17 11:18:36 +0900239 static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000240
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900241 int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900242 int removeStream(int deviceId, int streamId);
Wonsik Kim102d0b72015-11-26 18:51:09 +0900243 const tv_stream_config_ext_t* getStreamConfigs(int deviceId, int* numConfigs);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000244
Wonsik Kima6170742014-12-17 11:18:36 +0900245 void onDeviceAvailable(const tv_input_device_info_t& info);
246 void onDeviceUnavailable(int deviceId);
247 void onStreamConfigurationsChanged(int deviceId);
248 void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
249
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000250private:
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900251 // Connection between a surface and a stream.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000252 class Connection {
253 public:
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900254 Connection() {}
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000255
256 sp<Surface> mSurface;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900257 tv_stream_type_t mStreamType;
258
259 // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000260 sp<NativeHandle> mSourceHandle;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900261 // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
262 sp<BufferProducerThread> mThread;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000263 };
264
Wonsik Kima6170742014-12-17 11:18:36 +0900265 class NotifyHandler : public MessageHandler {
266 public:
267 NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event);
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900268 ~NotifyHandler();
Wonsik Kima6170742014-12-17 11:18:36 +0900269
270 virtual void handleMessage(const Message& message);
271
272 private:
273 tv_input_event_t mEvent;
274 JTvInputHal* mHal;
275 };
276
277 JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000278
279 static void notify(
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900280 tv_input_device_t* dev, tv_input_event_t* event, void* data);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000281
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900282 static void cloneTvInputEvent(
283 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent);
284
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900285 Mutex mLock;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000286 jweak mThiz;
287 tv_input_device_t* mDevice;
288 tv_input_callback_ops_t mCallback;
Wonsik Kima6170742014-12-17 11:18:36 +0900289 sp<Looper> mLooper;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000290
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900291 KeyedVector<int, KeyedVector<int, Connection> > mConnections;
Wonsik Kim102d0b72015-11-26 18:51:09 +0900292
293 tv_stream_config_ext_t* mConfigBuffer;
294 int mConfigBufferSize;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000295};
296
Wonsik Kima6170742014-12-17 11:18:36 +0900297JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
Wonsik Kim102d0b72015-11-26 18:51:09 +0900298 const sp<Looper>& looper)
299 : mConfigBuffer(NULL),
300 mConfigBufferSize(0) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000301 mThiz = env->NewWeakGlobalRef(thiz);
302 mDevice = device;
303 mCallback.notify = &JTvInputHal::notify;
Wonsik Kima6170742014-12-17 11:18:36 +0900304 mLooper = looper;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000305
306 mDevice->initialize(mDevice, &mCallback, this);
307}
308
309JTvInputHal::~JTvInputHal() {
310 mDevice->common.close((hw_device_t*)mDevice);
311
312 JNIEnv* env = AndroidRuntime::getJNIEnv();
313 env->DeleteWeakGlobalRef(mThiz);
314 mThiz = NULL;
Wonsik Kim102d0b72015-11-26 18:51:09 +0900315
316 if (mConfigBuffer != NULL) {
317 delete[] mConfigBuffer;
318 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000319}
320
Wonsik Kima6170742014-12-17 11:18:36 +0900321JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000322 tv_input_module_t* module = NULL;
323 status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
324 (hw_module_t const**)&module);
325 if (err) {
326 ALOGE("Couldn't load %s module (%s)",
327 TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
328 return 0;
329 }
330
331 tv_input_device_t* device = NULL;
332 err = module->common.methods->open(
333 (hw_module_t*)module,
334 TV_INPUT_DEFAULT_DEVICE,
335 (hw_device_t**)&device);
336 if (err) {
337 ALOGE("Couldn't open %s device (%s)",
338 TV_INPUT_DEFAULT_DEVICE, strerror(-err));
339 return 0;
340 }
341
Wonsik Kima6170742014-12-17 11:18:36 +0900342 return new JTvInputHal(env, thiz, device, looper);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000343}
344
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900345int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900346 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
347 if (connections.indexOfKey(streamId) < 0) {
348 connections.add(streamId, Connection());
349 }
350 Connection& connection = connections.editValueFor(streamId);
351 if (connection.mSurface == surface) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000352 // Nothing to do
353 return NO_ERROR;
354 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900355 // Clear the surface in the connection.
356 if (connection.mSurface != NULL) {
357 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
358 if (Surface::isValid(connection.mSurface)) {
359 connection.mSurface->setSidebandStream(NULL);
360 }
361 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000362 connection.mSurface.clear();
363 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900364 if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000365 // Need to configure stream
366 int numConfigs = 0;
Wonsik Kim102d0b72015-11-26 18:51:09 +0900367 const tv_stream_config_ext_t* configs = getStreamConfigs(deviceId, &numConfigs);
368 if (configs == NULL) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000369 ALOGE("Couldn't get stream configs");
370 return UNKNOWN_ERROR;
371 }
372 int configIndex = -1;
373 for (int i = 0; i < numConfigs; ++i) {
Wonsik Kim102d0b72015-11-26 18:51:09 +0900374 if (configs[i].config.stream_id == streamId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000375 configIndex = i;
376 break;
377 }
378 }
379 if (configIndex == -1) {
380 ALOGE("Cannot find a config with given stream ID: %d", streamId);
381 return BAD_VALUE;
382 }
Wonsik Kim102d0b72015-11-26 18:51:09 +0900383 connection.mStreamType = configs[configIndex].config.type;
Wonsik Kim21aa3462014-07-29 16:39:00 +0900384
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000385 tv_stream_t stream;
Wonsik Kim102d0b72015-11-26 18:51:09 +0900386 stream.stream_id = configs[configIndex].config.stream_id;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900387 if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
Wonsik Kim102d0b72015-11-26 18:51:09 +0900388 stream.buffer_producer.width = configs[configIndex].config.max_video_width;
389 stream.buffer_producer.height = configs[configIndex].config.max_video_height;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900390 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000391 if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
392 ALOGE("Couldn't add stream");
393 return UNKNOWN_ERROR;
394 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900395 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
396 connection.mSourceHandle = NativeHandle::create(
397 stream.sideband_stream_source_handle, false);
398 } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
399 if (connection.mThread != NULL) {
400 connection.mThread->shutdown();
401 }
402 connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
403 connection.mThread->run();
404 }
405 }
406 connection.mSurface = surface;
407 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000408 connection.mSurface->setSidebandStream(connection.mSourceHandle);
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900409 } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
410 connection.mThread->setSurface(surface);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000411 }
412 return NO_ERROR;
413}
414
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900415int JTvInputHal::removeStream(int deviceId, int streamId) {
416 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
417 if (connections.indexOfKey(streamId) < 0) {
418 return BAD_VALUE;
419 }
420 Connection& connection = connections.editValueFor(streamId);
421 if (connection.mSurface == NULL) {
422 // Nothing to do
423 return NO_ERROR;
424 }
425 if (Surface::isValid(connection.mSurface)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900426 connection.mSurface->setSidebandStream(NULL);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900427 }
Wonsik Kim6b1e6952015-10-30 10:36:56 +0900428 connection.mSurface.clear();
Wonsik Kimb82de362014-08-05 15:35:07 +0900429 if (connection.mThread != NULL) {
430 connection.mThread->shutdown();
431 connection.mThread.clear();
432 }
433 if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
434 ALOGE("Couldn't remove stream");
435 return BAD_VALUE;
436 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900437 if (connection.mSourceHandle != NULL) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900438 connection.mSourceHandle.clear();
439 }
440 return NO_ERROR;
441}
442
Wonsik Kim102d0b72015-11-26 18:51:09 +0900443const tv_stream_config_ext_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
444 const tv_stream_config_ext_t* configs = NULL;
445 if (mDevice->common.version >= TV_INPUT_DEVICE_API_VERSION_0_2) {
446 if (mDevice->get_stream_configurations_ext(
447 mDevice, deviceId, numConfigs, &configs) != 0) {
448 ALOGE("Couldn't get stream configs");
449 return NULL;
450 }
451 } else {
452 const tv_stream_config_t* oldConfigs;
453 if (mDevice->get_stream_configurations(
454 mDevice, deviceId, numConfigs, &oldConfigs) != 0) {
455 ALOGE("Couldn't get stream configs");
456 return NULL;
457 }
458 if (mConfigBufferSize < *numConfigs) {
459 mConfigBufferSize = (*numConfigs / 16 + 1) * 16;
460 if (mConfigBuffer != NULL) {
461 delete[] mConfigBuffer;
462 }
463 mConfigBuffer = new tv_stream_config_ext_t[mConfigBufferSize];
464 }
465 for (int i = 0; i < *numConfigs; ++i) {
466 mConfigBuffer[i].config = oldConfigs[i];
467 mConfigBuffer[i].flags = 0;
468 }
469 configs = mConfigBuffer;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000470 }
471 return configs;
472}
473
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000474// static
475void JTvInputHal::notify(
476 tv_input_device_t* dev, tv_input_event_t* event, void* data) {
477 JTvInputHal* thiz = (JTvInputHal*)data;
Wonsik Kima6170742014-12-17 11:18:36 +0900478 thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000479}
480
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900481// static
482void JTvInputHal::cloneTvInputEvent(
483 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) {
484 memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t));
485 if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
486 srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
487 srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
488 srcEvent->device_info.audio_address != NULL){
489 char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1];
490 strcpy(audio_address, srcEvent->device_info.audio_address);
491 dstEvent->device_info.audio_address = audio_address;
492 }
493}
494
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000495void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900496 {
497 Mutex::Autolock autoLock(&mLock);
498 mConnections.add(info.device_id, KeyedVector<int, Connection>());
499 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000500 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900501
502 jobject builder = env->NewObject(
503 gTvInputHardwareInfoBuilderClassInfo.clazz,
504 gTvInputHardwareInfoBuilderClassInfo.constructor);
505 env->CallObjectMethod(
506 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
507 env->CallObjectMethod(
508 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
Wonsik Kima358b532014-06-12 14:56:18 +0900509 if (info.type == TV_INPUT_TYPE_HDMI) {
510 env->CallObjectMethod(
511 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
512 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900513 env->CallObjectMethod(
514 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
515 if (info.audio_type != AUDIO_DEVICE_NONE) {
516 jstring audioAddress = env->NewStringUTF(info.audio_address);
517 env->CallObjectMethod(
518 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
519 env->DeleteLocalRef(audioAddress);
520 }
521
522 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
523
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000524 env->CallVoidMethod(
525 mThiz,
526 gTvInputHalClassInfo.deviceAvailable,
Wonsik Kimd7c29182014-05-27 10:38:21 +0900527 infoObject);
528
529 env->DeleteLocalRef(builder);
530 env->DeleteLocalRef(infoObject);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000531}
532
533void JTvInputHal::onDeviceUnavailable(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900534 {
535 Mutex::Autolock autoLock(&mLock);
536 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
537 for (size_t i = 0; i < connections.size(); ++i) {
538 removeStream(deviceId, connections.keyAt(i));
539 }
540 connections.clear();
541 mConnections.removeItem(deviceId);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900542 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900543 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000544 env->CallVoidMethod(
545 mThiz,
546 gTvInputHalClassInfo.deviceUnavailable,
547 deviceId);
548}
549
550void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900551 {
552 Mutex::Autolock autoLock(&mLock);
553 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
554 for (size_t i = 0; i < connections.size(); ++i) {
555 removeStream(deviceId, connections.keyAt(i));
556 }
557 connections.clear();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900558 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900559 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000560 env->CallVoidMethod(
561 mThiz,
562 gTvInputHalClassInfo.streamConfigsChanged,
563 deviceId);
564}
565
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900566void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
567 sp<BufferProducerThread> thread;
568 {
569 Mutex::Autolock autoLock(&mLock);
570 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
571 Connection& connection = connections.editValueFor(streamId);
572 if (connection.mThread == NULL) {
573 ALOGE("capture thread not existing.");
574 return;
575 }
576 thread = connection.mThread;
577 }
578 thread->onCaptured(seq, succeeded);
Terry Heoc086a3d2014-06-18 14:26:44 +0900579 if (seq == 0) {
580 JNIEnv* env = AndroidRuntime::getJNIEnv();
581 env->CallVoidMethod(
582 mThiz,
583 gTvInputHalClassInfo.firstFrameCaptured,
584 deviceId,
585 streamId);
586 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900587}
588
Wonsik Kima6170742014-12-17 11:18:36 +0900589JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
590 mHal = hal;
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900591 cloneTvInputEvent(&mEvent, event);
592}
593
594JTvInputHal::NotifyHandler::~NotifyHandler() {
595 if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
596 mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
597 mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
598 mEvent.device_info.audio_address != NULL) {
599 delete mEvent.device_info.audio_address;
600 }
Wonsik Kima6170742014-12-17 11:18:36 +0900601}
602
603void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
604 switch (mEvent.type) {
605 case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
606 mHal->onDeviceAvailable(mEvent.device_info);
607 } break;
608 case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
609 mHal->onDeviceUnavailable(mEvent.device_info.device_id);
610 } break;
611 case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
612 mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
613 } break;
614 case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
615 mHal->onCaptured(mEvent.capture_result.device_id,
616 mEvent.capture_result.stream_id,
617 mEvent.capture_result.seq,
618 true /* succeeded */);
619 } break;
620 case TV_INPUT_EVENT_CAPTURE_FAILED: {
621 mHal->onCaptured(mEvent.capture_result.device_id,
622 mEvent.capture_result.stream_id,
623 mEvent.capture_result.seq,
624 false /* succeeded */);
625 } break;
626 default:
627 ALOGE("Unrecognizable event");
628 }
629}
630
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000631////////////////////////////////////////////////////////////////////////////////
632
Wonsik Kima6170742014-12-17 11:18:36 +0900633static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
634 sp<MessageQueue> messageQueue =
635 android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
636 return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000637}
638
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900639static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000640 jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
641 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900642 if (!jsurface) {
643 return BAD_VALUE;
644 }
645 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
Wonsik Kim6b1e6952015-10-30 10:36:56 +0900646 if (!Surface::isValid(surface)) {
647 return BAD_VALUE;
648 }
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900649 return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900650}
651
652static int nativeRemoveStream(JNIEnv* env, jclass clazz,
653 jlong ptr, jint deviceId, jint streamId) {
654 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
655 return tvInputHal->removeStream(deviceId, streamId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000656}
657
658static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
659 jlong ptr, jint deviceId, jint generation) {
660 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
661 int numConfigs = 0;
Wonsik Kim102d0b72015-11-26 18:51:09 +0900662 const tv_stream_config_ext_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000663
664 jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
665 for (int i = 0; i < numConfigs; ++i) {
666 jobject builder = env->NewObject(
667 gTvStreamConfigBuilderClassInfo.clazz,
668 gTvStreamConfigBuilderClassInfo.constructor);
669 env->CallObjectMethod(
Wonsik Kim102d0b72015-11-26 18:51:09 +0900670 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].config.stream_id);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000671 env->CallObjectMethod(
Wonsik Kim102d0b72015-11-26 18:51:09 +0900672 builder, gTvStreamConfigBuilderClassInfo.type, configs[i].config.type);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000673 env->CallObjectMethod(
Wonsik Kim102d0b72015-11-26 18:51:09 +0900674 builder, gTvStreamConfigBuilderClassInfo.maxWidth,
675 configs[i].config.max_video_width);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000676 env->CallObjectMethod(
Wonsik Kim102d0b72015-11-26 18:51:09 +0900677 builder, gTvStreamConfigBuilderClassInfo.maxHeight,
678 configs[i].config.max_video_height);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000679 env->CallObjectMethod(
680 builder, gTvStreamConfigBuilderClassInfo.generation, generation);
Wonsik Kim102d0b72015-11-26 18:51:09 +0900681 env->CallObjectMethod(
682 builder, gTvStreamConfigBuilderClassInfo.flags,
683 configs[i].flags);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000684
685 jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
686
687 env->SetObjectArrayElement(result, i, config);
688
689 env->DeleteLocalRef(config);
690 env->DeleteLocalRef(builder);
691 }
692 return result;
693}
694
695static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
696 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
697 delete tvInputHal;
698}
699
Daniel Micay76f6a862015-09-19 17:31:01 -0400700static const JNINativeMethod gTvInputHalMethods[] = {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000701 /* name, signature, funcPtr */
Wonsik Kima6170742014-12-17 11:18:36 +0900702 { "nativeOpen", "(Landroid/os/MessageQueue;)J",
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000703 (void*) nativeOpen },
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900704 { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
705 (void*) nativeAddOrUpdateStream },
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900706 { "nativeRemoveStream", "(JII)I",
707 (void*) nativeRemoveStream },
Jae Seod5cc4a22014-05-30 16:57:43 -0700708 { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000709 (void*) nativeGetStreamConfigs },
710 { "nativeClose", "(J)V",
711 (void*) nativeClose },
712};
713
714#define FIND_CLASS(var, className) \
715 var = env->FindClass(className); \
716 LOG_FATAL_IF(! var, "Unable to find class " className)
717
718#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
719 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
720 LOG_FATAL_IF(! var, "Unable to find method" methodName)
721
722int register_android_server_tv_TvInputHal(JNIEnv* env) {
723 int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
724 gTvInputHalMethods, NELEM(gTvInputHalMethods));
725 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
Bernhard Rosenkränzer4048a4b2014-11-23 22:24:32 +0100726 (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000727
728 jclass clazz;
729 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
730
731 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900732 gTvInputHalClassInfo.deviceAvailable, clazz,
733 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000734 GET_METHOD_ID(
735 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
736 GET_METHOD_ID(
737 gTvInputHalClassInfo.streamConfigsChanged, clazz,
738 "streamConfigsChangedFromNative", "(I)V");
Terry Heoc086a3d2014-06-18 14:26:44 +0900739 GET_METHOD_ID(
740 gTvInputHalClassInfo.firstFrameCaptured, clazz,
741 "firstFrameCapturedFromNative", "(II)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000742
Jae Seod5cc4a22014-05-30 16:57:43 -0700743 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000744 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
745
Jae Seod5cc4a22014-05-30 16:57:43 -0700746 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000747 gTvStreamConfigBuilderClassInfo.clazz =
748 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
749
750 GET_METHOD_ID(
751 gTvStreamConfigBuilderClassInfo.constructor,
752 gTvStreamConfigBuilderClassInfo.clazz,
753 "<init>", "()V");
754 GET_METHOD_ID(
755 gTvStreamConfigBuilderClassInfo.streamId,
756 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700757 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000758 GET_METHOD_ID(
759 gTvStreamConfigBuilderClassInfo.type,
760 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700761 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000762 GET_METHOD_ID(
763 gTvStreamConfigBuilderClassInfo.maxWidth,
764 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700765 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000766 GET_METHOD_ID(
767 gTvStreamConfigBuilderClassInfo.maxHeight,
768 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700769 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000770 GET_METHOD_ID(
771 gTvStreamConfigBuilderClassInfo.generation,
772 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700773 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000774 GET_METHOD_ID(
Wonsik Kim102d0b72015-11-26 18:51:09 +0900775 gTvStreamConfigBuilderClassInfo.flags,
776 gTvStreamConfigBuilderClassInfo.clazz,
777 "flags", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
778 GET_METHOD_ID(
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000779 gTvStreamConfigBuilderClassInfo.build,
780 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700781 "build", "()Landroid/media/tv/TvStreamConfig;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000782
Wonsik Kimd7c29182014-05-27 10:38:21 +0900783 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
784 "android/media/tv/TvInputHardwareInfo$Builder");
785 gTvInputHardwareInfoBuilderClassInfo.clazz =
786 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
787
788 GET_METHOD_ID(
789 gTvInputHardwareInfoBuilderClassInfo.constructor,
790 gTvInputHardwareInfoBuilderClassInfo.clazz,
791 "<init>", "()V");
792 GET_METHOD_ID(
793 gTvInputHardwareInfoBuilderClassInfo.deviceId,
794 gTvInputHardwareInfoBuilderClassInfo.clazz,
795 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
796 GET_METHOD_ID(
797 gTvInputHardwareInfoBuilderClassInfo.type,
798 gTvInputHardwareInfoBuilderClassInfo.clazz,
799 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
800 GET_METHOD_ID(
Wonsik Kima358b532014-06-12 14:56:18 +0900801 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
802 gTvInputHardwareInfoBuilderClassInfo.clazz,
803 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
804 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900805 gTvInputHardwareInfoBuilderClassInfo.audioType,
806 gTvInputHardwareInfoBuilderClassInfo.clazz,
807 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
808 GET_METHOD_ID(
809 gTvInputHardwareInfoBuilderClassInfo.audioAddress,
810 gTvInputHardwareInfoBuilderClassInfo.clazz,
811 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
812 GET_METHOD_ID(
813 gTvInputHardwareInfoBuilderClassInfo.build,
814 gTvInputHardwareInfoBuilderClassInfo.clazz,
815 "build", "()Landroid/media/tv/TvInputHardwareInfo;");
816
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000817 return 0;
818}
819
820} /* namespace android */