• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BufferProducerThread(tv_input_device_t * device,int deviceId,const tv_stream_t * stream)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 
readyToRun()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 
setSurface(const sp<Surface> & surface)153 void BufferProducerThread::setSurface(const sp<Surface>& surface) {
154     Mutex::Autolock autoLock(&mLock);
155     setSurfaceLocked(surface);
156 }
157 
setSurfaceLocked(const sp<Surface> & surface)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 
onCaptured(uint32_t seq,bool succeeded)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 
shutdown()197 void BufferProducerThread::shutdown() {
198     Mutex::Autolock autoLock(&mLock);
199     mShutdown = true;
200     setSurfaceLocked(NULL);
201     requestExitAndWait();
202 }
203 
threadLoop()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, int cableConnectionStatus);
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:
Connection()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         explicit 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     Mutex mStreamLock;
305     jweak mThiz;
306     sp<Looper> mLooper;
307 
308     KeyedVector<int, KeyedVector<int, Connection> > mConnections;
309 
310     sp<ITvInput> mTvInput;
311     sp<ITvInputCallback> mTvInputCallback;
312 };
313 
JTvInputHal(JNIEnv * env,jobject thiz,sp<ITvInput> tvInput,const sp<Looper> & looper)314 JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, sp<ITvInput> tvInput,
315         const sp<Looper>& looper) {
316     mThiz = env->NewWeakGlobalRef(thiz);
317     mTvInput = tvInput;
318     mLooper = looper;
319     mTvInputCallback = new TvInputCallback(this);
320     mTvInput->setCallback(mTvInputCallback);
321 }
322 
~JTvInputHal()323 JTvInputHal::~JTvInputHal() {
324     mTvInput->setCallback(nullptr);
325     JNIEnv* env = AndroidRuntime::getJNIEnv();
326     env->DeleteWeakGlobalRef(mThiz);
327     mThiz = NULL;
328 }
329 
createInstance(JNIEnv * env,jobject thiz,const sp<Looper> & looper)330 JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz, const sp<Looper>& looper) {
331     // TODO(b/31632518)
332     sp<ITvInput> tvInput = ITvInput::getService();
333     if (tvInput == nullptr) {
334         ALOGE("Couldn't get tv.input service.");
335         return nullptr;
336     }
337 
338     return new JTvInputHal(env, thiz, tvInput, looper);
339 }
340 
addOrUpdateStream(int deviceId,int streamId,const sp<Surface> & surface)341 int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>& surface) {
342     Mutex::Autolock autoLock(&mStreamLock);
343     KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
344     if (connections.indexOfKey(streamId) < 0) {
345         connections.add(streamId, Connection());
346     }
347     Connection& connection = connections.editValueFor(streamId);
348     if (connection.mSurface == surface) {
349         // Nothing to do
350         return NO_ERROR;
351     }
352     // Clear the surface in the connection.
353     if (connection.mSurface != NULL) {
354         if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
355             if (Surface::isValid(connection.mSurface)) {
356                 connection.mSurface->setSidebandStream(NULL);
357             }
358         }
359         connection.mSurface.clear();
360     }
361     if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
362         // Need to configure stream
363         Result result = Result::UNKNOWN;
364         hidl_vec<TvStreamConfig> list;
365         mTvInput->getStreamConfigurations(deviceId,
366                 [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
367                     result = res;
368                     if (res == Result::OK) {
369                         list = configs;
370                     }
371                 });
372         if (result != Result::OK) {
373             ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
374             return UNKNOWN_ERROR;
375         }
376         int configIndex = -1;
377         for (size_t i = 0; i < list.size(); ++i) {
378             if (list[i].streamId == streamId) {
379                 configIndex = i;
380                 break;
381             }
382         }
383         if (configIndex == -1) {
384             ALOGE("Cannot find a config with given stream ID: %d", streamId);
385             return BAD_VALUE;
386         }
387         connection.mStreamType = TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE;
388 
389         result = Result::UNKNOWN;
390         const native_handle_t* sidebandStream;
391         mTvInput->openStream(deviceId, streamId,
392                 [&result, &sidebandStream](Result res, const native_handle_t* handle) {
393                     result = res;
394                     if (res == Result::OK) {
395                         if (handle) {
396                             sidebandStream = native_handle_clone(handle);
397                         } else {
398                             result = Result::UNKNOWN;
399                         }
400                     }
401                 });
402         if (result != Result::OK) {
403             ALOGE("Couldn't open stream. device id:%d stream id:%d result:%d", deviceId, streamId,
404                     result);
405             return UNKNOWN_ERROR;
406         }
407         connection.mSourceHandle = NativeHandle::create((native_handle_t*)sidebandStream, true);
408     }
409     connection.mSurface = surface;
410     if (connection.mSurface != nullptr) {
411         connection.mSurface->setSidebandStream(connection.mSourceHandle);
412     }
413     return NO_ERROR;
414 }
415 
removeStream(int deviceId,int streamId)416 int JTvInputHal::removeStream(int deviceId, int streamId) {
417     Mutex::Autolock autoLock(&mStreamLock);
418     KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
419     if (connections.indexOfKey(streamId) < 0) {
420         return BAD_VALUE;
421     }
422     Connection& connection = connections.editValueFor(streamId);
423     if (connection.mSurface == NULL) {
424         // Nothing to do
425         return NO_ERROR;
426     }
427     if (Surface::isValid(connection.mSurface)) {
428         connection.mSurface->setSidebandStream(NULL);
429     }
430     connection.mSurface.clear();
431     if (connection.mThread != NULL) {
432         connection.mThread->shutdown();
433         connection.mThread.clear();
434     }
435     if (mTvInput->closeStream(deviceId, streamId) != Result::OK) {
436         ALOGE("Couldn't close stream. device id:%d stream id:%d", deviceId, streamId);
437         return BAD_VALUE;
438     }
439     if (connection.mSourceHandle != NULL) {
440         connection.mSourceHandle.clear();
441     }
442     return NO_ERROR;
443 }
444 
getStreamConfigs(int deviceId)445 const hidl_vec<TvStreamConfig> JTvInputHal::getStreamConfigs(int deviceId) {
446     Result result = Result::UNKNOWN;
447     hidl_vec<TvStreamConfig> list;
448     mTvInput->getStreamConfigurations(deviceId,
449             [&result, &list](Result res, hidl_vec<TvStreamConfig> configs) {
450                 result = res;
451                 if (res == Result::OK) {
452                     list = configs;
453                 }
454             });
455     if (result != Result::OK) {
456         ALOGE("Couldn't get stream configs for device id:%d result:%d", deviceId, result);
457     }
458     return list;
459 }
460 
onDeviceAvailable(const TvInputDeviceInfo & info)461 void JTvInputHal::onDeviceAvailable(const TvInputDeviceInfo& info) {
462     {
463         Mutex::Autolock autoLock(&mLock);
464         mConnections.add(info.deviceId, KeyedVector<int, Connection>());
465     }
466     JNIEnv* env = AndroidRuntime::getJNIEnv();
467 
468     jobject builder = env->NewObject(
469             gTvInputHardwareInfoBuilderClassInfo.clazz,
470             gTvInputHardwareInfoBuilderClassInfo.constructor);
471     env->CallObjectMethod(
472             builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.deviceId);
473     env->CallObjectMethod(
474             builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
475     if (info.type == TvInputType::HDMI) {
476         env->CallObjectMethod(
477                 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId);
478     }
479     env->CallObjectMethod(
480             builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
481             info.cableConnectionStatus);
482     env->CallObjectMethod(
483             builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType);
484     if (info.audioType != AudioDevice::NONE) {
485         uint8_t buffer[info.audioAddress.size() + 1];
486         memcpy(buffer, info.audioAddress.data(), info.audioAddress.size());
487         buffer[info.audioAddress.size()] = '\0';
488         jstring audioAddress = env->NewStringUTF(reinterpret_cast<const char *>(buffer));
489         env->CallObjectMethod(
490                 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
491         env->DeleteLocalRef(audioAddress);
492     }
493 
494     jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
495 
496     env->CallVoidMethod(
497             mThiz,
498             gTvInputHalClassInfo.deviceAvailable,
499             infoObject);
500 
501     env->DeleteLocalRef(builder);
502     env->DeleteLocalRef(infoObject);
503 }
504 
onDeviceUnavailable(int deviceId)505 void JTvInputHal::onDeviceUnavailable(int deviceId) {
506     {
507         Mutex::Autolock autoLock(&mLock);
508         KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
509         for (size_t i = 0; i < connections.size(); ++i) {
510             removeStream(deviceId, connections.keyAt(i));
511         }
512         connections.clear();
513         mConnections.removeItem(deviceId);
514     }
515     JNIEnv* env = AndroidRuntime::getJNIEnv();
516     env->CallVoidMethod(
517             mThiz,
518             gTvInputHalClassInfo.deviceUnavailable,
519             deviceId);
520 }
521 
onStreamConfigurationsChanged(int deviceId,int cableConnectionStatus)522 void JTvInputHal::onStreamConfigurationsChanged(int deviceId, int cableConnectionStatus) {
523     {
524         Mutex::Autolock autoLock(&mLock);
525         KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
526         for (size_t i = 0; i < connections.size(); ++i) {
527             removeStream(deviceId, connections.keyAt(i));
528         }
529         connections.clear();
530     }
531     JNIEnv* env = AndroidRuntime::getJNIEnv();
532     env->CallVoidMethod(mThiz, gTvInputHalClassInfo.streamConfigsChanged, deviceId,
533                         cableConnectionStatus);
534 }
535 
onCaptured(int deviceId,int streamId,uint32_t seq,bool succeeded)536 void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
537     sp<BufferProducerThread> thread;
538     {
539         Mutex::Autolock autoLock(&mLock);
540         KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
541         Connection& connection = connections.editValueFor(streamId);
542         if (connection.mThread == NULL) {
543             ALOGE("capture thread not existing.");
544             return;
545         }
546         thread = connection.mThread;
547     }
548     thread->onCaptured(seq, succeeded);
549     if (seq == 0) {
550         JNIEnv* env = AndroidRuntime::getJNIEnv();
551         env->CallVoidMethod(
552                 mThiz,
553                 gTvInputHalClassInfo.firstFrameCaptured,
554                 deviceId,
555                 streamId);
556     }
557 }
558 
NotifyHandler(JTvInputHal * hal,const TvInputEvent & event)559 JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const TvInputEvent& event) {
560     mHal = hal;
561     mEvent = event;
562 }
563 
handleMessage(const Message & message)564 void JTvInputHal::NotifyHandler::handleMessage(const Message& message) {
565     switch (mEvent.type) {
566         case TvInputEventType::DEVICE_AVAILABLE: {
567             mHal->onDeviceAvailable(mEvent.deviceInfo);
568         } break;
569         case TvInputEventType::DEVICE_UNAVAILABLE: {
570             mHal->onDeviceUnavailable(mEvent.deviceInfo.deviceId);
571         } break;
572         case TvInputEventType::STREAM_CONFIGURATIONS_CHANGED: {
573             int cableConnectionStatus = static_cast<int>(mEvent.deviceInfo.cableConnectionStatus);
574             mHal->onStreamConfigurationsChanged(mEvent.deviceInfo.deviceId, cableConnectionStatus);
575         } break;
576         default:
577             ALOGE("Unrecognizable event");
578     }
579 }
580 
TvInputCallback(JTvInputHal * hal)581 JTvInputHal::TvInputCallback::TvInputCallback(JTvInputHal* hal) {
582     mHal = hal;
583 }
584 
notify(const TvInputEvent & event)585 Return<void> JTvInputHal::TvInputCallback::notify(const TvInputEvent& event) {
586     mHal->mLooper->sendMessage(new NotifyHandler(mHal, event), static_cast<int>(event.type));
587     return Void();
588 }
589 
590 ////////////////////////////////////////////////////////////////////////////////
591 
nativeOpen(JNIEnv * env,jobject thiz,jobject messageQueueObj)592 static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) {
593     sp<MessageQueue> messageQueue =
594             android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
595     return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper());
596 }
597 
nativeAddOrUpdateStream(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint streamId,jobject jsurface)598 static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz,
599         jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
600     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
601     if (!jsurface) {
602         return BAD_VALUE;
603     }
604     sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
605     if (!Surface::isValid(surface)) {
606         return BAD_VALUE;
607     }
608     return tvInputHal->addOrUpdateStream(deviceId, streamId, surface);
609 }
610 
nativeRemoveStream(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint streamId)611 static int nativeRemoveStream(JNIEnv* env, jclass clazz,
612         jlong ptr, jint deviceId, jint streamId) {
613     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
614     return tvInputHal->removeStream(deviceId, streamId);
615 }
616 
nativeGetStreamConfigs(JNIEnv * env,jclass clazz,jlong ptr,jint deviceId,jint generation)617 static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
618         jlong ptr, jint deviceId, jint generation) {
619     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
620     const hidl_vec<TvStreamConfig> configs = tvInputHal->getStreamConfigs(deviceId);
621 
622     jobjectArray result = env->NewObjectArray(configs.size(), gTvStreamConfigClassInfo.clazz, NULL);
623     for (size_t i = 0; i < configs.size(); ++i) {
624         jobject builder = env->NewObject(
625                 gTvStreamConfigBuilderClassInfo.clazz,
626                 gTvStreamConfigBuilderClassInfo.constructor);
627         env->CallObjectMethod(
628                 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].streamId);
629         env->CallObjectMethod(
630                 builder, gTvStreamConfigBuilderClassInfo.type,
631                         TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE);
632         env->CallObjectMethod(
633                 builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].maxVideoWidth);
634         env->CallObjectMethod(
635                 builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].maxVideoHeight);
636         env->CallObjectMethod(
637                 builder, gTvStreamConfigBuilderClassInfo.generation, generation);
638 
639         jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
640 
641         env->SetObjectArrayElement(result, i, config);
642 
643         env->DeleteLocalRef(config);
644         env->DeleteLocalRef(builder);
645     }
646     return result;
647 }
648 
nativeClose(JNIEnv * env,jclass clazz,jlong ptr)649 static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
650     JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
651     delete tvInputHal;
652 }
653 
654 static const JNINativeMethod gTvInputHalMethods[] = {
655     /* name, signature, funcPtr */
656     { "nativeOpen", "(Landroid/os/MessageQueue;)J",
657             (void*) nativeOpen },
658     { "nativeAddOrUpdateStream", "(JIILandroid/view/Surface;)I",
659             (void*) nativeAddOrUpdateStream },
660     { "nativeRemoveStream", "(JII)I",
661             (void*) nativeRemoveStream },
662     { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;",
663             (void*) nativeGetStreamConfigs },
664     { "nativeClose", "(J)V",
665             (void*) nativeClose },
666 };
667 
668 #define FIND_CLASS(var, className) \
669         var = env->FindClass(className); \
670         LOG_FATAL_IF(! (var), "Unable to find class " className)
671 
672 #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
673         var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
674         LOG_FATAL_IF(! (var), "Unable to find method" methodName)
675 
register_android_server_tv_TvInputHal(JNIEnv * env)676 int register_android_server_tv_TvInputHal(JNIEnv* env) {
677     int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
678             gTvInputHalMethods, NELEM(gTvInputHalMethods));
679     LOG_FATAL_IF(res < 0, "Unable to register native methods.");
680     (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
681 
682     jclass clazz;
683     FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
684 
685     GET_METHOD_ID(
686             gTvInputHalClassInfo.deviceAvailable, clazz,
687             "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
688     GET_METHOD_ID(
689             gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
690     GET_METHOD_ID(gTvInputHalClassInfo.streamConfigsChanged, clazz,
691                   "streamConfigsChangedFromNative", "(II)V");
692     GET_METHOD_ID(
693             gTvInputHalClassInfo.firstFrameCaptured, clazz,
694             "firstFrameCapturedFromNative", "(II)V");
695 
696     FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig");
697     gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
698 
699     FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder");
700     gTvStreamConfigBuilderClassInfo.clazz =
701             jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
702 
703     GET_METHOD_ID(
704             gTvStreamConfigBuilderClassInfo.constructor,
705             gTvStreamConfigBuilderClassInfo.clazz,
706             "<init>", "()V");
707     GET_METHOD_ID(
708             gTvStreamConfigBuilderClassInfo.streamId,
709             gTvStreamConfigBuilderClassInfo.clazz,
710             "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
711     GET_METHOD_ID(
712             gTvStreamConfigBuilderClassInfo.type,
713             gTvStreamConfigBuilderClassInfo.clazz,
714             "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
715     GET_METHOD_ID(
716             gTvStreamConfigBuilderClassInfo.maxWidth,
717             gTvStreamConfigBuilderClassInfo.clazz,
718             "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
719     GET_METHOD_ID(
720             gTvStreamConfigBuilderClassInfo.maxHeight,
721             gTvStreamConfigBuilderClassInfo.clazz,
722             "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
723     GET_METHOD_ID(
724             gTvStreamConfigBuilderClassInfo.generation,
725             gTvStreamConfigBuilderClassInfo.clazz,
726             "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;");
727     GET_METHOD_ID(
728             gTvStreamConfigBuilderClassInfo.build,
729             gTvStreamConfigBuilderClassInfo.clazz,
730             "build", "()Landroid/media/tv/TvStreamConfig;");
731 
732     FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
733             "android/media/tv/TvInputHardwareInfo$Builder");
734     gTvInputHardwareInfoBuilderClassInfo.clazz =
735             jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
736 
737     GET_METHOD_ID(
738             gTvInputHardwareInfoBuilderClassInfo.constructor,
739             gTvInputHardwareInfoBuilderClassInfo.clazz,
740             "<init>", "()V");
741     GET_METHOD_ID(
742             gTvInputHardwareInfoBuilderClassInfo.deviceId,
743             gTvInputHardwareInfoBuilderClassInfo.clazz,
744             "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
745     GET_METHOD_ID(
746             gTvInputHardwareInfoBuilderClassInfo.type,
747             gTvInputHardwareInfoBuilderClassInfo.clazz,
748             "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
749     GET_METHOD_ID(
750             gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
751             gTvInputHardwareInfoBuilderClassInfo.clazz,
752             "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
753     GET_METHOD_ID(
754             gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
755             gTvInputHardwareInfoBuilderClassInfo.clazz,
756             "cableConnectionStatus", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
757     GET_METHOD_ID(
758             gTvInputHardwareInfoBuilderClassInfo.audioType,
759             gTvInputHardwareInfoBuilderClassInfo.clazz,
760             "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
761     GET_METHOD_ID(
762             gTvInputHardwareInfoBuilderClassInfo.audioAddress,
763             gTvInputHardwareInfoBuilderClassInfo.clazz,
764             "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
765     GET_METHOD_ID(
766             gTvInputHardwareInfoBuilderClassInfo.build,
767             gTvInputHardwareInfoBuilderClassInfo.clazz,
768             "build", "()Landroid/media/tv/TvInputHardwareInfo;");
769 
770     return 0;
771 }
772 
773 } /* namespace android */
774