1 /*
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
21 #include "android_os_MessageQueue.h"
22 #include "android_runtime/AndroidRuntime.h"
23 #include "android_runtime/android_view_Surface.h"
24 #include <nativehelper/JNIHelp.h>
25 #include "jni.h"
26
27 #include <android/hardware/tv/input/1.0/ITvInputCallback.h>
28 #include <android/hardware/tv/input/1.0/ITvInput.h>
29 #include <android/hardware/tv/input/1.0/types.h>
30 #include <gui/Surface.h>
31 #include <utils/Errors.h>
32 #include <utils/KeyedVector.h>
33 #include <utils/Log.h>
34 #include <utils/Looper.h>
35 #include <utils/NativeHandle.h>
36 #include <hardware/tv_input.h>
37
38 using ::android::hardware::audio::common::V2_0::AudioDevice;
39 using ::android::hardware::tv::input::V1_0::ITvInput;
40 using ::android::hardware::tv::input::V1_0::ITvInputCallback;
41 using ::android::hardware::tv::input::V1_0::Result;
42 using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
43 using ::android::hardware::tv::input::V1_0::TvInputEvent;
44 using ::android::hardware::tv::input::V1_0::TvInputEventType;
45 using ::android::hardware::tv::input::V1_0::TvInputType;
46 using ::android::hardware::tv::input::V1_0::TvStreamConfig;
47 using ::android::hardware::Return;
48 using ::android::hardware::Void;
49 using ::android::hardware::hidl_vec;
50 using ::android::hardware::hidl_string;
51
52 namespace android {
53
54 static struct {
55 jmethodID deviceAvailable;
56 jmethodID deviceUnavailable;
57 jmethodID streamConfigsChanged;
58 jmethodID firstFrameCaptured;
59 } gTvInputHalClassInfo;
60
61 static struct {
62 jclass clazz;
63 } gTvStreamConfigClassInfo;
64
65 static struct {
66 jclass clazz;
67
68 jmethodID constructor;
69 jmethodID streamId;
70 jmethodID type;
71 jmethodID maxWidth;
72 jmethodID maxHeight;
73 jmethodID generation;
74 jmethodID build;
75 } gTvStreamConfigBuilderClassInfo;
76
77 static struct {
78 jclass clazz;
79
80 jmethodID constructor;
81 jmethodID deviceId;
82 jmethodID type;
83 jmethodID hdmiPortId;
84 jmethodID cableConnectionStatus;
85 jmethodID audioType;
86 jmethodID audioAddress;
87 jmethodID build;
88 } gTvInputHardwareInfoBuilderClassInfo;
89
90 ////////////////////////////////////////////////////////////////////////////////
91
92 class BufferProducerThread : public Thread {
93 public:
94 BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
95
96 virtual status_t readyToRun();
97
98 void setSurface(const sp<Surface>& surface);
99 void onCaptured(uint32_t seq, bool succeeded);
100 void shutdown();
101
102 private:
103 Mutex mLock;
104 Condition mCondition;
105 sp<Surface> mSurface;
106 tv_input_device_t* mDevice;
107 int mDeviceId;
108 tv_stream_t mStream;
109 sp<ANativeWindowBuffer_t> mBuffer;
110 enum {
111 CAPTURING,
112 CAPTURED,
113 RELEASED,
114 } mBufferState;
115 uint32_t mSeq;
116 bool mShutdown;
117
118 virtual bool threadLoop();
119
120 void setSurfaceLocked(const sp<Surface>& surface);
121 };
122
BufferProducerThread(tv_input_device_t * device,int deviceId,const tv_stream_t * stream)123 BufferProducerThread::BufferProducerThread(
124 tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
125 : Thread(false),
126 mDevice(device),
127 mDeviceId(deviceId),
128 mBuffer(NULL),
129 mBufferState(RELEASED),
130 mSeq(0u),
131 mShutdown(false) {
132 memcpy(&mStream, stream, sizeof(mStream));
133 }
134
readyToRun()135 status_t BufferProducerThread::readyToRun() {
136 sp<ANativeWindow> anw(mSurface);
137 status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
138 if (err != NO_ERROR) {
139 return err;
140 }
141 err = native_window_set_buffers_dimensions(
142 anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
143 if (err != NO_ERROR) {
144 return err;
145 }
146 err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
147 if (err != NO_ERROR) {
148 return err;
149 }
150 return NO_ERROR;
151 }
152
setSurface(const sp<Surface> & surface)153 void BufferProducerThread::setSurface(const sp<Surface>& surface) {
154 Mutex::Autolock autoLock(&mLock);
155 setSurfaceLocked(surface);
156 }
157
setSurfaceLocked(const sp<Surface> & surface)158 void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
159 if (surface == mSurface) {
160 return;
161 }
162
163 if (mBufferState == CAPTURING) {
164 mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
165 }
166 while (mBufferState == CAPTURING) {
167 status_t err = mCondition.waitRelative(mLock, s2ns(1));
168 if (err != NO_ERROR) {
169 ALOGE("error %d while wating for buffer state to change.", err);
170 break;
171 }
172 }
173 mBuffer.clear();
174 mBufferState = RELEASED;
175
176 mSurface = surface;
177 mCondition.broadcast();
178 }
179
onCaptured(uint32_t seq,bool succeeded)180 void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
181 Mutex::Autolock autoLock(&mLock);
182 if (seq != mSeq) {
183 ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
184 }
185 if (mBufferState != CAPTURING) {
186 ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
187 }
188 if (succeeded) {
189 mBufferState = CAPTURED;
190 } else {
191 mBuffer.clear();
192 mBufferState = RELEASED;
193 }
194 mCondition.broadcast();
195 }
196
shutdown()197 void BufferProducerThread::shutdown() {
198 Mutex::Autolock autoLock(&mLock);
199 mShutdown = true;
200 setSurfaceLocked(NULL);
201 requestExitAndWait();
202 }
203
threadLoop()204 bool BufferProducerThread::threadLoop() {
205 Mutex::Autolock autoLock(&mLock);
206
207 status_t err = NO_ERROR;
208 if (mSurface == NULL) {
209 err = mCondition.waitRelative(mLock, s2ns(1));
210 // It's OK to time out here.
211 if (err != NO_ERROR && err != TIMED_OUT) {
212 ALOGE("error %d while wating for non-null surface to be set", err);
213 return false;
214 }
215 return true;
216 }
217 sp<ANativeWindow> anw(mSurface);
218 while (mBufferState == CAPTURING) {
219 err = mCondition.waitRelative(mLock, s2ns(1));
220 if (err != NO_ERROR) {
221 ALOGE("error %d while wating for buffer state to change.", err);
222 return false;
223 }
224 }
225 if (mBufferState == CAPTURED && anw != NULL) {
226 err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
227 if (err != NO_ERROR) {
228 ALOGE("error %d while queueing buffer to surface", err);
229 return false;
230 }
231 mBuffer.clear();
232 mBufferState = RELEASED;
233 }
234 if (mBuffer == NULL && !mShutdown && anw != NULL) {
235 ANativeWindowBuffer_t* buffer = NULL;
236 err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
237 if (err != NO_ERROR) {
238 ALOGE("error %d while dequeueing buffer to surface", err);
239 return false;
240 }
241 mBuffer = buffer;
242 mBufferState = CAPTURING;
243 mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
244 buffer->handle, ++mSeq);
245 }
246
247 return true;
248 }
249
250 ////////////////////////////////////////////////////////////////////////////////
251
252 class JTvInputHal {
253 public:
254 ~JTvInputHal();
255
256 static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
257
258 int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
259 int removeStream(int deviceId, int streamId);
260 const hidl_vec<TvStreamConfig> getStreamConfigs(int deviceId);
261
262 void onDeviceAvailable(const TvInputDeviceInfo& info);
263 void onDeviceUnavailable(int deviceId);
264 void onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus);
265 void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
266
267 private:
268 // Connection between a surface and a stream.
269 class Connection {
270 public:
Connection()271 Connection() {}
272
273 sp<Surface> mSurface;
274 tv_stream_type_t mStreamType;
275
276 // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
277 sp<NativeHandle> mSourceHandle;
278 // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
279 sp<BufferProducerThread> mThread;
280 };
281
282 class NotifyHandler : public MessageHandler {
283 public:
284 NotifyHandler(JTvInputHal* hal, const TvInputEvent& event);
285
286 virtual void handleMessage(const Message& message);
287
288 private:
289 TvInputEvent mEvent;
290 JTvInputHal* mHal;
291 };
292
293 class TvInputCallback : public ITvInputCallback {
294 public:
295 explicit TvInputCallback(JTvInputHal* hal);
296 Return<void> notify(const TvInputEvent& event) override;
297 private:
298 JTvInputHal* mHal;
299 };
300
301 JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput, const sp<Looper>& looper);
302
303 Mutex mLock;
304 Mutex mStreamLock;
305 jweak mThiz;
306 sp<Looper> mLooper;
307
308 KeyedVector<int, KeyedVector<int, Connection> > mConnections;
309
310 sp<ITvInput> mTvInput;
311 sp<ITvInputCallback> mTvInputCallback;
312 };
313
JTvInputHal(JNIEnv * env,jobject thiz,sp<ITvInput> tvInput,const sp<Looper> & looper)314 JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput,
315 const sp<Looper>& looper) {
316 mThiz = env->NewWeakGlobalRef(thiz);
317 mTvInput = tvInput;
318 mLooper = looper;
319 mTvInputCallback = new TvInputCallback(this);
320 mTvInput->setCallback(mTvInputCallback);
321 }
322
~JTvInputHal()323 JTvInputHal::~JTvInputHal() {
324 mTvInput->setCallback(nullptr);
325 JNIEnv* env = AndroidRuntime::getJNIEnv();
326 env->DeleteWeakGlobalRef(mThiz);
327 mThiz = NULL;
328 }
329
createInstance(JNIEnv * env,jobject thiz,const sp<Looper> & looper)330 JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
331 // TODO(b/31632518)
332 sp<ITvInput> tvInput = ITvInput::getService();
333 if (tvInput == nullptr) {
334 ALOGE("Couldn't get tv.input service.");
335 return nullptr;
336 }
337
338 return new JTvInputHal(env, thiz, tvInput, looper);
339 }
340
addOrUpdateStream(int deviceId,int streamId,const sp<Surface> & surface)341 int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
342 Mutex::Autolock autoLock(&mStreamLock);
343 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
344 if (connections.indexOfKey(streamId) < 0) {
345 connections.add(streamId, Connection());
346 }
347 Connection& connection = connections.editValueFor(streamId);
348 if (connection.mSurface == surface) {
349 // Nothing to do
350 return NO_ERROR;
351 }
352 // Clear the surface in the connection.
353 if (connection.mSurface != NULL) {
354 if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
355 if (Surface::isValid(connection.mSurface)) {
356 connection.mSurface->setSidebandStream(NULL);
357 }
358 }
359 connection.mSurface.clear();
360 }
361 if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
362 // Need to configure stream
363 Result result = Result::UNKNOWN;
364 hidl_vec<TvStreamConfig> list;
365 mTvInput->getStreamConfigurations(deviceId,
366 [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
367 result = res;
368 if (res == Result::OK) {
369 list = configs;
370 }
371 });
372 if (result != Result::OK) {
373 ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
374 return UNKNOWN_ERROR;
375 }
376 int configIndex = -1;
377 for (size_t i = 0; i < list.size(); ++i) {
378 if (list[i].streamId == streamId) {
379 configIndex = i;
380 break;
381 }
382 }
383 if (configIndex == -1) {
384 ALOGE("Cannot find a config with given stream ID: %d", streamId);
385 return BAD_VALUE;
386 }
387 connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE;
388
389 result = Result::UNKNOWN;
390 const native_handle_t* sidebandStream;
391 mTvInput->openStream(deviceId, streamId,
392 [&result, &sidebandStream](Result res, const native_handle_t* handle) {
393 result = res;
394 if (res == Result::OK) {
395 if (handle) {
396 sidebandStream = native_handle_clone(handle);
397 } else {
398 result = Result::UNKNOWN;
399 }
400 }
401 });
402 if (result != Result::OK) {
403 ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId,
404 result);
405 return UNKNOWN_ERROR;
406 }
407 connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true);
408 }
409 connection.mSurface = surface;
410 if (connection.mSurface != nullptr) {
411 connection.mSurface->setSidebandStream(connection.mSourceHandle);
412 }
413 return NO_ERROR;
414 }
415
removeStream(int deviceId,int streamId)416 int JTvInputHal::removeStream(int deviceId, int streamId) {
417 Mutex::Autolock autoLock(&mStreamLock);
418 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
419 if (connections.indexOfKey(streamId) < 0) {
420 return BAD_VALUE;
421 }
422 Connection& connection = connections.editValueFor(streamId);
423 if (connection.mSurface == NULL) {
424 // Nothing to do
425 return NO_ERROR;
426 }
427 if (Surface::isValid(connection.mSurface)) {
428 connection.mSurface->setSidebandStream(NULL);
429 }
430 connection.mSurface.clear();
431 if (connection.mThread != NULL) {
432 connection.mThread->shutdown();
433 connection.mThread.clear();
434 }
435 if (mTvInput->closeStream(deviceId, streamId) != Result::OK) {
436 ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId);
437 return BAD_VALUE;
438 }
439 if (connection.mSourceHandle != NULL) {
440 connection.mSourceHandle.clear();
441 }
442 return NO_ERROR;
443 }
444
getStreamConfigs(int deviceId)445 const hidl_vec<TvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
446 Result result = Result::UNKNOWN;
447 hidl_vec<TvStreamConfig> list;
448 mTvInput->getStreamConfigurations(deviceId,
449 [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
450 result = res;
451 if (res == Result::OK) {
452 list = configs;
453 }
454 });
455 if (result != Result::OK) {
456 ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
457 }
458 return list;
459 }
460
onDeviceAvailable(const TvInputDeviceInfo & info)461 void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfo& info) {
462 {
463 Mutex::Autolock autoLock(&mLock);
464 mConnections.add(info.deviceId, KeyedVector<int, Connection>());
465 }
466 JNIEnv* env = AndroidRuntime::getJNIEnv();
467
468 jobject builder = env->NewObject(
469 gTvInputHardwareInfoBuilderClassInfo.clazz,
470 gTvInputHardwareInfoBuilderClassInfo.constructor);
471 env->CallObjectMethod(
472 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId);
473 env->CallObjectMethod(
474 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
475 if (info.type == TvInputType::HDMI) {
476 env->CallObjectMethod(
477 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId);
478 }
479 env->CallObjectMethod(
480 builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
481 info.cableConnectionStatus);
482 env->CallObjectMethod(
483 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType);
484 if (info.audioType != AudioDevice::NONE) {
485 uint8_t buffer[info.audioAddress.size() + 1];
486 memcpy(buffer, info.audioAddress.data(), info.audioAddress.size());
487 buffer[info.audioAddress.size()] = '\0';
488 jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char *>(buffer));
489 env->CallObjectMethod(
490 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
491 env->DeleteLocalRef(audioAddress);
492 }
493
494 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
495
496 env->CallVoidMethod(
497 mThiz,
498 gTvInputHalClassInfo.deviceAvailable,
499 infoObject);
500
501 env->DeleteLocalRef(builder);
502 env->DeleteLocalRef(infoObject);
503 }
504
onDeviceUnavailable(int deviceId)505 void JTvInputHal::onDeviceUnavailable(int deviceId) {
506 {
507 Mutex::Autolock autoLock(&mLock);
508 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
509 for (size_t i = 0; i < connections.size(); ++i) {
510 removeStream(deviceId, connections.keyAt(i));
511 }
512 connections.clear();
513 mConnections.removeItem(deviceId);
514 }
515 JNIEnv* env = AndroidRuntime::getJNIEnv();
516 env->CallVoidMethod(
517 mThiz,
518 gTvInputHalClassInfo.deviceUnavailable,
519 deviceId);
520 }
521
onStreamConfigurationsChanged(int deviceId,int cableConnectionStatus)522 void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
523 {
524 Mutex::Autolock autoLock(&mLock);
525 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
526 for (size_t i = 0; i < connections.size(); ++i) {
527 removeStream(deviceId, connections.keyAt(i));
528 }
529 connections.clear();
530 }
531 JNIEnv* env = AndroidRuntime::getJNIEnv();
532 env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId,
533 cableConnectionStatus);
534 }
535
onCaptured(int deviceId,int streamId,uint32_t seq,bool succeeded)536 void 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);
549 if (seq == 0) {
550 JNIEnv* env = AndroidRuntime::getJNIEnv();
551 env->CallVoidMethod(
552 mThiz,
553 gTvInputHalClassInfo.firstFrameCaptured,
554 deviceId,
555 streamId);
556 }
557 }
558
NotifyHandler(JTvInputHal * hal,const TvInputEvent & event)559 JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) {
560 mHal = hal;
561 mEvent = event;
562 }
563
handleMessage(const Message & message)564 void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
565 switch (mEvent.type) {
566 case TvInputEventType::DEVICE_AVAILABLE: {
567 mHal->onDeviceAvailable(mEvent.deviceInfo);
568 } break;
569 case TvInputEventType::DEVICE_UNAVAILABLE: {
570 mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId);
571 } break;
572 case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: {
573 int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus);
574 mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus);
575 } break;
576 default:
577 ALOGE("Unrecognizable event");
578 }
579 }
580
TvInputCallback(JTvInputHal * hal)581 JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
582 mHal = hal;
583 }
584
notify(const TvInputEvent & event)585 Return<void> JTvInputHal::TvInputCallback::notify(const TvInputEvent& event) {
586 mHal->mLooper->sendMessage(new NotifyHandler(mHal, event), static_cast<int>(event.type));
587 return Void();
588 }
589
590 ////////////////////////////////////////////////////////////////////////////////
591
nativeOpen(JNIEnv * env,jobject thiz,jobject messageQueueObj)592 static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
593 sp<MessageQueue> messageQueue =
594 android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
595 return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
596 }
597
nativeAddOrUpdateStream(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint streamId,jobject jsurface)598 static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
599 jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
600 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
601 if (!jsurface) {
602 return BAD_VALUE;
603 }
604 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
605 if (!Surface::isValid(surface)) {
606 return BAD_VALUE;
607 }
608 return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
609 }
610
nativeRemoveStream(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint streamId)611 static int nativeRemoveStream(JNIEnv* env, jclass clazz,
612 jlong ptr, jint deviceId, jint streamId) {
613 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
614 return tvInputHal->removeStream(deviceId, streamId);
615 }
616
nativeGetStreamConfigs(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint generation)617 static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
618 jlong ptr, jint deviceId, jint generation) {
619 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
620 const hidl_vec<TvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId);
621
622 jobjectArray result = env->NewObjectArray(configs.size(), gTvStreamConfigClassInfo.clazz, NULL);
623 for (size_t i = 0; i < configs.size(); ++i) {
624 jobject builder = env->NewObject(
625 gTvStreamConfigBuilderClassInfo.clazz,
626 gTvStreamConfigBuilderClassInfo.constructor);
627 env->CallObjectMethod(
628 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].streamId);
629 env->CallObjectMethod(
630 builder, gTvStreamConfigBuilderClassInfo.type,
631 TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE);
632 env->CallObjectMethod(
633 builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].maxVideoWidth);
634 env->CallObjectMethod(
635 builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].maxVideoHeight);
636 env->CallObjectMethod(
637 builder, gTvStreamConfigBuilderClassInfo.generation, generation);
638
639 jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
640
641 env->SetObjectArrayElement(result, i, config);
642
643 env->DeleteLocalRef(config);
644 env->DeleteLocalRef(builder);
645 }
646 return result;
647 }
648
nativeClose(JNIEnv * env,jclass clazz,jlong ptr)649 static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
650 JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
651 delete tvInputHal;
652 }
653
654 static const JNINativeMethod gTvInputHalMethods[] = {
655 /* name, signature, funcPtr */
656 { "nativeOpen", "(Landroid/os/MessageQueue;)J",
657 (void*) nativeOpen },
658 { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
659 (void*) nativeAddOrUpdateStream },
660 { "nativeRemoveStream", "(JII)I",
661 (void*) nativeRemoveStream },
662 { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
663 (void*) nativeGetStreamConfigs },
664 { "nativeClose", "(J)V",
665 (void*) nativeClose },
666 };
667
668 #define FIND_CLASS(var, className) \
669 var = env->FindClass(className); \
670 LOG_FATAL_IF(! (var), "Unable to find class " className)
671
672 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
673 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
674 LOG_FATAL_IF(! (var), "Unable to find method" methodName)
675
register_android_server_tv_TvInputHal(JNIEnv * env)676 int register_android_server_tv_TvInputHal(JNIEnv* env) {
677 int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
678 gTvInputHalMethods, NELEM(gTvInputHalMethods));
679 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
680 (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
681
682 jclass clazz;
683 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
684
685 GET_METHOD_ID(
686 gTvInputHalClassInfo.deviceAvailable, clazz,
687 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
688 GET_METHOD_ID(
689 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
690 GET_METHOD_ID(gTvInputHalClassInfo.streamConfigsChanged, clazz,
691 "streamConfigsChangedFromNative", "(II)V");
692 GET_METHOD_ID(
693 gTvInputHalClassInfo.firstFrameCaptured, clazz,
694 "firstFrameCapturedFromNative", "(II)V");
695
696 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
697 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
698
699 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
700 gTvStreamConfigBuilderClassInfo.clazz =
701 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
702
703 GET_METHOD_ID(
704 gTvStreamConfigBuilderClassInfo.constructor,
705 gTvStreamConfigBuilderClassInfo.clazz,
706 "<init>", "()V");
707 GET_METHOD_ID(
708 gTvStreamConfigBuilderClassInfo.streamId,
709 gTvStreamConfigBuilderClassInfo.clazz,
710 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
711 GET_METHOD_ID(
712 gTvStreamConfigBuilderClassInfo.type,
713 gTvStreamConfigBuilderClassInfo.clazz,
714 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
715 GET_METHOD_ID(
716 gTvStreamConfigBuilderClassInfo.maxWidth,
717 gTvStreamConfigBuilderClassInfo.clazz,
718 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
719 GET_METHOD_ID(
720 gTvStreamConfigBuilderClassInfo.maxHeight,
721 gTvStreamConfigBuilderClassInfo.clazz,
722 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
723 GET_METHOD_ID(
724 gTvStreamConfigBuilderClassInfo.generation,
725 gTvStreamConfigBuilderClassInfo.clazz,
726 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
727 GET_METHOD_ID(
728 gTvStreamConfigBuilderClassInfo.build,
729 gTvStreamConfigBuilderClassInfo.clazz,
730 "build", "()Landroid/media/tv/TvStreamConfig;");
731
732 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
733 "android/media/tv/TvInputHardwareInfo$Builder");
734 gTvInputHardwareInfoBuilderClassInfo.clazz =
735 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
736
737 GET_METHOD_ID(
738 gTvInputHardwareInfoBuilderClassInfo.constructor,
739 gTvInputHardwareInfoBuilderClassInfo.clazz,
740 "<init>", "()V");
741 GET_METHOD_ID(
742 gTvInputHardwareInfoBuilderClassInfo.deviceId,
743 gTvInputHardwareInfoBuilderClassInfo.clazz,
744 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
745 GET_METHOD_ID(
746 gTvInputHardwareInfoBuilderClassInfo.type,
747 gTvInputHardwareInfoBuilderClassInfo.clazz,
748 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
749 GET_METHOD_ID(
750 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
751 gTvInputHardwareInfoBuilderClassInfo.clazz,
752 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
753 GET_METHOD_ID(
754 gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
755 gTvInputHardwareInfoBuilderClassInfo.clazz,
756 "cableConnectionStatus", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
757 GET_METHOD_ID(
758 gTvInputHardwareInfoBuilderClassInfo.audioType,
759 gTvInputHardwareInfoBuilderClassInfo.clazz,
760 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
761 GET_METHOD_ID(
762 gTvInputHardwareInfoBuilderClassInfo.audioAddress,
763 gTvInputHardwareInfoBuilderClassInfo.clazz,
764 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
765 GET_METHOD_ID(
766 gTvInputHardwareInfoBuilderClassInfo.build,
767 gTvInputHardwareInfoBuilderClassInfo.clazz,
768 "build", "()Landroid/media/tv/TvInputHardwareInfo;");
769
770 return 0;
771 }
772
773 } /* namespace android */
774