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