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