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