1c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/*
2c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Copyright 2014 The Android Open Source Project
3c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
4c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Licensed under the Apache License, Version 2.0 (the "License");
5c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * you may not use this file except in compliance with the License.
6c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * You may obtain a copy of the License at
7c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
8c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *      http://www.apache.org/licenses/LICENSE-2.0
9c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim *
10c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Unless required by applicable law or agreed to in writing, software
11c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * distributed under the License is distributed on an "AS IS" BASIS,
12c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * See the License for the specific language governing permissions and
14c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * limitations under the License.
15c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
16c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
17c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimpackage com.android.server.tv;
18c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
19d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvInputHardwareInfo;
20d5cc4a281e7ce29d1e8687ff3394b57a3a549260Jae Seoimport android.media.tv.TvStreamConfig;
21c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kimimport android.os.Handler;
229e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kimimport android.os.Message;
2357b37f610d33989f1b23e1b8d9e61fb177456364Wonsik Kimimport android.os.MessageQueue;
249e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kimimport android.util.Slog;
2521aa3467cd14260418cc47334b656adf841a567cWonsik Kimimport android.util.SparseArray;
2621aa3467cd14260418cc47334b656adf841a567cWonsik Kimimport android.util.SparseIntArray;
275b1caaf7d8408bf0ce78d8d7a36f4649dda17797Jae Seoimport android.view.Surface;
289e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
299e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kimimport java.util.LinkedList;
309e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kimimport java.util.Queue;
31c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
32c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim/**
33c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim * Provides access to the low-level TV input hardware abstraction layer.
34c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim */
359e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kimfinal class TvInputHal implements Handler.Callback {
36ee2ec05ed7c0d3cb9115f4ddd7c3613269c4a57bJae Seo    private final static boolean DEBUG = false;
379e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    private final static String TAG = TvInputHal.class.getSimpleName();
389e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
39c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public final static int SUCCESS = 0;
40c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public final static int ERROR_NO_INIT = -1;
41c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public final static int ERROR_STALE_CONFIG = -2;
42c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public final static int ERROR_UNKNOWN = -3;
43c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
449e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    public static final int EVENT_DEVICE_AVAILABLE = 1;
459e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    public static final int EVENT_DEVICE_UNAVAILABLE = 2;
469e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
47c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
489e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
49c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public interface Callback {
506e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs);
516e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onDeviceUnavailable(int deviceId);
526e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
536e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo        void onFirstFrameCaptured(int deviceId, int streamId);
54c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
55c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
5657b37f610d33989f1b23e1b8d9e61fb177456364Wonsik Kim    private native long nativeOpen(MessageQueue queue);
57c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
588f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim    private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId,
59c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            Surface surface);
60839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim    private static native int nativeRemoveStream(long ptr, int deviceId, int streamId);
61c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
62c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            int generation);
63c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private static native void nativeClose(long ptr);
64c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
655b1caaf7d8408bf0ce78d8d7a36f4649dda17797Jae Seo    private final Object mLock = new Object();
66610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim    private long mPtr = 0;
67c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Callback mCallback;
68c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    private final Handler mHandler;
695b1caaf7d8408bf0ce78d8d7a36f4649dda17797Jae Seo    private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
705b1caaf7d8408bf0ce78d8d7a36f4649dda17797Jae Seo    private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
71c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
72c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    public TvInputHal(Callback callback) {
73c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        mCallback = callback;
74610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        mHandler = new Handler(this);
75c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
76c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
7721aa3467cd14260418cc47334b656adf841a567cWonsik Kim    public void init() {
7821aa3467cd14260418cc47334b656adf841a567cWonsik Kim        synchronized (mLock) {
7957b37f610d33989f1b23e1b8d9e61fb177456364Wonsik Kim            mPtr = nativeOpen(mHandler.getLooper().getQueue());
8021aa3467cd14260418cc47334b656adf841a567cWonsik Kim        }
81c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
82c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
838f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim    public int addOrUpdateStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
8421aa3467cd14260418cc47334b656adf841a567cWonsik Kim        synchronized (mLock) {
8521aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (mPtr == 0) {
8621aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_NO_INIT;
8721aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
8821aa3467cd14260418cc47334b656adf841a567cWonsik Kim            int generation = mStreamConfigGenerations.get(deviceId, 0);
8921aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (generation != streamConfig.getGeneration()) {
9021aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_STALE_CONFIG;
9121aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
928f24a8b60f9afc1aedb89e7ee80ce65515439600Wonsik Kim            if (nativeAddOrUpdateStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
9321aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return SUCCESS;
9421aa3467cd14260418cc47334b656adf841a567cWonsik Kim            } else {
9521aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_UNKNOWN;
9621aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
97839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim        }
98839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim    }
99839ae5f460caadf8580b7e0ab77e255d7a1ddae5Wonsik Kim
10021aa3467cd14260418cc47334b656adf841a567cWonsik Kim    public int removeStream(int deviceId, TvStreamConfig streamConfig) {
10121aa3467cd14260418cc47334b656adf841a567cWonsik Kim        synchronized (mLock) {
10221aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (mPtr == 0) {
10321aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_NO_INIT;
10421aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
10521aa3467cd14260418cc47334b656adf841a567cWonsik Kim            int generation = mStreamConfigGenerations.get(deviceId, 0);
10621aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (generation != streamConfig.getGeneration()) {
10721aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_STALE_CONFIG;
10821aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
10921aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
11021aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return SUCCESS;
11121aa3467cd14260418cc47334b656adf841a567cWonsik Kim            } else {
11221aa3467cd14260418cc47334b656adf841a567cWonsik Kim                return ERROR_UNKNOWN;
11321aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
114c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
115c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
116c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
11721aa3467cd14260418cc47334b656adf841a567cWonsik Kim    public void close() {
11821aa3467cd14260418cc47334b656adf841a567cWonsik Kim        synchronized (mLock) {
11921aa3467cd14260418cc47334b656adf841a567cWonsik Kim            if (mPtr != 0l) {
12021aa3467cd14260418cc47334b656adf841a567cWonsik Kim                nativeClose(mPtr);
12121aa3467cd14260418cc47334b656adf841a567cWonsik Kim            }
122c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim        }
123c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
124c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
12521aa3467cd14260418cc47334b656adf841a567cWonsik Kim    private void retrieveStreamConfigsLocked(int deviceId) {
12621aa3467cd14260418cc47334b656adf841a567cWonsik Kim        int generation = mStreamConfigGenerations.get(deviceId, 0) + 1;
12721aa3467cd14260418cc47334b656adf841a567cWonsik Kim        mStreamConfigs.put(deviceId, nativeGetStreamConfigs(mPtr, deviceId, generation));
12821aa3467cd14260418cc47334b656adf841a567cWonsik Kim        mStreamConfigGenerations.put(deviceId, generation);
129c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
130c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
131c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    // Called from native
1329e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
133610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        if (DEBUG) {
134610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim            Slog.d(TAG, "deviceAvailableFromNative: info = " + info);
135610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        }
136610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info).sendToTarget();
1379e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    }
1389e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
1399e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    private void deviceUnavailableFromNative(int deviceId) {
140610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget();
1419e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    }
1429e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
1439e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    private void streamConfigsChangedFromNative(int deviceId) {
144610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
1459e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    }
1469e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
147c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    private void firstFrameCapturedFromNative(int deviceId, int streamId) {
148c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo        mHandler.sendMessage(
149c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
150c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo    }
151c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
1529e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    // Handler.Callback implementation
1539e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
1546e4cbfd2e5ffb739269e5e4affc2b6894bc4090eJae Seo    private final Queue<Message> mPendingMessageQueue = new LinkedList<>();
1559e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
1569e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    @Override
1579e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim    public boolean handleMessage(Message msg) {
1589e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim        switch (msg.what) {
1599e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim            case EVENT_DEVICE_AVAILABLE: {
16021aa3467cd14260418cc47334b656adf841a567cWonsik Kim                TvStreamConfig[] configs;
1619e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
16221aa3467cd14260418cc47334b656adf841a567cWonsik Kim                synchronized (mLock) {
16321aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    retrieveStreamConfigsLocked(info.getDeviceId());
16421aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    if (DEBUG) {
16521aa3467cd14260418cc47334b656adf841a567cWonsik Kim                        Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
16621aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    }
16721aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    configs = mStreamConfigs.get(info.getDeviceId());
168610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                }
16921aa3467cd14260418cc47334b656adf841a567cWonsik Kim                mCallback.onDeviceAvailable(info, configs);
1709e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                break;
171c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
172c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1739e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim            case EVENT_DEVICE_UNAVAILABLE: {
1749e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                int deviceId = msg.arg1;
175610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                if (DEBUG) {
176610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                    Slog.d(TAG, "EVENT_DEVICE_UNAVAILABLE: deviceId = " + deviceId);
177610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                }
178d7c29189aa639bfac1e6efcd222e65c2c8ecf3f1Wonsik Kim                mCallback.onDeviceUnavailable(deviceId);
1799e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                break;
180c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
181c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim
1829e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim            case EVENT_STREAM_CONFIGURATION_CHANGED: {
18321aa3467cd14260418cc47334b656adf841a567cWonsik Kim                TvStreamConfig[] configs;
1849e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                int deviceId = msg.arg1;
18521aa3467cd14260418cc47334b656adf841a567cWonsik Kim                synchronized (mLock) {
18621aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    if (DEBUG) {
18721aa3467cd14260418cc47334b656adf841a567cWonsik Kim                        Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
18821aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    }
18921aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    retrieveStreamConfigsLocked(deviceId);
19021aa3467cd14260418cc47334b656adf841a567cWonsik Kim                    configs = mStreamConfigs.get(deviceId);
191610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                }
19221aa3467cd14260418cc47334b656adf841a567cWonsik Kim                mCallback.onStreamConfigurationChanged(deviceId, configs);
1939e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                break;
194c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim            }
1959e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim
196c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            case EVENT_FIRST_FRAME_CAPTURED: {
197c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int deviceId = msg.arg1;
198c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                int streamId = msg.arg2;
199c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                mCallback.onFirstFrameCaptured(deviceId, streamId);
200c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo                break;
201c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo            }
202c086a3df3b28996cd10ebe42c5f59035d054aa0dTerry Heo
2039e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim            default:
2049e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim                Slog.e(TAG, "Unknown event: " + msg);
205610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim                return false;
2069e922ca97097cb1aa67ff53219d874ea2503a80dWonsik Kim        }
207610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim
208610ccd9117fc1611fcc576d1cb1f717f1ef3fcbfWonsik Kim        return true;
209c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim    }
210c22dbb69194c8e8fe2a32326d1f37a738cad0904Wonsik Kim}
211