TvInputHal.java revision 839ae5f460caadf8580b7e0ab77e255d7a1ddae5
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 nativeAddStream(long ptr, int deviceId, int streamId,
61            Surface surface);
62    private static native int nativeRemoveStream(long ptr, int deviceId, int streamId);
63    private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
64            int generation);
65    private static native void nativeClose(long ptr);
66
67    private volatile long mPtr = 0;
68    private final Callback mCallback;
69    private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
70    private final Handler mHandler;
71    private int mStreamConfigGeneration = 0;
72    private TvStreamConfig[] mStreamConfigs;
73
74    public TvInputHal(Callback callback) {
75        mCallback = callback;
76        mThread.start();
77        mHandler = new Handler(mThread.getLooper(), this);
78    }
79
80    public void init() {
81        mPtr = nativeOpen();
82        mHandler.sendEmptyMessage(EVENT_OPEN);
83    }
84
85    public int addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
86        long ptr = mPtr;
87        if (ptr == 0) {
88            return ERROR_NO_INIT;
89        }
90        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
91            return ERROR_STALE_CONFIG;
92        }
93        if (nativeAddStream(ptr, deviceId, streamConfig.getStreamId(), surface) == 0) {
94            return SUCCESS;
95        } else {
96            return ERROR_UNKNOWN;
97        }
98    }
99
100    public int removeStream(int deviceId, TvStreamConfig streamConfig) {
101        long ptr = mPtr;
102        if (ptr == 0) {
103            return ERROR_NO_INIT;
104        }
105        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
106            return ERROR_STALE_CONFIG;
107        }
108        if (nativeRemoveStream(ptr, deviceId, streamConfig.getStreamId()) == 0) {
109            return SUCCESS;
110        } else {
111            return ERROR_UNKNOWN;
112        }
113    }
114
115    public void close() {
116        long ptr = mPtr;
117        if (ptr != 0l) {
118            nativeClose(ptr);
119            mThread.quitSafely();
120        }
121    }
122
123    private synchronized void retrieveStreamConfigs(long ptr, int deviceId) {
124        ++mStreamConfigGeneration;
125        mStreamConfigs = nativeGetStreamConfigs(ptr, deviceId, mStreamConfigGeneration);
126    }
127
128    // Called from native
129    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
130        mHandler.sendMessage(
131                mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info));
132    }
133
134    private void deviceUnavailableFromNative(int deviceId) {
135        mHandler.sendMessage(
136                mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0));
137    }
138
139    private void streamConfigsChangedFromNative(int deviceId) {
140        mHandler.sendMessage(
141                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0));
142    }
143
144    // Handler.Callback implementation
145
146    private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
147
148    @Override
149    public boolean handleMessage(Message msg) {
150        long ptr = mPtr;
151        if (ptr == 0) {
152            mPendingMessageQueue.add(msg);
153            return true;
154        }
155        while (!mPendingMessageQueue.isEmpty()) {
156            handleMessageInternal(ptr, mPendingMessageQueue.remove());
157        }
158        handleMessageInternal(ptr, msg);
159        return true;
160    }
161
162    private void handleMessageInternal(long ptr, Message msg) {
163        switch (msg.what) {
164            case EVENT_OPEN:
165                // No-op
166                break;
167
168            case EVENT_DEVICE_AVAILABLE: {
169                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
170                retrieveStreamConfigs(ptr, info.getDeviceId());
171                mCallback.onDeviceAvailable(info, mStreamConfigs);
172                break;
173            }
174
175            case EVENT_DEVICE_UNAVAILABLE: {
176                int deviceId = msg.arg1;
177                mCallback.onDeviceUnavailable(deviceId);
178                break;
179            }
180
181            case EVENT_STREAM_CONFIGURATION_CHANGED: {
182                int deviceId = msg.arg1;
183                retrieveStreamConfigs(ptr, deviceId);
184                mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs);
185                break;
186            }
187
188            default:
189                Slog.e(TAG, "Unknown event: " + msg);
190                break;
191        }
192    }
193}
194