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