TvInputHal.java revision 5b1caaf7d8408bf0ce78d8d7a36f4649dda17797
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.Message;
23import android.util.Slog;
24import android.util.SparseArray;
25import android.util.SparseIntArray;
26import android.view.Surface;
27
28import java.util.LinkedList;
29import java.util.Queue;
30
31/**
32 * Provides access to the low-level TV input hardware abstraction layer.
33 */
34final class TvInputHal implements Handler.Callback {
35    // STOPSHIP: Turn debugging off
36    private final static boolean DEBUG = true;
37    private final static String TAG = TvInputHal.class.getSimpleName();
38
39    public final static int SUCCESS = 0;
40    public final static int ERROR_NO_INIT = -1;
41    public final static int ERROR_STALE_CONFIG = -2;
42    public final static int ERROR_UNKNOWN = -3;
43
44    public static final int EVENT_DEVICE_AVAILABLE = 1;
45    public static final int EVENT_DEVICE_UNAVAILABLE = 2;
46    public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
47    public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
48
49    public interface Callback {
50        public void onDeviceAvailable(
51                TvInputHardwareInfo info, TvStreamConfig[] configs);
52        public void onDeviceUnavailable(int deviceId);
53        public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
54        public void onFirstFrameCaptured(int deviceId, int streamId);
55    }
56
57    private native long nativeOpen();
58
59    private static native int nativeAddStream(long ptr, int deviceId, int streamId,
60            Surface surface);
61    private static native int nativeRemoveStream(long ptr, int deviceId, int streamId);
62    private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
63            int generation);
64    private static native void nativeClose(long ptr);
65
66    private final Object mLock = new Object();
67    private long mPtr = 0;
68    private final Callback mCallback;
69    private final Handler mHandler;
70    private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
71    private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
72
73    public TvInputHal(Callback callback) {
74        mCallback = callback;
75        mHandler = new Handler(this);
76    }
77
78    public void init() {
79        synchronized (mLock) {
80            mPtr = nativeOpen();
81        }
82    }
83
84    public int addStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
85        synchronized (mLock) {
86            if (mPtr == 0) {
87                return ERROR_NO_INIT;
88            }
89            int generation = mStreamConfigGenerations.get(deviceId, 0);
90            if (generation != streamConfig.getGeneration()) {
91                return ERROR_STALE_CONFIG;
92            }
93            if (nativeAddStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
94                return SUCCESS;
95            } else {
96                return ERROR_UNKNOWN;
97            }
98        }
99    }
100
101    public int removeStream(int deviceId, TvStreamConfig streamConfig) {
102        synchronized (mLock) {
103            if (mPtr == 0) {
104                return ERROR_NO_INIT;
105            }
106            int generation = mStreamConfigGenerations.get(deviceId, 0);
107            if (generation != streamConfig.getGeneration()) {
108                return ERROR_STALE_CONFIG;
109            }
110            if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
111                return SUCCESS;
112            } else {
113                return ERROR_UNKNOWN;
114            }
115        }
116    }
117
118    public void close() {
119        synchronized (mLock) {
120            if (mPtr != 0l) {
121                nativeClose(mPtr);
122            }
123        }
124    }
125
126    private void retrieveStreamConfigsLocked(int deviceId) {
127        int generation = mStreamConfigGenerations.get(deviceId, 0) + 1;
128        mStreamConfigs.put(deviceId, nativeGetStreamConfigs(mPtr, deviceId, generation));
129        mStreamConfigGenerations.put(deviceId, generation);
130    }
131
132    // Called from native
133    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
134        if (DEBUG) {
135            Slog.d(TAG, "deviceAvailableFromNative: info = " + info);
136        }
137        mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info).sendToTarget();
138    }
139
140    private void deviceUnavailableFromNative(int deviceId) {
141        mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget();
142    }
143
144    private void streamConfigsChangedFromNative(int deviceId) {
145        mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
146    }
147
148    private void firstFrameCapturedFromNative(int deviceId, int streamId) {
149        mHandler.sendMessage(
150                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
151    }
152
153    // Handler.Callback implementation
154
155    private final Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
156
157    @Override
158    public boolean handleMessage(Message msg) {
159        switch (msg.what) {
160            case EVENT_DEVICE_AVAILABLE: {
161                TvStreamConfig[] configs;
162                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
163                synchronized (mLock) {
164                    retrieveStreamConfigsLocked(info.getDeviceId());
165                    if (DEBUG) {
166                        Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
167                    }
168                    configs = mStreamConfigs.get(info.getDeviceId());
169                }
170                mCallback.onDeviceAvailable(info, configs);
171                break;
172            }
173
174            case EVENT_DEVICE_UNAVAILABLE: {
175                int deviceId = msg.arg1;
176                if (DEBUG) {
177                    Slog.d(TAG, "EVENT_DEVICE_UNAVAILABLE: deviceId = " + deviceId);
178                }
179                mCallback.onDeviceUnavailable(deviceId);
180                break;
181            }
182
183            case EVENT_STREAM_CONFIGURATION_CHANGED: {
184                TvStreamConfig[] configs;
185                int deviceId = msg.arg1;
186                synchronized (mLock) {
187                    if (DEBUG) {
188                        Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
189                    }
190                    retrieveStreamConfigsLocked(deviceId);
191                    configs = mStreamConfigs.get(deviceId);
192                }
193                mCallback.onStreamConfigurationChanged(deviceId, configs);
194                break;
195            }
196
197            case EVENT_FIRST_FRAME_CAPTURED: {
198                int deviceId = msg.arg1;
199                int streamId = msg.arg2;
200                mCallback.onFirstFrameCaptured(deviceId, streamId);
201                break;
202            }
203
204            default:
205                Slog.e(TAG, "Unknown event: " + msg);
206                return false;
207        }
208
209        return true;
210    }
211}
212