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 <nativehelper/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 
     38 using ::android::hardware::audio::common::V2_0::AudioDevice;
     39 using ::android::hardware::tv::input::V1_0::ITvInput;
     40 using ::android::hardware::tv::input::V1_0::ITvInputCallback;
     41 using ::android::hardware::tv::input::V1_0::Result;
     42 using ::android::hardware::tv::input::V1_0::TvInputDeviceInfo;
     43 using ::android::hardware::tv::input::V1_0::TvInputEvent;
     44 using ::android::hardware::tv::input::V1_0::TvInputEventType;
     45 using ::android::hardware::tv::input::V1_0::TvInputType;
     46 using ::android::hardware::tv::input::V1_0::TvStreamConfig;
     47 using ::android::hardware::Return;
     48 using ::android::hardware::Void;
     49 using ::android::hardware::hidl_vec;
     50 using ::android::hardware::hidl_string;
     51 
     52 namespace android {
     53 
     54 static struct {
     55     jmethodID deviceAvailable;
     56     jmethodID deviceUnavailable;
     57     jmethodID streamConfigsChanged;
     58     jmethodID firstFrameCaptured;
     59 } gTvInputHalClassInfo;
     60 
     61 static struct {
     62     jclass clazz;
     63 } gTvStreamConfigClassInfo;
     64 
     65 static 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 
     77 static 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 
     92 class BufferProducerThread : public Thread {
     93 public:
     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 
    102 private:
    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 
    123 BufferProducerThread::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 
    135 status_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 
    153 void BufferProducerThread::setSurface(const sp<Surface>& surface) {
    154     Mutex::Autolock autoLock(&mLock);
    155     setSurfaceLocked(surface);
    156 }
    157 
    158 void 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 
    180 void 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 
    197 void BufferProducerThread::shutdown() {
    198     Mutex::Autolock autoLock(&mLock);
    199     mShutdown = true;
    200     setSurfaceLocked(NULL);
    201     requestExitAndWait();
    202 }
    203 
    204 bool 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 
    252 class JTvInputHal {
    253 public:
    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 
    267 private:
    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 
    313 JTvInputHal::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 
    322 JTvInputHal::~JTvInputHal() {
    323     mTvInput->setCallback(nullptr);
    324     JNIEnv* env = AndroidRuntime::getJNIEnv();
    325     env->DeleteWeakGlobalRef(mThiz);
    326     mThiz = NULL;
    327 }
    328 
    329 JTvInputHal* 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 
    340 int 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 
    410 int 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 
    438 const 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 
    454 void 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 
    498 void 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 
    515 void 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 
    531 void 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 
    554 JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) {
    555     mHal = hal;
    556     mEvent = event;
    557 }
    558 
    559 void 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 
    575 JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
    576     mHal = hal;
    577 }
    578 
    579 Return<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 
    586 static 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 
    592 static 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 
    605 static 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 
    611 static 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 
    643 static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
    644     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
    645     delete tvInputHal;
    646 }
    647 
    648 static 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 
    670 int 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