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