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