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