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