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