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