blob: dcb5199b9e5073c5200ad0a6dce61c46cac43b67 [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);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000242 const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
243
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,
294 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;
357 const tv_stream_config_t* configs = NULL;
358 if (mDevice->get_stream_configurations(
359 mDevice, deviceId, &numConfigs, &configs) != 0) {
360 ALOGE("Couldn't get stream configs");
361 return UNKNOWN_ERROR;
362 }
363 int configIndex = -1;
364 for (int i = 0; i < numConfigs; ++i) {
365 if (configs[i].stream_id == streamId) {
366 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 }
Wonsik Kim21aa3462014-07-29 16:39:00 +0900374 connection.mStreamType = configs[configIndex].type;
375
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000376 tv_stream_t stream;
377 stream.stream_id = configs[configIndex].stream_id;
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900378 if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
379 stream.buffer_producer.width = configs[configIndex].max_video_width;
380 stream.buffer_producer.height = configs[configIndex].max_video_height;
381 }
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);
394 connection.mThread->run();
395 }
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)) {
417 connection.mSurface.clear();
418 }
419 if (connection.mSurface != NULL) {
420 connection.mSurface->setSidebandStream(NULL);
421 connection.mSurface.clear();
422 }
Wonsik Kimb82de362014-08-05 15:35:07 +0900423 if (connection.mThread != NULL) {
424 connection.mThread->shutdown();
425 connection.mThread.clear();
426 }
427 if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
428 ALOGE("Couldn't remove stream");
429 return BAD_VALUE;
430 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900431 if (connection.mSourceHandle != NULL) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900432 connection.mSourceHandle.clear();
433 }
434 return NO_ERROR;
435}
436
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000437const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
438 const tv_stream_config_t* configs = NULL;
439 if (mDevice->get_stream_configurations(
440 mDevice, deviceId, numConfigs, &configs) != 0) {
441 ALOGE("Couldn't get stream configs");
442 return NULL;
443 }
444 return configs;
445}
446
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000447// static
448void JTvInputHal::notify(
449 tv_input_device_t* dev, tv_input_event_t* event, void* data) {
450 JTvInputHal* thiz = (JTvInputHal*)data;
Wonsik Kima6170742014-12-17 11:18:36 +0900451 thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000452}
453
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900454// static
455void JTvInputHal::cloneTvInputEvent(
456 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) {
457 memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t));
458 if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
459 srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
460 srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
461 srcEvent->device_info.audio_address != NULL){
462 char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1];
463 strcpy(audio_address, srcEvent->device_info.audio_address);
464 dstEvent->device_info.audio_address = audio_address;
465 }
466}
467
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000468void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900469 {
470 Mutex::Autolock autoLock(&mLock);
471 mConnections.add(info.device_id, KeyedVector<int, Connection>());
472 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000473 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900474
475 jobject builder = env->NewObject(
476 gTvInputHardwareInfoBuilderClassInfo.clazz,
477 gTvInputHardwareInfoBuilderClassInfo.constructor);
478 env->CallObjectMethod(
479 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
480 env->CallObjectMethod(
481 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
Wonsik Kima358b532014-06-12 14:56:18 +0900482 if (info.type == TV_INPUT_TYPE_HDMI) {
483 env->CallObjectMethod(
484 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
485 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900486 env->CallObjectMethod(
487 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
488 if (info.audio_type != AUDIO_DEVICE_NONE) {
489 jstring audioAddress = env->NewStringUTF(info.audio_address);
490 env->CallObjectMethod(
491 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
492 env->DeleteLocalRef(audioAddress);
493 }
494
495 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
496
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000497 env->CallVoidMethod(
498 mThiz,
499 gTvInputHalClassInfo.deviceAvailable,
Wonsik Kimd7c29182014-05-27 10:38:21 +0900500 infoObject);
501
502 env->DeleteLocalRef(builder);
503 env->DeleteLocalRef(infoObject);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000504}
505
506void JTvInputHal::onDeviceUnavailable(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900507 {
508 Mutex::Autolock autoLock(&mLock);
509 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
510 for (size_t i = 0; i < connections.size(); ++i) {
511 removeStream(deviceId, connections.keyAt(i));
512 }
513 connections.clear();
514 mConnections.removeItem(deviceId);
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900515 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900516 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000517 env->CallVoidMethod(
518 mThiz,
519 gTvInputHalClassInfo.deviceUnavailable,
520 deviceId);
521}
522
523void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900524 {
525 Mutex::Autolock autoLock(&mLock);
526 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
527 for (size_t i = 0; i < connections.size(); ++i) {
528 removeStream(deviceId, connections.keyAt(i));
529 }
530 connections.clear();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900531 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900532 JNIEnv* env = AndroidRuntime::getJNIEnv();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000533 env->CallVoidMethod(
534 mThiz,
535 gTvInputHalClassInfo.streamConfigsChanged,
536 deviceId);
537}
538
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900539void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
540 sp<BufferProducerThread> thread;
541 {
542 Mutex::Autolock autoLock(&mLock);
543 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
544 Connection& connection = connections.editValueFor(streamId);
545 if (connection.mThread == NULL) {
546 ALOGE("capture thread not existing.");
547 return;
548 }
549 thread = connection.mThread;
550 }
551 thread->onCaptured(seq, succeeded);
Terry Heoc086a3d2014-06-18 14:26:44 +0900552 if (seq == 0) {
553 JNIEnv* env = AndroidRuntime::getJNIEnv();
554 env->CallVoidMethod(
555 mThiz,
556 gTvInputHalClassInfo.firstFrameCaptured,
557 deviceId,
558 streamId);
559 }
Wonsik Kim0b2691b2014-05-23 16:07:55 +0900560}
561
Wonsik Kima6170742014-12-17 11:18:36 +0900562JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
563 mHal = hal;
Sungsoo Lim62f38d12014-12-23 13:56:11 +0900564 cloneTvInputEvent(&mEvent, event);
565}
566
567JTvInputHal::NotifyHandler::~NotifyHandler() {
568 if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE ||
569 mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE ||
570 mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) &&
571 mEvent.device_info.audio_address != NULL) {
572 delete mEvent.device_info.audio_address;
573 }
Wonsik Kima6170742014-12-17 11:18:36 +0900574}
575
576void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
577 switch (mEvent.type) {
578 case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
579 mHal->onDeviceAvailable(mEvent.device_info);
580 } break;
581 case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
582 mHal->onDeviceUnavailable(mEvent.device_info.device_id);
583 } break;
584 case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
585 mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
586 } break;
587 case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
588 mHal->onCaptured(mEvent.capture_result.device_id,
589 mEvent.capture_result.stream_id,
590 mEvent.capture_result.seq,
591 true /* succeeded */);
592 } break;
593 case TV_INPUT_EVENT_CAPTURE_FAILED: {
594 mHal->onCaptured(mEvent.capture_result.device_id,
595 mEvent.capture_result.stream_id,
596 mEvent.capture_result.seq,
597 false /* succeeded */);
598 } break;
599 default:
600 ALOGE("Unrecognizable event");
601 }
602}
603
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000604////////////////////////////////////////////////////////////////////////////////
605
Wonsik Kima6170742014-12-17 11:18:36 +0900606static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
607 sp<MessageQueue> messageQueue =
608 android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
609 return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000610}
611
Wonsik Kim8f24a8b2014-10-22 16:27:39 +0900612static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000613 jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
614 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900615 if (!jsurface) {
616 return BAD_VALUE;
617 }
618 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
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;
632 const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
633
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(
640 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
641 env->CallObjectMethod(
642 builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
643 env->CallObjectMethod(
644 builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
645 env->CallObjectMethod(
646 builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
647 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
665static JNINativeMethod gTvInputHalMethods[] = {
666 /* 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); \
681 LOG_FATAL_IF(! var, "Unable to find class " className)
682
683#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
684 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
685 LOG_FATAL_IF(! var, "Unable to find method" methodName)
686
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.");
691
692 jclass clazz;
693 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
694
695 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900696 gTvInputHalClassInfo.deviceAvailable, clazz,
697 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000698 GET_METHOD_ID(
699 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
700 GET_METHOD_ID(
701 gTvInputHalClassInfo.streamConfigsChanged, clazz,
702 "streamConfigsChangedFromNative", "(I)V");
Terry Heoc086a3d2014-06-18 14:26:44 +0900703 GET_METHOD_ID(
704 gTvInputHalClassInfo.firstFrameCaptured, clazz,
705 "firstFrameCapturedFromNative", "(II)V");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000706
Jae Seod5cc4a22014-05-30 16:57:43 -0700707 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000708 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
709
Jae Seod5cc4a22014-05-30 16:57:43 -0700710 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000711 gTvStreamConfigBuilderClassInfo.clazz =
712 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
713
714 GET_METHOD_ID(
715 gTvStreamConfigBuilderClassInfo.constructor,
716 gTvStreamConfigBuilderClassInfo.clazz,
717 "<init>", "()V");
718 GET_METHOD_ID(
719 gTvStreamConfigBuilderClassInfo.streamId,
720 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700721 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000722 GET_METHOD_ID(
723 gTvStreamConfigBuilderClassInfo.type,
724 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700725 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000726 GET_METHOD_ID(
727 gTvStreamConfigBuilderClassInfo.maxWidth,
728 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700729 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000730 GET_METHOD_ID(
731 gTvStreamConfigBuilderClassInfo.maxHeight,
732 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700733 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000734 GET_METHOD_ID(
735 gTvStreamConfigBuilderClassInfo.generation,
736 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700737 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000738 GET_METHOD_ID(
739 gTvStreamConfigBuilderClassInfo.build,
740 gTvStreamConfigBuilderClassInfo.clazz,
Jae Seod5cc4a22014-05-30 16:57:43 -0700741 "build", "()Landroid/media/tv/TvStreamConfig;");
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000742
Wonsik Kimd7c29182014-05-27 10:38:21 +0900743 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
744 "android/media/tv/TvInputHardwareInfo$Builder");
745 gTvInputHardwareInfoBuilderClassInfo.clazz =
746 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
747
748 GET_METHOD_ID(
749 gTvInputHardwareInfoBuilderClassInfo.constructor,
750 gTvInputHardwareInfoBuilderClassInfo.clazz,
751 "<init>", "()V");
752 GET_METHOD_ID(
753 gTvInputHardwareInfoBuilderClassInfo.deviceId,
754 gTvInputHardwareInfoBuilderClassInfo.clazz,
755 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
756 GET_METHOD_ID(
757 gTvInputHardwareInfoBuilderClassInfo.type,
758 gTvInputHardwareInfoBuilderClassInfo.clazz,
759 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
760 GET_METHOD_ID(
Wonsik Kima358b532014-06-12 14:56:18 +0900761 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
762 gTvInputHardwareInfoBuilderClassInfo.clazz,
763 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
764 GET_METHOD_ID(
Wonsik Kimd7c29182014-05-27 10:38:21 +0900765 gTvInputHardwareInfoBuilderClassInfo.audioType,
766 gTvInputHardwareInfoBuilderClassInfo.clazz,
767 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
768 GET_METHOD_ID(
769 gTvInputHardwareInfoBuilderClassInfo.audioAddress,
770 gTvInputHardwareInfoBuilderClassInfo.clazz,
771 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
772 GET_METHOD_ID(
773 gTvInputHardwareInfoBuilderClassInfo.build,
774 gTvInputHardwareInfoBuilderClassInfo.clazz,
775 "build", "()Landroid/media/tv/TvInputHardwareInfo;");
776
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000777 return 0;
778}
779
780} /* namespace android */