TvInputHal.java revision 9e922ca97097cb1aa67ff53219d874ea2503a80d
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
17package com.android.server.tv;
18
19import android.media.tv.TvInputHardwareInfo;
20import android.media.tv.TvStreamConfig;
21import android.os.Handler;
22import android.os.HandlerThread;
23import android.os.Message;
24import android.view.Surface;
25import android.util.Slog;
26
27import java.util.LinkedList;
28import java.util.Queue;
29
30/**
31 * Provides access to the low-level TV input hardware abstraction layer.
32 */
33final class TvInputHal implements Handler.Callback {
34    private final static String TAG = TvInputHal.class.getSimpleName();
35
36    public final static int SUCCESS = 0;
37    public final static int ERROR_NO_INIT = -1;
38    public final static int ERROR_STALE_CONFIG = -2;
39    public final static int ERROR_UNKNOWN = -3;
40
41    public static final int TYPE_HDMI = 1;
42    public static final int TYPE_BUILT_IN_TUNER = 2;
43    public static final int TYPE_PASSTHROUGH = 3;
44
45    public static final int EVENT_OPEN = 0;
46    // Below should be in sync with hardware/libhardware/include/hardware/tv_input.h
47    public static final int EVENT_DEVICE_AVAILABLE = 1;
48    public static final int EVENT_DEVICE_UNAVAILABLE = 2;
49    public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
50
51    public interface Callback {
52        public void onDeviceAvailable(
53                TvInputHardwareInfo info, TvStreamConfig[] configs);
54        public void onDeviceUnavailable(int deviceId);
55        public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
56    }
57
58    private native long nativeOpen();
59
60    private static native int nativeSetSurface(long ptr, int deviceId, int streamId,
61            Surface surface);
62    private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
63            int generation);
64    private static native void nativeClose(long ptr);
65
66    private volatile long mPtr = 0;
67    private final Callback mCallback;
68    private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
69    private final Handler mHandler;
70    private int mStreamConfigGeneration = 0;
71    private TvStreamConfig[] mStreamConfigs;
72
73    public TvInputHal(Callback callback) {
74        mCallback = callback;
75        mThread.start();
76        mHandler = new Handler(mThread.getLooper(), this);
77    }
78
79    public void init() {
80        mPtr = nativeOpen();
81        mHandler.sendEmptyMessage(EVENT_OPEN);
82    }
83
84    public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
85        long ptr = mPtr;
86        if (ptr == 0) {
87            return ERROR_NO_INIT;
88        }
89        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
90            return ERROR_STALE_CONFIG;
91        }
92        if (nativeSetSurface(ptr, deviceId, streamConfig.getStreamId(), surface) == 0) {
93            return SUCCESS;
94        } else {
95            return ERROR_UNKNOWN;
96        }
97    }
98
99    public void close() {
100        long ptr = mPtr;
101        if (ptr != 0l) {
102            nativeClose(ptr);
103            mThread.quitSafely();
104        }
105    }
106
107    private synchronized void retrieveStreamConfigs(long ptr, int deviceId) {
108        ++mStreamConfigGeneration;
109        mStreamConfigs = nativeGetStreamConfigs(ptr, deviceId, mStreamConfigGeneration);
110    }
111
112    // Called from native
113    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
114        mHandler.sendMessage(
115                mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info));
116    }
117
118    private void deviceUnavailableFromNative(int deviceId) {
119        mHandler.sendMessage(
120                mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0));
121    }
122
123    private void streamConfigsChangedFromNative(int deviceId) {
124        mHandler.sendMessage(
125                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0));
126    }
127
128    // Handler.Callback implementation
129
130    private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
131
132    @Override
133    public boolean handleMessage(Message msg) {
134        long ptr = mPtr;
135        if (ptr == 0) {
136            mPendingMessageQueue.add(msg);
137            return true;
138        }
139        while (!mPendingMessageQueue.isEmpty()) {
140            handleMessageInternal(ptr, mPendingMessageQueue.remove());
141        }
142        handleMessageInternal(ptr, msg);
143        return true;
144    }
145
146    private void handleMessageInternal(long ptr, Message msg) {
147        switch (msg.what) {
148            case EVENT_OPEN:
149                // No-op
150                break;
151
152            case EVENT_DEVICE_AVAILABLE: {
153                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
154                retrieveStreamConfigs(ptr, info.getDeviceId());
155                mCallback.onDeviceAvailable(info, mStreamConfigs);
156                break;
157            }
158
159            case EVENT_DEVICE_UNAVAILABLE: {
160                int deviceId = msg.arg1;
161                mCallback.onDeviceUnavailable(deviceId);
162                break;
163            }
164
165            case EVENT_STREAM_CONFIGURATION_CHANGED: {
166                int deviceId = msg.arg1;
167                retrieveStreamConfigs(ptr, deviceId);
168                mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs);
169                break;
170            }
171
172            default:
173                Slog.e(TAG, "Unknown event: " + msg);
174                break;
175        }
176    }
177}
178