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("BufferProducerThread"); 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->setSidebandStream(NULL); 418 } 419 connection.mSurface.clear(); 420 if (connection.mThread != NULL) { 421 connection.mThread->shutdown(); 422 connection.mThread.clear(); 423 } 424 if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) { 425 ALOGE("Couldn't remove stream"); 426 return BAD_VALUE; 427 } 428 if (connection.mSourceHandle != NULL) { 429 connection.mSourceHandle.clear(); 430 } 431 return NO_ERROR; 432 } 433 434 const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) { 435 const tv_stream_config_t* configs = NULL; 436 if (mDevice->get_stream_configurations( 437 mDevice, deviceId, numConfigs, &configs) != 0) { 438 ALOGE("Couldn't get stream configs"); 439 return NULL; 440 } 441 return configs; 442 } 443 444 // static 445 void JTvInputHal::notify( 446 tv_input_device_t* dev, tv_input_event_t* event, void* data) { 447 JTvInputHal* thiz = (JTvInputHal*)data; 448 thiz->mLooper->sendMessage(new NotifyHandler(thiz, event), event->type); 449 } 450 451 // static 452 void JTvInputHal::cloneTvInputEvent( 453 tv_input_event_t* dstEvent, const tv_input_event_t* srcEvent) { 454 memcpy(dstEvent, srcEvent, sizeof(tv_input_event_t)); 455 if ((srcEvent->type == TV_INPUT_EVENT_DEVICE_AVAILABLE || 456 srcEvent->type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE || 457 srcEvent->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) && 458 srcEvent->device_info.audio_address != NULL){ 459 char* audio_address = new char[strlen(srcEvent->device_info.audio_address) + 1]; 460 strcpy(audio_address, srcEvent->device_info.audio_address); 461 dstEvent->device_info.audio_address = audio_address; 462 } 463 } 464 465 void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { 466 { 467 Mutex::Autolock autoLock(&mLock); 468 mConnections.add(info.device_id, KeyedVector<int, Connection>()); 469 } 470 JNIEnv* env = AndroidRuntime::getJNIEnv(); 471 472 jobject builder = env->NewObject( 473 gTvInputHardwareInfoBuilderClassInfo.clazz, 474 gTvInputHardwareInfoBuilderClassInfo.constructor); 475 env->CallObjectMethod( 476 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id); 477 env->CallObjectMethod( 478 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); 479 if (info.type == TV_INPUT_TYPE_HDMI) { 480 env->CallObjectMethod( 481 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id); 482 } 483 env->CallObjectMethod( 484 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type); 485 if (info.audio_type != AUDIO_DEVICE_NONE) { 486 jstring audioAddress = env->NewStringUTF(info.audio_address); 487 env->CallObjectMethod( 488 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress); 489 env->DeleteLocalRef(audioAddress); 490 } 491 492 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); 493 494 env->CallVoidMethod( 495 mThiz, 496 gTvInputHalClassInfo.deviceAvailable, 497 infoObject); 498 499 env->DeleteLocalRef(builder); 500 env->DeleteLocalRef(infoObject); 501 } 502 503 void JTvInputHal::onDeviceUnavailable(int deviceId) { 504 { 505 Mutex::Autolock autoLock(&mLock); 506 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); 507 for (size_t i = 0; i < connections.size(); ++i) { 508 removeStream(deviceId, connections.keyAt(i)); 509 } 510 connections.clear(); 511 mConnections.removeItem(deviceId); 512 } 513 JNIEnv* env = AndroidRuntime::getJNIEnv(); 514 env->CallVoidMethod( 515 mThiz, 516 gTvInputHalClassInfo.deviceUnavailable, 517 deviceId); 518 } 519 520 void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { 521 { 522 Mutex::Autolock autoLock(&mLock); 523 KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId); 524 for (size_t i = 0; i < connections.size(); ++i) { 525 removeStream(deviceId, connections.keyAt(i)); 526 } 527 connections.clear(); 528 } 529 JNIEnv* env = AndroidRuntime::getJNIEnv(); 530 env->CallVoidMethod( 531 mThiz, 532 gTvInputHalClassInfo.streamConfigsChanged, 533 deviceId); 534 } 535 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 559 JTvInputHal::NotifyHandler::NotifyHandler(JTvInputHal* hal, const tv_input_event_t* event) { 560 mHal = hal; 561 cloneTvInputEvent(&mEvent, event); 562 } 563 564 JTvInputHal::NotifyHandler::~NotifyHandler() { 565 if ((mEvent.type == TV_INPUT_EVENT_DEVICE_AVAILABLE || 566 mEvent.type == TV_INPUT_EVENT_DEVICE_UNAVAILABLE || 567 mEvent.type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) && 568 mEvent.device_info.audio_address != NULL) { 569 delete mEvent.device_info.audio_address; 570 } 571 } 572 573 void JTvInputHal::NotifyHandler::handleMessage(const Message& message) { 574 switch (mEvent.type) { 575 case TV_INPUT_EVENT_DEVICE_AVAILABLE: { 576 mHal->onDeviceAvailable(mEvent.device_info); 577 } break; 578 case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { 579 mHal->onDeviceUnavailable(mEvent.device_info.device_id); 580 } break; 581 case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { 582 mHal->onStreamConfigurationsChanged(mEvent.device_info.device_id); 583 } break; 584 case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: { 585 mHal->onCaptured(mEvent.capture_result.device_id, 586 mEvent.capture_result.stream_id, 587 mEvent.capture_result.seq, 588 true /* succeeded */); 589 } break; 590 case TV_INPUT_EVENT_CAPTURE_FAILED: { 591 mHal->onCaptured(mEvent.capture_result.device_id, 592 mEvent.capture_result.stream_id, 593 mEvent.capture_result.seq, 594 false /* succeeded */); 595 } break; 596 default: 597 ALOGE("Unrecognizable event"); 598 } 599 } 600 601 //////////////////////////////////////////////////////////////////////////////// 602 603 static jlong nativeOpen(JNIEnv* env, jobject thiz, jobject messageQueueObj) { 604 sp<MessageQueue> messageQueue = 605 android_os_MessageQueue_getMessageQueue(env, messageQueueObj); 606 return (jlong)JTvInputHal::createInstance(env, thiz, messageQueue->getLooper()); 607 } 608 609 static int nativeAddOrUpdateStream(JNIEnv* env, jclass clazz, 610 jlong ptr, jint deviceId, jint streamId, jobject jsurface) { 611 JTvInputHal* tvInputHal = (JTvInputHal*)ptr; 612 if (!jsurface) { 613 return BAD_VALUE; 614 } 615 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); 616 if (!Surface::isValid(surface)) { 617 return BAD_VALUE; 618 } 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 const 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 (void)res; // Don't complain about unused variable in the LOG_NDEBUG case 692 693 jclass clazz; 694 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); 695 696 GET_METHOD_ID( 697 gTvInputHalClassInfo.deviceAvailable, clazz, 698 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); 699 GET_METHOD_ID( 700 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); 701 GET_METHOD_ID( 702 gTvInputHalClassInfo.streamConfigsChanged, clazz, 703 "streamConfigsChangedFromNative", "(I)V"); 704 GET_METHOD_ID( 705 gTvInputHalClassInfo.firstFrameCaptured, clazz, 706 "firstFrameCapturedFromNative", "(II)V"); 707 708 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig"); 709 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz)); 710 711 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder"); 712 gTvStreamConfigBuilderClassInfo.clazz = 713 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz)); 714 715 GET_METHOD_ID( 716 gTvStreamConfigBuilderClassInfo.constructor, 717 gTvStreamConfigBuilderClassInfo.clazz, 718 "<init>", "()V"); 719 GET_METHOD_ID( 720 gTvStreamConfigBuilderClassInfo.streamId, 721 gTvStreamConfigBuilderClassInfo.clazz, 722 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 723 GET_METHOD_ID( 724 gTvStreamConfigBuilderClassInfo.type, 725 gTvStreamConfigBuilderClassInfo.clazz, 726 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 727 GET_METHOD_ID( 728 gTvStreamConfigBuilderClassInfo.maxWidth, 729 gTvStreamConfigBuilderClassInfo.clazz, 730 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 731 GET_METHOD_ID( 732 gTvStreamConfigBuilderClassInfo.maxHeight, 733 gTvStreamConfigBuilderClassInfo.clazz, 734 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 735 GET_METHOD_ID( 736 gTvStreamConfigBuilderClassInfo.generation, 737 gTvStreamConfigBuilderClassInfo.clazz, 738 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 739 GET_METHOD_ID( 740 gTvStreamConfigBuilderClassInfo.build, 741 gTvStreamConfigBuilderClassInfo.clazz, 742 "build", "()Landroid/media/tv/TvStreamConfig;"); 743 744 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz, 745 "android/media/tv/TvInputHardwareInfo$Builder"); 746 gTvInputHardwareInfoBuilderClassInfo.clazz = 747 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz)); 748 749 GET_METHOD_ID( 750 gTvInputHardwareInfoBuilderClassInfo.constructor, 751 gTvInputHardwareInfoBuilderClassInfo.clazz, 752 "<init>", "()V"); 753 GET_METHOD_ID( 754 gTvInputHardwareInfoBuilderClassInfo.deviceId, 755 gTvInputHardwareInfoBuilderClassInfo.clazz, 756 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 757 GET_METHOD_ID( 758 gTvInputHardwareInfoBuilderClassInfo.type, 759 gTvInputHardwareInfoBuilderClassInfo.clazz, 760 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 761 GET_METHOD_ID( 762 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, 763 gTvInputHardwareInfoBuilderClassInfo.clazz, 764 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 765 GET_METHOD_ID( 766 gTvInputHardwareInfoBuilderClassInfo.audioType, 767 gTvInputHardwareInfoBuilderClassInfo.clazz, 768 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 769 GET_METHOD_ID( 770 gTvInputHardwareInfoBuilderClassInfo.audioAddress, 771 gTvInputHardwareInfoBuilderClassInfo.clazz, 772 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 773 GET_METHOD_ID( 774 gTvInputHardwareInfoBuilderClassInfo.build, 775 gTvInputHardwareInfoBuilderClassInfo.clazz, 776 "build", "()Landroid/media/tv/TvInputHardwareInfo;"); 777 778 return 0; 779 } 780 781 } /* namespace android */ 782