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