TvInputHardwareManager.java revision e7ae0ce53b6e1ddee3e456d2a69eebcd5a196b1f
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.media.tv.ITvInputHardware; 21import android.media.tv.ITvInputHardwareCallback; 22import android.media.tv.TvInputHardwareInfo; 23import android.media.tv.TvStreamConfig; 24import android.os.IBinder; 25import android.os.RemoteException; 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 private boolean checkUidChangedLocked( 120 Connection connection, int callingUid, int resolvedUserId) { 121 Integer connectionCallingUid = connection.getCallingUidLocked(); 122 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked(); 123 if (connectionCallingUid == null || connectionResolvedUserId == null) { 124 return true; 125 } 126 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) { 127 return true; 128 } 129 return false; 130 } 131 132 /** 133 * Create a TvInputHardware object with a specific deviceId. One service at a time can access 134 * the object, and if more than one process attempts to create hardware with the same deviceId, 135 * the latest service will get the object and all the other hardware are released. The 136 * release is notified via ITvInputHardwareCallback.onReleased(). 137 */ 138 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback, 139 int callingUid, int resolvedUserId) { 140 if (callback == null) { 141 throw new NullPointerException(); 142 } 143 synchronized (mLock) { 144 Connection connection = mConnections.get(deviceId); 145 if (connection == null) { 146 Slog.e(TAG, "Invalid deviceId : " + deviceId); 147 return null; 148 } 149 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) { 150 TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked()); 151 try { 152 callback.asBinder().linkToDeath(connection, 0); 153 } catch (RemoteException e) { 154 hardware.release(); 155 return null; 156 } 157 connection.resetLocked(hardware, callback, callingUid, resolvedUserId); 158 } 159 return connection.getHardwareLocked(); 160 } 161 } 162 163 /** 164 * Release the specified hardware. 165 */ 166 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid, 167 int resolvedUserId) { 168 synchronized (mLock) { 169 Connection connection = mConnections.get(deviceId); 170 if (connection == null) { 171 Slog.e(TAG, "Invalid deviceId : " + deviceId); 172 return; 173 } 174 if (connection.getHardwareLocked() != hardware 175 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) { 176 return; 177 } 178 connection.resetLocked(null, null, null, null); 179 } 180 } 181 182 private class Connection implements IBinder.DeathRecipient { 183 private final TvInputHardwareInfo mInfo; 184 private TvInputHardwareImpl mHardware = null; 185 private ITvInputHardwareCallback mCallback; 186 private TvStreamConfig[] mConfigs = null; 187 private Integer mCallingUid = null; 188 private Integer mResolvedUserId = null; 189 190 public Connection(TvInputHardwareInfo info) { 191 mInfo = info; 192 } 193 194 // *Locked methods assume TvInputHardwareManager.mLock is held. 195 196 public void resetLocked(TvInputHardwareImpl hardware, 197 ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) { 198 if (mHardware != null) { 199 try { 200 mCallback.onReleased(); 201 } catch (RemoteException e) { 202 Slog.e(TAG, "Connection::resetHardware: " + e); 203 } 204 mHardware.release(); 205 } 206 mHardware = hardware; 207 mCallback = callback; 208 mCallingUid = callingUid; 209 mResolvedUserId = resolvedUserId; 210 211 if (mHardware != null && mCallback != null) { 212 try { 213 mCallback.onStreamConfigChanged(getConfigsLocked()); 214 } catch (RemoteException e) { 215 Slog.e(TAG, "Connection::resetHardware: " + e); 216 } 217 } 218 } 219 220 public void updateConfigsLocked(TvStreamConfig[] configs) { 221 mConfigs = configs; 222 } 223 224 public TvInputHardwareInfo getInfoLocked() { 225 return mInfo; 226 } 227 228 public ITvInputHardware getHardwareLocked() { 229 return mHardware; 230 } 231 232 public ITvInputHardwareCallback getCallbackLocked() { 233 return mCallback; 234 } 235 236 public TvStreamConfig[] getConfigsLocked() { 237 return mConfigs; 238 } 239 240 public Integer getCallingUidLocked() { 241 return mCallingUid; 242 } 243 244 public Integer getResolvedUserIdLocked() { 245 return mResolvedUserId; 246 } 247 248 @Override 249 public void binderDied() { 250 synchronized (mLock) { 251 resetLocked(null, null, null, null); 252 } 253 } 254 } 255 256 private class TvInputHardwareImpl extends ITvInputHardware.Stub { 257 private final TvInputHardwareInfo mInfo; 258 private boolean mReleased = false; 259 private final Object mImplLock = new Object(); 260 261 public TvInputHardwareImpl(TvInputHardwareInfo info) { 262 mInfo = info; 263 } 264 265 public void release() { 266 synchronized (mImplLock) { 267 mReleased = true; 268 } 269 } 270 271 @Override 272 public boolean setSurface(Surface surface, TvStreamConfig config) 273 throws RemoteException { 274 synchronized (mImplLock) { 275 if (mReleased) { 276 throw new IllegalStateException("Device already released."); 277 } 278 if (mInfo.getType() == TvInputHal.TYPE_HDMI) { 279 if (surface != null) { 280 // Set "Active Source" for HDMI. 281 // TODO(hdmi): mHdmiClient.deviceSelect(...); 282 mActiveHdmiSources.add(mInfo.getDeviceId()); 283 } else { 284 mActiveHdmiSources.remove(mInfo.getDeviceId()); 285 if (mActiveHdmiSources.size() == 0) { 286 // Tell HDMI that no HDMI source is active 287 // TODO(hdmi): mHdmiClient.portSelect(null); 288 } 289 } 290 } 291 return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS; 292 } 293 } 294 295 @Override 296 public void setVolume(float volume) throws RemoteException { 297 synchronized (mImplLock) { 298 if (mReleased) { 299 throw new IllegalStateException("Device already released."); 300 } 301 } 302 // TODO 303 } 304 305 @Override 306 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException { 307 synchronized (mImplLock) { 308 if (mReleased) { 309 throw new IllegalStateException("Device already released."); 310 } 311 } 312 if (mInfo.getType() != TvInputHal.TYPE_HDMI) { 313 return false; 314 } 315 // TODO(hdmi): mHdmiClient.sendKeyEvent(event); 316 return false; 317 } 318 } 319} 320