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