com_android_server_tv_TvInputHal.cpp revision 1f589759969f170fe58303d495b1a3e096c91780
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    void 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
135void JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) {
136    Connection& connection = mConnections.editValueFor(deviceId);
137    if (connection.mSurface == surface) {
138        // Nothing to do
139        return;
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;
155            }
156            connection.mSourceHandle.clear();
157        }
158        return;
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;
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;
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;
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;
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}
200
201const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
202    const tv_stream_config_t* configs = NULL;
203    if (mDevice->get_stream_configurations(
204            mDevice, deviceId, numConfigs, &configs) != 0) {
205        ALOGE("Couldn't get stream configs");
206        return NULL;
207    }
208    return configs;
209}
210
211
212// static
213void JTvInputHal::notify(
214        tv_input_device_t* dev, tv_input_event_t* event, void* data) {
215    JTvInputHal* thiz = (JTvInputHal*)data;
216    switch (event->type) {
217        case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
218            thiz->onDeviceAvailable(event->device_info);
219        } break;
220        case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
221            thiz->onDeviceUnavailable(event->device_info.device_id);
222        } break;
223        case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
224            thiz->onStreamConfigurationsChanged(event->device_info.device_id);
225        } break;
226        default:
227            ALOGE("Unrecognizable event");
228    }
229}
230
231void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
232    JNIEnv* env = AndroidRuntime::getJNIEnv();
233    mConnections.add(info.device_id, Connection());
234    env->CallVoidMethod(
235            mThiz,
236            gTvInputHalClassInfo.deviceAvailable,
237            info.device_id,
238            info.type);
239}
240
241void JTvInputHal::onDeviceUnavailable(int deviceId) {
242    JNIEnv* env = AndroidRuntime::getJNIEnv();
243    mConnections.removeItem(deviceId);
244    env->CallVoidMethod(
245            mThiz,
246            gTvInputHalClassInfo.deviceUnavailable,
247            deviceId);
248}
249
250void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
251    JNIEnv* env = AndroidRuntime::getJNIEnv();
252    mConnections.removeItem(deviceId);
253    env->CallVoidMethod(
254            mThiz,
255            gTvInputHalClassInfo.streamConfigsChanged,
256            deviceId);
257}
258
259////////////////////////////////////////////////////////////////////////////////
260
261static jlong nativeOpen(JNIEnv* env, jobject thiz) {
262    return (jlong)JTvInputHal::createInstance(env, thiz);
263}
264
265static void nativeSetSurface(JNIEnv* env, jclass clazz,
266        jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
267    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
268    sp<Surface> surface(
269            jsurface
270            ? android_view_Surface_getSurface(env, jsurface)
271            : NULL);
272    tvInputHal->setSurface(deviceId, streamId, surface);
273}
274
275static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
276        jlong ptr, jint deviceId, jint generation) {
277    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
278    int numConfigs = 0;
279    const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
280
281    jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
282    for (int i = 0; i < numConfigs; ++i) {
283        jobject builder = env->NewObject(
284                gTvStreamConfigBuilderClassInfo.clazz,
285                gTvStreamConfigBuilderClassInfo.constructor);
286        env->CallObjectMethod(
287                builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
288        env->CallObjectMethod(
289                builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
290        env->CallObjectMethod(
291                builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
292        env->CallObjectMethod(
293                builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
294        env->CallObjectMethod(
295                builder, gTvStreamConfigBuilderClassInfo.generation, generation);
296
297        jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
298
299        env->SetObjectArrayElement(result, i, config);
300
301        env->DeleteLocalRef(config);
302        env->DeleteLocalRef(builder);
303    }
304    return result;
305}
306
307static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
308    JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
309    delete tvInputHal;
310}
311
312static JNINativeMethod gTvInputHalMethods[] = {
313    /* name, signature, funcPtr */
314    { "nativeOpen", "()J",
315            (void*) nativeOpen },
316    { "nativeSetSurface", "(JIILandroid/view/Surface;)V",
317            (void*) nativeSetSurface },
318    { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
319            (void*) nativeSetSurface },
320    { "nativeClose", "(J)V",
321            (void*) nativeClose },
322};
323
324#define FIND_CLASS(var, className) \
325        var = env->FindClass(className); \
326        LOG_FATAL_IF(! var, "Unable to find class " className)
327
328#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
329        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
330        LOG_FATAL_IF(! var, "Unable to find method" methodName)
331
332int register_android_server_tv_TvInputHal(JNIEnv* env) {
333    int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
334            gTvInputHalMethods, NELEM(gTvInputHalMethods));
335    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
336
337    jclass clazz;
338    FIND_CLASS(clazz, "android/tv/TvInputHal");
339
340    GET_METHOD_ID(
341            gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V");
342    GET_METHOD_ID(
343            gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
344    GET_METHOD_ID(
345            gTvInputHalClassInfo.streamConfigsChanged, clazz,
346            "streamConfigsChangedFromNative", "(I)V");
347
348    FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
349    gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
350
351    FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig/Builder");
352    gTvStreamConfigBuilderClassInfo.clazz =
353            jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
354
355    GET_METHOD_ID(
356            gTvStreamConfigBuilderClassInfo.constructor,
357            gTvStreamConfigBuilderClassInfo.clazz,
358            "<init>", "()V");
359    GET_METHOD_ID(
360            gTvStreamConfigBuilderClassInfo.streamId,
361            gTvStreamConfigBuilderClassInfo.clazz,
362            "streamId", "(I)Landroid/tv/TvStreamConfig/Builder;");
363    GET_METHOD_ID(
364            gTvStreamConfigBuilderClassInfo.type,
365            gTvStreamConfigBuilderClassInfo.clazz,
366            "type", "(I)Landroid/tv/TvStreamConfig/Builder;");
367    GET_METHOD_ID(
368            gTvStreamConfigBuilderClassInfo.maxWidth,
369            gTvStreamConfigBuilderClassInfo.clazz,
370            "maxWidth", "(I)Landroid/tv/TvStreamConfig/Builder;");
371    GET_METHOD_ID(
372            gTvStreamConfigBuilderClassInfo.maxHeight,
373            gTvStreamConfigBuilderClassInfo.clazz,
374            "maxHeight", "(I)Landroid/tv/TvStreamConfig/Builder;");
375    GET_METHOD_ID(
376            gTvStreamConfigBuilderClassInfo.generation,
377            gTvStreamConfigBuilderClassInfo.clazz,
378            "generation", "(I)Landroid/tv/TvStreamConfig/Builder;");
379    GET_METHOD_ID(
380            gTvStreamConfigBuilderClassInfo.build,
381            gTvStreamConfigBuilderClassInfo.clazz,
382            "build", "()Landroid/tv/TvStreamConfig;");
383
384    return 0;
385}
386
387} /* namespace android */
388