com_android_server_tv_TvInputHal.cpp revision a617074c531dfa317e5051fa70ec6b6c21d14386
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
268        virtual void handleMessage(const Message& message);
269
270    private:
271        tv_input_event_t mEvent;
272        JTvInputHal* mHal;
273    };
274
275    JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev, const sp<Looper>& looper);
276
277    static void notify(
278            tv_input_device_t* dev, tv_input_event_t* event, void* data);
279
280    Mutex mLock;
281    jweak mThiz;
282    tv_input_device_t* mDevice;
283    tv_input_callback_ops_t mCallback;
284    sp<Looper> mLooper;
285
286    KeyedVector<int, KeyedVector<int, Connection> > mConnections;
287};
288
289JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device,
290        const sp<Looper>& looper) {
291    mThiz = env->NewWeakGlobalRef(thiz);
292    mDevice = device;
293    mCallback.notify = &JTvInputHal::notify;
294    mLooper = looper;
295
296    mDevice->initialize(mDevice, &mCallback, this);
297}
298
299JTvInputHal::~JTvInputHal() {
300    mDevice->common.close((hw_device_t*)mDevice);
301
302    JNIEnv* env = AndroidRuntime::getJNIEnv();
303    env->DeleteWeakGlobalRef(mThiz);
304    mThiz = NULL;
305}
306
307JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
308    tv_input_module_t* module = NULL;
309    status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
310            (hw_module_t const**)&module);
311    if (err) {
312        ALOGE("Couldn't load %s module (%s)",
313                TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
314        return 0;
315    }
316
317    tv_input_device_t* device = NULL;
318    err = module->common.methods->open(
319            (hw_module_t*)module,
320            TV_INPUT_DEFAULT_DEVICE,
321            (hw_device_t**)&device);
322    if (err) {
323        ALOGE("Couldn't open %s device (%s)",
324                TV_INPUT_DEFAULT_DEVICE, strerror(-err));
325        return 0;
326    }
327
328    return new JTvInputHal(env, thiz, device, looper);
329}
330
331int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
332    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
333    if (connections.indexOfKey(streamId) < 0) {
334        connections.add(streamId, Connection());
335    }
336    Connection& connection = connections.editValueFor(streamId);
337    if (connection.mSurface == surface) {
338        // Nothing to do
339        return NO_ERROR;
340    }
341    // Clear the surface in the connection.
342    if (connection.mSurface != NULL) {
343        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
344            if (Surface::isValid(connection.mSurface)) {
345                connection.mSurface->setSidebandStream(NULL);
346            }
347        }
348        connection.mSurface.clear();
349    }
350    if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
351        // Need to configure stream
352        int numConfigs = 0;
353        const tv_stream_config_t* configs = NULL;
354        if (mDevice->get_stream_configurations(
355                mDevice, deviceId, &numConfigs, &configs) != 0) {
356            ALOGE("Couldn't get stream configs");
357            return UNKNOWN_ERROR;
358        }
359        int configIndex = -1;
360        for (int i = 0; i < numConfigs; ++i) {
361            if (configs[i].stream_id == streamId) {
362                configIndex = i;
363                break;
364            }
365        }
366        if (configIndex == -1) {
367            ALOGE("Cannot find a config with given stream ID: %d", streamId);
368            return BAD_VALUE;
369        }
370        connection.mStreamType = configs[configIndex].type;
371
372        tv_stream_t stream;
373        stream.stream_id = configs[configIndex].stream_id;
374        if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
375            stream.buffer_producer.width = configs[configIndex].max_video_width;
376            stream.buffer_producer.height = configs[configIndex].max_video_height;
377        }
378        if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
379            ALOGE("Couldn't add stream");
380            return UNKNOWN_ERROR;
381        }
382        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
383            connection.mSourceHandle = NativeHandle::create(
384                    stream.sideband_stream_source_handle, false);
385        } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
386            if (connection.mThread != NULL) {
387                connection.mThread->shutdown();
388            }
389            connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
390            connection.mThread->run();
391        }
392    }
393    connection.mSurface = surface;
394    if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
395        connection.mSurface->setSidebandStream(connection.mSourceHandle);
396    } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
397        connection.mThread->setSurface(surface);
398    }
399    return NO_ERROR;
400}
401
402int JTvInputHal::removeStream(int deviceId, int streamId) {
403    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
404    if (connections.indexOfKey(streamId) < 0) {
405        return BAD_VALUE;
406    }
407    Connection& connection = connections.editValueFor(streamId);
408    if (connection.mSurface == NULL) {
409        // Nothing to do
410        return NO_ERROR;
411    }
412    if (Surface::isValid(connection.mSurface)) {
413        connection.mSurface.clear();
414    }
415    if (connection.mSurface != NULL) {
416        connection.mSurface->setSidebandStream(NULL);
417        connection.mSurface.clear();
418    }
419    if (connection.mThread != NULL) {
420        connection.mThread->shutdown();
421        connection.mThread.clear();
422    }
423    if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
424        ALOGE("Couldn't remove stream");
425        return BAD_VALUE;
426    }
427    if (connection.mSourceHandle != NULL) {
428        connection.mSourceHandle.clear();
429    }
430    return NO_ERROR;
431}
432
433const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
434    const tv_stream_config_t* configs = NULL;
435    if (mDevice->get_stream_configurations(
436            mDevice, deviceId, numConfigs, &configs) != 0) {
437        ALOGE("Couldn't get stream configs");
438        return NULL;
439    }
440    return configs;
441}
442
443// static
444void JTvInputHal::notify(
445        tv_input_device_t* dev, tv_input_event_t* event, void* data) {
446    JTvInputHal* thiz = (JTvInputHal*)data;
447    thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type);
448}
449
450void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
451    {
452        Mutex::Autolock autoLock(&mLock);
453        mConnections.add(info.device_id, KeyedVector<int, Connection>());
454    }
455    JNIEnv* env = AndroidRuntime::getJNIEnv();
456
457    jobject builder = env->NewObject(
458            gTvInputHardwareInfoBuilderClassInfo.clazz,
459            gTvInputHardwareInfoBuilderClassInfo.constructor);
460    env->CallObjectMethod(
461            builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
462    env->CallObjectMethod(
463            builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
464    if (info.type == TV_INPUT_TYPE_HDMI) {
465        env->CallObjectMethod(
466                builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
467    }
468    env->CallObjectMethod(
469            builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
470    if (info.audio_type != AUDIO_DEVICE_NONE) {
471        jstring audioAddress = env->NewStringUTF(info.audio_address);
472        env->CallObjectMethod(
473                builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
474        env->DeleteLocalRef(audioAddress);
475    }
476
477    jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
478
479    env->CallVoidMethod(
480            mThiz,
481            gTvInputHalClassInfo.deviceAvailable,
482            infoObject);
483
484    env->DeleteLocalRef(builder);
485    env->DeleteLocalRef(infoObject);
486}
487
488void JTvInputHal::onDeviceUnavailable(int deviceId) {
489    {
490        Mutex::Autolock autoLock(&mLock);
491        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
492        for (size_t i = 0; i < connections.size(); ++i) {
493            removeStream(deviceId, connections.keyAt(i));
494        }
495        connections.clear();
496        mConnections.removeItem(deviceId);
497    }
498    JNIEnv* env = AndroidRuntime::getJNIEnv();
499    env->CallVoidMethod(
500            mThiz,
501            gTvInputHalClassInfo.deviceUnavailable,
502            deviceId);
503}
504
505void JTvInputHal::onStreamConfigurationsChanged(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    }
514    JNIEnv* env = AndroidRuntime::getJNIEnv();
515    env->CallVoidMethod(
516            mThiz,
517            gTvInputHalClassInfo.streamConfigsChanged,
518            deviceId);
519}
520
521void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
522    sp<BufferProducerThread> thread;
523    {
524        Mutex::Autolock autoLock(&mLock);
525        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
526        Connection& connection = connections.editValueFor(streamId);
527        if (connection.mThread == NULL) {
528            ALOGE("capture thread not existing.");
529            return;
530        }
531        thread = connection.mThread;
532    }
533    thread->onCaptured(seq, succeeded);
534    if (seq == 0) {
535        JNIEnv* env = AndroidRuntime::getJNIEnv();
536        env->CallVoidMethod(
537                mThiz,
538                gTvInputHalClassInfo.firstFrameCaptured,
539                deviceId,
540                streamId);
541    }
542}
543
544JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) {
545    mHal = hal;
546    memcpy(&mEvent, event, sizeof(mEvent));
547}
548
549void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
550    switch (mEvent.type) {
551        case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
552            mHal->onDeviceAvailable(mEvent.device_info);
553        } break;
554        case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
555            mHal->onDeviceUnavailable(mEvent.device_info.device_id);
556        } break;
557        case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
558            mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id);
559        } break;
560        case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
561            mHal->onCaptured(mEvent.capture_result.device_id,
562                             mEvent.capture_result.stream_id,
563                             mEvent.capture_result.seq,
564                             true /* succeeded */);
565        } break;
566        case TV_INPUT_EVENT_CAPTURE_FAILED: {
567            mHal->onCaptured(mEvent.capture_result.device_id,
568                             mEvent.capture_result.stream_id,
569                             mEvent.capture_result.seq,
570                             false /* succeeded */);
571        } break;
572        default:
573            ALOGE("Unrecognizable event");
574    }
575}
576
577////////////////////////////////////////////////////////////////////////////////
578
579static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
580    sp<MessageQueue> messageQueue =
581            android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
582    return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
583}
584
585static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
586        jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
587    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
588    if (!jsurface) {
589        return BAD_VALUE;
590    }
591    sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
592    return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
593}
594
595static int nativeRemoveStream(JNIEnv* env, jclass clazz,
596        jlong ptr, jint deviceId, jint streamId) {
597    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
598    return tvInputHal->removeStream(deviceId, streamId);
599}
600
601static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
602        jlong ptr, jint deviceId, jint generation) {
603    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
604    int numConfigs = 0;
605    const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
606
607    jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
608    for (int i = 0; i < numConfigs; ++i) {
609        jobject builder = env->NewObject(
610                gTvStreamConfigBuilderClassInfo.clazz,
611                gTvStreamConfigBuilderClassInfo.constructor);
612        env->CallObjectMethod(
613                builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
614        env->CallObjectMethod(
615                builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
616        env->CallObjectMethod(
617                builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
618        env->CallObjectMethod(
619                builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
620        env->CallObjectMethod(
621                builder, gTvStreamConfigBuilderClassInfo.generation, generation);
622
623        jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
624
625        env->SetObjectArrayElement(result, i, config);
626
627        env->DeleteLocalRef(config);
628        env->DeleteLocalRef(builder);
629    }
630    return result;
631}
632
633static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
634    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
635    delete tvInputHal;
636}
637
638static JNINativeMethod gTvInputHalMethods[] = {
639    /* name, signature, funcPtr */
640    { "nativeOpen", "(Landroid/os/MessageQueue;)J",
641            (void*) nativeOpen },
642    { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
643            (void*) nativeAddOrUpdateStream },
644    { "nativeRemoveStream", "(JII)I",
645            (void*) nativeRemoveStream },
646    { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
647            (void*) nativeGetStreamConfigs },
648    { "nativeClose", "(J)V",
649            (void*) nativeClose },
650};
651
652#define FIND_CLASS(var, className) \
653        var = env->FindClass(className); \
654        LOG_FATAL_IF(! var, "Unable to find class " className)
655
656#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
657        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
658        LOG_FATAL_IF(! var, "Unable to find method" methodName)
659
660int register_android_server_tv_TvInputHal(JNIEnv* env) {
661    int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
662            gTvInputHalMethods, NELEM(gTvInputHalMethods));
663    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
664
665    jclass clazz;
666    FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
667
668    GET_METHOD_ID(
669            gTvInputHalClassInfo.deviceAvailable, clazz,
670            "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
671    GET_METHOD_ID(
672            gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
673    GET_METHOD_ID(
674            gTvInputHalClassInfo.streamConfigsChanged, clazz,
675            "streamConfigsChangedFromNative", "(I)V");
676    GET_METHOD_ID(
677            gTvInputHalClassInfo.firstFrameCaptured, clazz,
678            "firstFrameCapturedFromNative", "(II)V");
679
680    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
681    gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
682
683    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
684    gTvStreamConfigBuilderClassInfo.clazz =
685            jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
686
687    GET_METHOD_ID(
688            gTvStreamConfigBuilderClassInfo.constructor,
689            gTvStreamConfigBuilderClassInfo.clazz,
690            "<init>", "()V");
691    GET_METHOD_ID(
692            gTvStreamConfigBuilderClassInfo.streamId,
693            gTvStreamConfigBuilderClassInfo.clazz,
694            "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
695    GET_METHOD_ID(
696            gTvStreamConfigBuilderClassInfo.type,
697            gTvStreamConfigBuilderClassInfo.clazz,
698            "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
699    GET_METHOD_ID(
700            gTvStreamConfigBuilderClassInfo.maxWidth,
701            gTvStreamConfigBuilderClassInfo.clazz,
702            "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
703    GET_METHOD_ID(
704            gTvStreamConfigBuilderClassInfo.maxHeight,
705            gTvStreamConfigBuilderClassInfo.clazz,
706            "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
707    GET_METHOD_ID(
708            gTvStreamConfigBuilderClassInfo.generation,
709            gTvStreamConfigBuilderClassInfo.clazz,
710            "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
711    GET_METHOD_ID(
712            gTvStreamConfigBuilderClassInfo.build,
713            gTvStreamConfigBuilderClassInfo.clazz,
714            "build", "()Landroid/media/tv/TvStreamConfig;");
715
716    FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
717            "android/media/tv/TvInputHardwareInfo$Builder");
718    gTvInputHardwareInfoBuilderClassInfo.clazz =
719            jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
720
721    GET_METHOD_ID(
722            gTvInputHardwareInfoBuilderClassInfo.constructor,
723            gTvInputHardwareInfoBuilderClassInfo.clazz,
724            "<init>", "()V");
725    GET_METHOD_ID(
726            gTvInputHardwareInfoBuilderClassInfo.deviceId,
727            gTvInputHardwareInfoBuilderClassInfo.clazz,
728            "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
729    GET_METHOD_ID(
730            gTvInputHardwareInfoBuilderClassInfo.type,
731            gTvInputHardwareInfoBuilderClassInfo.clazz,
732            "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
733    GET_METHOD_ID(
734            gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
735            gTvInputHardwareInfoBuilderClassInfo.clazz,
736            "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
737    GET_METHOD_ID(
738            gTvInputHardwareInfoBuilderClassInfo.audioType,
739            gTvInputHardwareInfoBuilderClassInfo.clazz,
740            "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
741    GET_METHOD_ID(
742            gTvInputHardwareInfoBuilderClassInfo.audioAddress,
743            gTvInputHardwareInfoBuilderClassInfo.clazz,
744            "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
745    GET_METHOD_ID(
746            gTvInputHardwareInfoBuilderClassInfo.build,
747            gTvInputHardwareInfoBuilderClassInfo.clazz,
748            "build", "()Landroid/media/tv/TvInputHardwareInfo;");
749
750    return 0;
751}
752
753} /* namespace android */
754