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