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 35namespace android { 36 37static struct { 38 jmethodID deviceAvailable; 39 jmethodID deviceUnavailable; 40 jmethodID streamConfigsChanged; 41 jmethodID firstFrameCaptured; 42} gTvInputHalClassInfo; 43 44static struct { 45 jclass clazz; 46} gTvStreamConfigClassInfo; 47 48static 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 60static 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 74class BufferProducerThread : public Thread { 75public: 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 84private: 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 105BufferProducerThread::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 117status_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 135void BufferProducerThread::setSurface(const sp<Surface>& surface) { 136 Mutex::Autolock autoLock(&mLock); 137 setSurfaceLocked(surface); 138} 139 140void 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 162void 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 179void BufferProducerThread::shutdown() { 180 Mutex::Autolock autoLock(&mLock); 181 mShutdown = true; 182 setSurfaceLocked(NULL); 183 requestExitAndWait(); 184} 185 186bool 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 234class JTvInputHal { 235public: 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 249private: 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 293JTvInputHal::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 303JTvInputHal::~JTvInputHal() { 304 mDevice->common.close((hw_device_t*)mDevice); 305 306 JNIEnv* env = AndroidRuntime::getJNIEnv(); 307 env->DeleteWeakGlobalRef(mThiz); 308 mThiz = NULL; 309} 310 311JTvInputHal* 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 335int 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 406int 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 437const 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 448void 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 455void 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 468void 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 506void 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 523void 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 539void 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 562JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) { 563 mHal = hal; 564 cloneTvInputEvent(&mEvent, event); 565} 566 567JTvInputHal::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 576void 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 606static 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 612static 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 622static 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 628static 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 660static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { 661 JTvInputHal* tvInputHal = (JTvInputHal*)ptr; 662 delete tvInputHal; 663} 664 665static 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 687int 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