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