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 "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>
31#include <utils/Looper.h>
32#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;
41    jmethodID firstFrameCaptured;
42} 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
60static struct {
61    jclass clazz;
62
63    jmethodID constructor;
64    jmethodID deviceId;
65    jmethodID type;
66    jmethodID hdmiPortId;
67    jmethodID audioType;
68    jmethodID audioAddress;
69    jmethodID build;
70} gTvInputHardwareInfoBuilderClassInfo;
71
72////////////////////////////////////////////////////////////////////////////////
73
74class 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
234class JTvInputHal {
235public:
236    ~JTvInputHal();
237
238    static JTvInputHal* createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper);
239
240    int addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface);
241    int removeStream(int deviceId, int streamId);
242    const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
243
244    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
249private:
250    // Connection between a surface and a stream.
251    class Connection {
252    public:
253        Connection() {}
254
255        sp<Surface> mSurface;
256        tv_stream_type_t mStreamType;
257
258        // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
259        sp<NativeHandle> mSourceHandle;
260        // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
261        sp<BufferProducerThread> mThread;
262    };
263
264    class NotifyHandler : public MessageHandler {
265    public:
266        NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event);
267        ~NotifyHandler();
268
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);
277
278    static void notify(
279            tv_input_device_t* dev, tv_input_event_t* event, void* data);
280
281    static void cloneTvInputEvent(
282            tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent);
283
284    Mutex mLock;
285    jweak mThiz;
286    tv_input_device_t* mDevice;
287    tv_input_callback_ops_t mCallback;
288    sp<Looper> mLooper;
289
290    KeyedVector<int, KeyedVector<int, Connection> > mConnections;
291};
292
293JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
294        const sp<Looper>& looper) {
295    mThiz = env->NewWeakGlobalRef(thiz);
296    mDevice = device;
297    mCallback.notify = &JTvInputHal::notify;
298    mLooper = looper;
299
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
311JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
312    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
332    return new JTvInputHal(env, thiz, device, looper);
333}
334
335int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
336    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) {
342        // Nothing to do
343        return NO_ERROR;
344    }
345    // 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        }
352        connection.mSurface.clear();
353    }
354    if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
355        // 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        }
374        connection.mStreamType = configs[configIndex].type;
375
376        tv_stream_t stream;
377        stream.stream_id = configs[configIndex].stream_id;
378        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        }
382        if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
383            ALOGE("Couldn't add stream");
384            return UNKNOWN_ERROR;
385        }
386        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) {
399        connection.mSurface->setSidebandStream(connection.mSourceHandle);
400    } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
401        connection.mThread->setSurface(surface);
402    }
403    return NO_ERROR;
404}
405
406int 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    }
423    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    }
431    if (connection.mSourceHandle != NULL) {
432        connection.mSourceHandle.clear();
433    }
434    return NO_ERROR;
435}
436
437const 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
447// static
448void JTvInputHal::notify(
449        tv_input_device_t* dev, tv_input_event_t* event, void* data) {
450    JTvInputHal* thiz = (JTvInputHal*)data;
451    thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
452}
453
454// 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
468void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
469    {
470        Mutex::Autolock autoLock(&mLock);
471        mConnections.add(info.device_id, KeyedVector<int, Connection>());
472    }
473    JNIEnv* env = AndroidRuntime::getJNIEnv();
474
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);
482    if (info.type == TV_INPUT_TYPE_HDMI) {
483        env->CallObjectMethod(
484                builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
485    }
486    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
497    env->CallVoidMethod(
498            mThiz,
499            gTvInputHalClassInfo.deviceAvailable,
500            infoObject);
501
502    env->DeleteLocalRef(builder);
503    env->DeleteLocalRef(infoObject);
504}
505
506void JTvInputHal::onDeviceUnavailable(int deviceId) {
507    {
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);
515    }
516    JNIEnv* env = AndroidRuntime::getJNIEnv();
517    env->CallVoidMethod(
518            mThiz,
519            gTvInputHalClassInfo.deviceUnavailable,
520            deviceId);
521}
522
523void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
524    {
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();
531    }
532    JNIEnv* env = AndroidRuntime::getJNIEnv();
533    env->CallVoidMethod(
534            mThiz,
535            gTvInputHalClassInfo.streamConfigsChanged,
536            deviceId);
537}
538
539void 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);
552    if (seq == 0) {
553        JNIEnv* env = AndroidRuntime::getJNIEnv();
554        env->CallVoidMethod(
555                mThiz,
556                gTvInputHalClassInfo.firstFrameCaptured,
557                deviceId,
558                streamId);
559    }
560}
561
562JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
563    mHal = hal;
564    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    }
574}
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
604////////////////////////////////////////////////////////////////////////////////
605
606static 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());
610}
611
612static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
613        jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
614    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
615    if (!jsurface) {
616        return BAD_VALUE;
617    }
618    sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
619    return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
620}
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);
626}
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 */
667    { "nativeOpen", "(Landroid/os/MessageQueue;)J",
668            (void*) nativeOpen },
669    { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
670            (void*) nativeAddOrUpdateStream },
671    { "nativeRemoveStream", "(JII)I",
672            (void*) nativeRemoveStream },
673    { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
674            (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(
696            gTvInputHalClassInfo.deviceAvailable, clazz,
697            "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
698    GET_METHOD_ID(
699            gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
700    GET_METHOD_ID(
701            gTvInputHalClassInfo.streamConfigsChanged, clazz,
702            "streamConfigsChangedFromNative", "(I)V");
703    GET_METHOD_ID(
704            gTvInputHalClassInfo.firstFrameCaptured, clazz,
705            "firstFrameCapturedFromNative", "(II)V");
706
707    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
708    gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
709
710    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
711    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,
721            "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
722    GET_METHOD_ID(
723            gTvStreamConfigBuilderClassInfo.type,
724            gTvStreamConfigBuilderClassInfo.clazz,
725            "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
726    GET_METHOD_ID(
727            gTvStreamConfigBuilderClassInfo.maxWidth,
728            gTvStreamConfigBuilderClassInfo.clazz,
729            "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
730    GET_METHOD_ID(
731            gTvStreamConfigBuilderClassInfo.maxHeight,
732            gTvStreamConfigBuilderClassInfo.clazz,
733            "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
734    GET_METHOD_ID(
735            gTvStreamConfigBuilderClassInfo.generation,
736            gTvStreamConfigBuilderClassInfo.clazz,
737            "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
738    GET_METHOD_ID(
739            gTvStreamConfigBuilderClassInfo.build,
740            gTvStreamConfigBuilderClassInfo.clazz,
741            "build", "()Landroid/media/tv/TvStreamConfig;");
742
743    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(
761            gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
762            gTvInputHardwareInfoBuilderClassInfo.clazz,
763            "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
764    GET_METHOD_ID(
765            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
777    return 0;
778}
779
780} /* namespace android */
781