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