TvInputHardwareManager.java revision 1f589759969f170fe58303d495b1a3e096c91780
1/* 2 * Copyright (C) 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.content.Context; 20import android.os.IBinder; 21import android.os.RemoteException; 22import android.tv.ITvInputHardware; 23import android.tv.ITvInputHardwareCallback; 24import android.tv.TvInputHardwareInfo; 25import android.tv.TvStreamConfig; 26import android.util.Slog; 27import android.util.SparseArray; 28import android.view.KeyEvent; 29import android.view.Surface; 30 31import java.util.ArrayList; 32import java.util.HashSet; 33import java.util.List; 34import java.util.Set; 35 36/** 37 * A helper class for TvInputManagerService to handle TV input hardware. 38 * 39 * This class does a basic connection management and forwarding calls to TvInputHal which eventually 40 * calls to tv_input HAL module. 41 * 42 * @hide 43 */ 44class TvInputHardwareManager implements TvInputHal.Callback { 45 private static final String TAG = TvInputHardwareManager.class.getSimpleName(); 46 private final TvInputHal mHal = new TvInputHal(this); 47 private final SparseArray<Connection> mConnections = new SparseArray<Connection>(); 48 private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>(); 49 private final Context mContext; 50 private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>(); 51 52 private final Object mLock = new Object(); 53 54 public TvInputHardwareManager(Context context) { 55 mContext = context; 56 // TODO(hdmi): mHdmiManager = mContext.getSystemService(...); 57 // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient(); 58 mHal.init(); 59 } 60 61 @Override 62 public void onDeviceAvailable( 63 TvInputHardwareInfo info, TvStreamConfig[] configs) { 64 synchronized (mLock) { 65 Connection connection = new Connection(info); 66 connection.updateConfigsLocked(configs); 67 mConnections.put(info.getDeviceId(), connection); 68 buildInfoListLocked(); 69 // TODO: notify if necessary 70 } 71 } 72 73 private void buildInfoListLocked() { 74 mInfoList.clear(); 75 for (int i = 0; i < mConnections.size(); ++i) { 76 mInfoList.add(mConnections.valueAt(i).getInfoLocked()); 77 } 78 } 79 80 @Override 81 public void onDeviceUnavailable(int deviceId) { 82 synchronized (mLock) { 83 Connection connection = mConnections.get(deviceId); 84 if (connection == null) { 85 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId); 86 return; 87 } 88 connection.resetLocked(null, null, null, null); 89 mConnections.remove(deviceId); 90 buildInfoListLocked(); 91 // TODO: notify if necessary 92 } 93 } 94 95 @Override 96 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) { 97 synchronized (mLock) { 98 Connection connection = mConnections.get(deviceId); 99 if (connection == null) { 100 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with " 101 + deviceId); 102 return; 103 } 104 connection.updateConfigsLocked(configs); 105 try { 106 connection.getCallbackLocked().onStreamConfigChanged(configs); 107 } catch (RemoteException e) { 108 Slog.e(TAG, "onStreamConfigurationChanged: " + e); 109 } 110 } 111 } 112 113 public List<TvInputHardwareInfo> getHardwareList() { 114 synchronized (mLock) { 115 return mInfoList; 116 } 117 } 118 119 /** 120 * Create a TvInputHardware object with a specific deviceId. One service at a time can access 121 * the object, and if more than one process attempts to create hardware with the same deviceId, 122 * the latest service will get the object and all the other hardware are released. The 123 * release is notified via ITvInputHardwareCallback.onReleased(). 124 */ 125 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, 126 int callingUid, int resolvedUserId) { 127 if (callback == null) { 128 throw new NullPointerException(); 129 } 130 synchronized (mLock) { 131 Connection connection = mConnections.get(deviceId); 132 if (connection == null) { 133 Slog.e(TAG, "Invalid deviceId : " + deviceId); 134 return null; 135 } 136 if (connection.getCallingUidLocked() != callingUid 137 || connection.getResolvedUserIdLocked() != resolvedUserId) { 138 TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked()); 139 try { 140 callback.asBinder().linkToDeath(connection, 0); 141 } catch (RemoteException e) { 142 hardware.release(); 143 return null; 144 } 145 connection.resetLocked(hardware, callback, callingUid, resolvedUserId); 146 } 147 return connection.getHardwareLocked(); 148 } 149 } 150 151 /** 152 * Release the specified hardware. 153 */ 154 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, 155 int resolvedUserId) { 156 synchronized (mLock) { 157 Connection connection = mConnections.get(deviceId); 158 if (connection == null) { 159 Slog.e(TAG, "Invalid deviceId : " + deviceId); 160 return; 161 } 162 if (connection.getHardwareLocked() != hardware 163 || connection.getCallingUidLocked() != callingUid 164 || connection.getResolvedUserIdLocked() != resolvedUserId) { 165 return; 166 } 167 connection.resetLocked(null, null, null, null); 168 } 169 } 170 171 private class Connection implements IBinder.DeathRecipient { 172 private final TvInputHardwareInfo mInfo; 173 private TvInputHardwareImpl mHardware = null; 174 private ITvInputHardwareCallback mCallback; 175 private TvStreamConfig[] mConfigs = null; 176 private Integer mCallingUid = null; 177 private Integer mResolvedUserId = null; 178 179 public Connection(TvInputHardwareInfo info) { 180 mInfo = info; 181 } 182 183 // *Locked methods assume TvInputHardwareManager.mLock is held. 184 185 public void resetLocked(TvInputHardwareImpl hardware, 186 ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) { 187 if (mHardware != null) { 188 try { 189 mCallback.onReleased(); 190 } catch (RemoteException e) { 191 Slog.e(TAG, "Connection::resetHardware: " + e); 192 } 193 mHardware.release(); 194 } 195 mHardware = hardware; 196 mCallback = callback; 197 mCallingUid = callingUid; 198 mResolvedUserId = resolvedUserId; 199 200 if (mHardware != null && mCallback != null) { 201 try { 202 mCallback.onStreamConfigChanged(getConfigsLocked()); 203 } catch (RemoteException e) { 204 Slog.e(TAG, "Connection::resetHardware: " + e); 205 } 206 } 207 } 208 209 public void updateConfigsLocked(TvStreamConfig[] configs) { 210 mConfigs = configs; 211 } 212 213 public TvInputHardwareInfo getInfoLocked() { 214 return mInfo; 215 } 216 217 public ITvInputHardware getHardwareLocked() { 218 return mHardware; 219 } 220 221 public ITvInputHardwareCallback getCallbackLocked() { 222 return mCallback; 223 } 224 225 public TvStreamConfig[] getConfigsLocked() { 226 return mConfigs; 227 } 228 229 public int getCallingUidLocked() { 230 return mCallingUid; 231 } 232 233 public int getResolvedUserIdLocked() { 234 return mResolvedUserId; 235 } 236 237 @Override 238 public void binderDied() { 239 synchronized (mLock) { 240 resetLocked(null, null, null, null); 241 } 242 } 243 } 244 245 private class TvInputHardwareImpl extends ITvInputHardware.Stub { 246 private final TvInputHardwareInfo mInfo; 247 private boolean mReleased = false; 248 private final Object mImplLock = new Object(); 249 250 public TvInputHardwareImpl(TvInputHardwareInfo info) { 251 mInfo = info; 252 } 253 254 public void release() { 255 synchronized (mImplLock) { 256 mReleased = true; 257 } 258 } 259 260 @Override 261 public boolean setSurface(Surface surface, TvStreamConfig config) 262 throws RemoteException { 263 synchronized (mImplLock) { 264 if (mReleased) { 265 throw new IllegalStateException("Device already released."); 266 } 267 if (mInfo.getType() == TvInputHal.TYPE_HDMI) { 268 if (surface != null) { 269 // Set "Active Source" for HDMI. 270 // TODO(hdmi): mHdmiClient.deviceSelect(...); 271 mActiveHdmiSources.add(mInfo.getDeviceId()); 272 } else { 273 mActiveHdmiSources.remove(mInfo.getDeviceId()); 274 if (mActiveHdmiSources.size() == 0) { 275 // Tell HDMI that no HDMI source is active 276 // TODO(hdmi): mHdmiClient.portSelect(null); 277 } 278 } 279 } 280 return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; 281 } 282 } 283 284 @Override 285 public void setVolume(float volume) throws RemoteException { 286 synchronized (mImplLock) { 287 if (mReleased) { 288 throw new IllegalStateException("Device already released."); 289 } 290 } 291 // TODO 292 } 293 294 @Override 295 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException { 296 synchronized (mImplLock) { 297 if (mReleased) { 298 throw new IllegalStateException("Device already released."); 299 } 300 } 301 if (mInfo.getType() != TvInputHal.TYPE_HDMI) { 302 return false; 303 } 304 // TODO(hdmi): mHdmiClient.sendKeyEvent(event); 305 return false; 306 } 307 } 308} 309