com_android_server_tv_TvInputHal.cpp revision a358b53e1d0bb7a7fc08b0a4d2a2e256f3045859
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_runtime/AndroidRuntime.h" 22#include "android_runtime/android_view_Surface.h" 23#include "JNIHelp.h" 24#include "jni.h" 25 26#include <gui/Surface.h> 27#include <utils/Errors.h> 28#include <utils/KeyedVector.h> 29#include <utils/Log.h> 30#include <utils/NativeHandle.h> 31#include <hardware/tv_input.h> 32 33namespace android { 34 35static struct { 36 jmethodID deviceAvailable; 37 jmethodID deviceUnavailable; 38 jmethodID streamConfigsChanged; 39} gTvInputHalClassInfo; 40 41static struct { 42 jclass clazz; 43} gTvStreamConfigClassInfo; 44 45static struct { 46 jclass clazz; 47 48 jmethodID constructor; 49 jmethodID streamId; 50 jmethodID type; 51 jmethodID maxWidth; 52 jmethodID maxHeight; 53 jmethodID generation; 54 jmethodID build; 55} gTvStreamConfigBuilderClassInfo; 56 57static struct { 58 jclass clazz; 59 60 jmethodID constructor; 61 jmethodID deviceId; 62 jmethodID type; 63 jmethodID hdmiPortId; 64 jmethodID audioType; 65 jmethodID audioAddress; 66 jmethodID build; 67} gTvInputHardwareInfoBuilderClassInfo; 68 69//////////////////////////////////////////////////////////////////////////////// 70 71class JTvInputHal { 72public: 73 ~JTvInputHal(); 74 75 static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); 76 77 int setSurface(int deviceId, int streamId, const sp<Surface>& surface); 78 void getStreamConfigs(int deviceId, jobjectArray* array); 79 const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); 80 81private: 82 class Connection { 83 public: 84 Connection() : mStreamId(0) {} 85 86 sp<Surface> mSurface; 87 sp<NativeHandle> mSourceHandle; 88 int mStreamId; 89 }; 90 91 JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev); 92 93 static void notify( 94 tv_input_device_t* dev,tv_input_event_t* event, void* data); 95 96 void onDeviceAvailable(const tv_input_device_info_t& info); 97 void onDeviceUnavailable(int deviceId); 98 void onStreamConfigurationsChanged(int deviceId); 99 100 jweak mThiz; 101 tv_input_device_t* mDevice; 102 tv_input_callback_ops_t mCallback; 103 104 KeyedVector<int, Connection> mConnections; 105}; 106 107JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) { 108 mThiz = env->NewWeakGlobalRef(thiz); 109 mDevice = device; 110 mCallback.notify = &JTvInputHal::notify; 111 112 mDevice->initialize(mDevice, &mCallback, this); 113} 114 115JTvInputHal::~JTvInputHal() { 116 mDevice->common.close((hw_device_t*)mDevice); 117 118 JNIEnv* env = AndroidRuntime::getJNIEnv(); 119 env->DeleteWeakGlobalRef(mThiz); 120 mThiz = NULL; 121} 122 123JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) { 124 tv_input_module_t* module = NULL; 125 status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID, 126 (hw_module_t const**)&module); 127 if (err) { 128 ALOGE("Couldn't load %s module (%s)", 129 TV_INPUT_HARDWARE_MODULE_ID, strerror(-err)); 130 return 0; 131 } 132 133 tv_input_device_t* device = NULL; 134 err = module->common.methods->open( 135 (hw_module_t*)module, 136 TV_INPUT_DEFAULT_DEVICE, 137 (hw_device_t**)&device); 138 if (err) { 139 ALOGE("Couldn't open %s device (%s)", 140 TV_INPUT_DEFAULT_DEVICE, strerror(-err)); 141 return 0; 142 } 143 144 return new JTvInputHal(env, thiz, device); 145} 146 147int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) { 148 Connection& connection = mConnections.editValueFor(deviceId); 149 if (connection.mStreamId == streamId && connection.mSurface == surface) { 150 // Nothing to do 151 return NO_ERROR; 152 } 153 if (Surface::isValid(connection.mSurface)) { 154 connection.mSurface.clear(); 155 } 156 if (surface == NULL) { 157 if (connection.mSurface != NULL) { 158 connection.mSurface->setSidebandStream(NULL); 159 connection.mSurface.clear(); 160 } 161 if (connection.mSourceHandle != NULL) { 162 // Need to reset streams 163 if (mDevice->close_stream( 164 mDevice, deviceId, connection.mStreamId) != 0) { 165 ALOGE("Couldn't remove stream"); 166 return BAD_VALUE; 167 } 168 connection.mSourceHandle.clear(); 169 } 170 return NO_ERROR; 171 } 172 connection.mSurface = surface; 173 if (connection.mSourceHandle == NULL) { 174 // Need to configure stream 175 int numConfigs = 0; 176 const tv_stream_config_t* configs = NULL; 177 if (mDevice->get_stream_configurations( 178 mDevice, deviceId, &numConfigs, &configs) != 0) { 179 ALOGE("Couldn't get stream configs"); 180 return UNKNOWN_ERROR; 181 } 182 int configIndex = -1; 183 for (int i = 0; i < numConfigs; ++i) { 184 if (configs[i].stream_id == streamId) { 185 configIndex = i; 186 break; 187 } 188 } 189 if (configIndex == -1) { 190 ALOGE("Cannot find a config with given stream ID: %d", streamId); 191 return BAD_VALUE; 192 } 193 // TODO: handle buffer producer profile. 194 if (configs[configIndex].type != 195 TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) { 196 ALOGE("Profiles other than independent video source is not yet " 197 "supported : type = %d", configs[configIndex].type); 198 return INVALID_OPERATION; 199 } 200 tv_stream_t stream; 201 stream.stream_id = configs[configIndex].stream_id; 202 if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) { 203 ALOGE("Couldn't add stream"); 204 return UNKNOWN_ERROR; 205 } 206 connection.mSourceHandle = NativeHandle::create( 207 stream.sideband_stream_source_handle, false); 208 connection.mStreamId = stream.stream_id; 209 connection.mSurface->setSidebandStream(connection.mSourceHandle); 210 } 211 return NO_ERROR; 212} 213 214const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) { 215 const tv_stream_config_t* configs = NULL; 216 if (mDevice->get_stream_configurations( 217 mDevice, deviceId, numConfigs, &configs) != 0) { 218 ALOGE("Couldn't get stream configs"); 219 return NULL; 220 } 221 return configs; 222} 223 224// static 225void JTvInputHal::notify( 226 tv_input_device_t* dev, tv_input_event_t* event, void* data) { 227 JTvInputHal* thiz = (JTvInputHal*)data; 228 switch (event->type) { 229 case TV_INPUT_EVENT_DEVICE_AVAILABLE: { 230 thiz->onDeviceAvailable(event->device_info); 231 } break; 232 case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: { 233 thiz->onDeviceUnavailable(event->device_info.device_id); 234 } break; 235 case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: { 236 thiz->onStreamConfigurationsChanged(event->device_info.device_id); 237 } break; 238 default: 239 ALOGE("Unrecognizable event"); 240 } 241} 242 243void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) { 244 JNIEnv* env = AndroidRuntime::getJNIEnv(); 245 mConnections.add(info.device_id, Connection()); 246 247 jobject builder = env->NewObject( 248 gTvInputHardwareInfoBuilderClassInfo.clazz, 249 gTvInputHardwareInfoBuilderClassInfo.constructor); 250 env->CallObjectMethod( 251 builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id); 252 env->CallObjectMethod( 253 builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type); 254 if (info.type == TV_INPUT_TYPE_HDMI) { 255 env->CallObjectMethod( 256 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id); 257 } 258 env->CallObjectMethod( 259 builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type); 260 if (info.audio_type != AUDIO_DEVICE_NONE) { 261 jstring audioAddress = env->NewStringUTF(info.audio_address); 262 env->CallObjectMethod( 263 builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress); 264 env->DeleteLocalRef(audioAddress); 265 } 266 267 jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build); 268 269 env->CallVoidMethod( 270 mThiz, 271 gTvInputHalClassInfo.deviceAvailable, 272 infoObject); 273 274 env->DeleteLocalRef(builder); 275 env->DeleteLocalRef(infoObject); 276} 277 278void JTvInputHal::onDeviceUnavailable(int deviceId) { 279 JNIEnv* env = AndroidRuntime::getJNIEnv(); 280 mConnections.removeItem(deviceId); 281 env->CallVoidMethod( 282 mThiz, 283 gTvInputHalClassInfo.deviceUnavailable, 284 deviceId); 285} 286 287void JTvInputHal::onStreamConfigurationsChanged(int deviceId) { 288 JNIEnv* env = AndroidRuntime::getJNIEnv(); 289 mConnections.removeItem(deviceId); 290 env->CallVoidMethod( 291 mThiz, 292 gTvInputHalClassInfo.streamConfigsChanged, 293 deviceId); 294} 295 296//////////////////////////////////////////////////////////////////////////////// 297 298static jlong nativeOpen(JNIEnv* env, jobject thiz) { 299 return (jlong)JTvInputHal::createInstance(env, thiz); 300} 301 302static int nativeSetSurface(JNIEnv* env, jclass clazz, 303 jlong ptr, jint deviceId, jint streamId, jobject jsurface) { 304 JTvInputHal* tvInputHal = (JTvInputHal*)ptr; 305 sp<Surface> surface( 306 jsurface 307 ? android_view_Surface_getSurface(env, jsurface) 308 : NULL); 309 return tvInputHal->setSurface(deviceId, streamId, surface); 310} 311 312static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz, 313 jlong ptr, jint deviceId, jint generation) { 314 JTvInputHal* tvInputHal = (JTvInputHal*)ptr; 315 int numConfigs = 0; 316 const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs); 317 318 jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL); 319 for (int i = 0; i < numConfigs; ++i) { 320 jobject builder = env->NewObject( 321 gTvStreamConfigBuilderClassInfo.clazz, 322 gTvStreamConfigBuilderClassInfo.constructor); 323 env->CallObjectMethod( 324 builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id); 325 env->CallObjectMethod( 326 builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type); 327 env->CallObjectMethod( 328 builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width); 329 env->CallObjectMethod( 330 builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height); 331 env->CallObjectMethod( 332 builder, gTvStreamConfigBuilderClassInfo.generation, generation); 333 334 jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build); 335 336 env->SetObjectArrayElement(result, i, config); 337 338 env->DeleteLocalRef(config); 339 env->DeleteLocalRef(builder); 340 } 341 return result; 342} 343 344static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) { 345 JTvInputHal* tvInputHal = (JTvInputHal*)ptr; 346 delete tvInputHal; 347} 348 349static JNINativeMethod gTvInputHalMethods[] = { 350 /* name, signature, funcPtr */ 351 { "nativeOpen", "()J", 352 (void*) nativeOpen }, 353 { "nativeSetSurface", "(JIILandroid/view/Surface;)I", 354 (void*) nativeSetSurface }, 355 { "nativeGetStreamConfigs", "(JII)[Landroid/media/tv/TvStreamConfig;", 356 (void*) nativeGetStreamConfigs }, 357 { "nativeClose", "(J)V", 358 (void*) nativeClose }, 359}; 360 361#define FIND_CLASS(var, className) \ 362 var = env->FindClass(className); \ 363 LOG_FATAL_IF(! var, "Unable to find class " className) 364 365#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 366 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 367 LOG_FATAL_IF(! var, "Unable to find method" methodName) 368 369int register_android_server_tv_TvInputHal(JNIEnv* env) { 370 int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal", 371 gTvInputHalMethods, NELEM(gTvInputHalMethods)); 372 LOG_FATAL_IF(res < 0, "Unable to register native methods."); 373 374 jclass clazz; 375 FIND_CLASS(clazz, "com/android/server/tv/TvInputHal"); 376 377 GET_METHOD_ID( 378 gTvInputHalClassInfo.deviceAvailable, clazz, 379 "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V"); 380 GET_METHOD_ID( 381 gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V"); 382 GET_METHOD_ID( 383 gTvInputHalClassInfo.streamConfigsChanged, clazz, 384 "streamConfigsChangedFromNative", "(I)V"); 385 386 FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/media/tv/TvStreamConfig"); 387 gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz)); 388 389 FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/media/tv/TvStreamConfig$Builder"); 390 gTvStreamConfigBuilderClassInfo.clazz = 391 jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz)); 392 393 GET_METHOD_ID( 394 gTvStreamConfigBuilderClassInfo.constructor, 395 gTvStreamConfigBuilderClassInfo.clazz, 396 "<init>", "()V"); 397 GET_METHOD_ID( 398 gTvStreamConfigBuilderClassInfo.streamId, 399 gTvStreamConfigBuilderClassInfo.clazz, 400 "streamId", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 401 GET_METHOD_ID( 402 gTvStreamConfigBuilderClassInfo.type, 403 gTvStreamConfigBuilderClassInfo.clazz, 404 "type", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 405 GET_METHOD_ID( 406 gTvStreamConfigBuilderClassInfo.maxWidth, 407 gTvStreamConfigBuilderClassInfo.clazz, 408 "maxWidth", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 409 GET_METHOD_ID( 410 gTvStreamConfigBuilderClassInfo.maxHeight, 411 gTvStreamConfigBuilderClassInfo.clazz, 412 "maxHeight", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 413 GET_METHOD_ID( 414 gTvStreamConfigBuilderClassInfo.generation, 415 gTvStreamConfigBuilderClassInfo.clazz, 416 "generation", "(I)Landroid/media/tv/TvStreamConfig$Builder;"); 417 GET_METHOD_ID( 418 gTvStreamConfigBuilderClassInfo.build, 419 gTvStreamConfigBuilderClassInfo.clazz, 420 "build", "()Landroid/media/tv/TvStreamConfig;"); 421 422 FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz, 423 "android/media/tv/TvInputHardwareInfo$Builder"); 424 gTvInputHardwareInfoBuilderClassInfo.clazz = 425 jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz)); 426 427 GET_METHOD_ID( 428 gTvInputHardwareInfoBuilderClassInfo.constructor, 429 gTvInputHardwareInfoBuilderClassInfo.clazz, 430 "<init>", "()V"); 431 GET_METHOD_ID( 432 gTvInputHardwareInfoBuilderClassInfo.deviceId, 433 gTvInputHardwareInfoBuilderClassInfo.clazz, 434 "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 435 GET_METHOD_ID( 436 gTvInputHardwareInfoBuilderClassInfo.type, 437 gTvInputHardwareInfoBuilderClassInfo.clazz, 438 "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 439 GET_METHOD_ID( 440 gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, 441 gTvInputHardwareInfoBuilderClassInfo.clazz, 442 "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 443 GET_METHOD_ID( 444 gTvInputHardwareInfoBuilderClassInfo.audioType, 445 gTvInputHardwareInfoBuilderClassInfo.clazz, 446 "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 447 GET_METHOD_ID( 448 gTvInputHardwareInfoBuilderClassInfo.audioAddress, 449 gTvInputHardwareInfoBuilderClassInfo.clazz, 450 "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;"); 451 GET_METHOD_ID( 452 gTvInputHardwareInfoBuilderClassInfo.build, 453 gTvInputHardwareInfoBuilderClassInfo.clazz, 454 "build", "()Landroid/media/tv/TvInputHardwareInfo;"); 455 456 return 0; 457} 458 459} /* namespace android */ 460