blob: 9252be40c95a1e9959f81e403f5f6d94b93baf01 [file] [log] [blame]
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001/*
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
Wonsik Kim969167d2014-06-24 16:33:17 +090019import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21
Wonsik Kimc22dbb62014-05-26 02:26:04 +000022import android.content.Context;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090023import android.content.Intent;
Wonsik Kim187423c2014-06-25 14:12:48 +090024import android.hardware.hdmi.HdmiCecDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090025import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090026import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090027import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090028import android.hardware.hdmi.IHdmiHotplugEventListener;
29import android.hardware.hdmi.IHdmiInputChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090030import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090031import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090032import android.media.AudioGain;
33import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090034import android.media.AudioManager;
35import android.media.AudioPatch;
36import android.media.AudioPort;
37import android.media.AudioPortConfig;
Jae Seod5cc4a22014-05-30 16:57:43 -070038import android.media.tv.ITvInputHardware;
39import android.media.tv.ITvInputHardwareCallback;
40import android.media.tv.TvInputHardwareInfo;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090041import android.media.tv.TvContract;
Wonsik Kim969167d2014-06-24 16:33:17 +090042import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070043import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090044import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000045import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090046import android.os.Looper;
47import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000048import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090049import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090050import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000051import android.util.Slog;
52import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090053import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000054import android.view.KeyEvent;
55import android.view.Surface;
56
Wonsik Kim969167d2014-06-24 16:33:17 +090057import com.android.server.SystemService;
58
Wonsik Kimc22dbb62014-05-26 02:26:04 +000059import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090060import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090061import java.util.Collections;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000062import java.util.HashSet;
Wonsik Kimd922a542014-07-24 18:25:29 +090063import java.util.Iterator;
64import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000065import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090066import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000067import java.util.Set;
68
69/**
70 * A helper class for TvInputManagerService to handle TV input hardware.
71 *
72 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
73 * calls to tv_input HAL module.
74 *
75 * @hide
76 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090077class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000078 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090079
80 private final Context mContext;
81 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000082 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090083 private final SparseArray<Connection> mConnections = new SparseArray<>();
84 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
85 private List<HdmiCecDeviceInfo> mHdmiCecDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090086 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090087 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090088 /* A map from a HDMI logical address to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090089 private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<>();
90 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090091
Wonsik Kimd7c29182014-05-27 10:38:21 +090092 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090093 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090094 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
95 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090096 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jinsuk Kim7474fac2014-07-18 17:22:26 +090097 private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener();
Wonsik Kimd922a542014-07-24 18:25:29 +090098 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090099 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900100 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900101
Wonsik Kim187423c2014-06-25 14:12:48 +0900102 // Calls to mListener should happen here.
103 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000104
105 private final Object mLock = new Object();
106
Wonsik Kim187423c2014-06-25 14:12:48 +0900107 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000108 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900109 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900110 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000111 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900112 }
113
114 public void onBootPhase(int phase) {
115 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900116 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
117 Context.HDMI_CONTROL_SERVICE));
118 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900119 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900120 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
121 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Wonsik Kimd922a542014-07-24 18:25:29 +0900122 mHdmiCecDeviceList.addAll(mHdmiControlService.getInputDevices());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900123 mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900124 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900125 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900126 }
127 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900128 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000129 }
130
131 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900132 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000133 synchronized (mLock) {
134 Connection connection = new Connection(info);
135 connection.updateConfigsLocked(configs);
136 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900137 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900138 mHandler.obtainMessage(
139 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900140 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
141 processPendingHdmiDeviceEventsLocked();
142 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000143 }
144 }
145
Wonsik Kimd922a542014-07-24 18:25:29 +0900146 private void buildHardwareListLocked() {
147 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000148 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900149 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000150 }
151 }
152
153 @Override
154 public void onDeviceUnavailable(int deviceId) {
155 synchronized (mLock) {
156 Connection connection = mConnections.get(deviceId);
157 if (connection == null) {
158 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
159 return;
160 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900161 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000162 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900163 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900164 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900165 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
166 // Remove HDMI CEC devices linked with this hardware.
167 for (Iterator<HdmiCecDeviceInfo> it = mHdmiCecDeviceList.iterator();
168 it.hasNext(); ) {
169 HdmiCecDeviceInfo deviceInfo = it.next();
170 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
171 mHandler.obtainMessage(ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0,
172 deviceInfo).sendToTarget();
173 it.remove();
174 }
175 }
176 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900177 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900178 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000179 }
180 }
181
182 @Override
183 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
184 synchronized (mLock) {
185 Connection connection = mConnections.get(deviceId);
186 if (connection == null) {
187 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
188 + deviceId);
189 return;
190 }
191 connection.updateConfigsLocked(configs);
192 try {
193 connection.getCallbackLocked().onStreamConfigChanged(configs);
194 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900195 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000196 }
197 }
198 }
199
Terry Heoc086a3d2014-06-18 14:26:44 +0900200 @Override
201 public void onFirstFrameCaptured(int deviceId, int streamId) {
202 synchronized (mLock) {
203 Connection connection = mConnections.get(deviceId);
204 if (connection == null) {
205 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
206 + deviceId);
207 return;
208 }
209 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
210 if (runnable != null) {
211 runnable.run();
212 connection.setOnFirstFrameCapturedLocked(null);
213 }
214 }
215 }
216
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000217 public List<TvInputHardwareInfo> getHardwareList() {
218 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900219 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000220 }
221 }
222
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900223 public List<HdmiCecDeviceInfo> getHdmiCecInputDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900224 synchronized (mLock) {
225 return Collections.unmodifiableList(mHdmiCecDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900226 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900227 }
228
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900229 private boolean checkUidChangedLocked(
230 Connection connection, int callingUid, int resolvedUserId) {
231 Integer connectionCallingUid = connection.getCallingUidLocked();
232 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
233 if (connectionCallingUid == null || connectionResolvedUserId == null) {
234 return true;
235 }
236 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
237 return true;
238 }
239 return false;
240 }
241
Wonsik Kim969167d2014-06-24 16:33:17 +0900242 private int convertConnectedToState(boolean connected) {
243 if (connected) {
244 return INPUT_STATE_CONNECTED;
245 } else {
246 return INPUT_STATE_DISCONNECTED;
247 }
248 }
249
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900250 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900251 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900252 String oldInputId = mHardwareInputIdMap.get(deviceId);
253 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900254 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900255 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900256 + info + ":" + deviceId);
257 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900258 mHardwareInputIdMap.put(deviceId, info.getId());
259 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900260
261 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900262 TvInputHardwareInfo hardwareInfo =
263 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
264 if (hardwareInfo == null) {
265 continue;
266 }
267 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900268 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900269 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900270 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
271 inputId).sendToTarget();
272 }
273 }
274 }
275 }
276
Wonsik Kim38feae92014-07-21 21:35:50 +0900277 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
278 for (int i = 0; i < map.size(); ++i) {
279 if (map.valueAt(i).equals(value)) {
280 return i;
281 }
282 }
283 return -1;
284 }
285
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900286 public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
287 if (info.getType() != TvInputInfo.TYPE_HDMI) {
288 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
289 }
290 synchronized (mLock) {
291 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900292 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
293 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900294 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
295 }
296 String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
297 if (oldInputId != null) {
298 Slog.w(TAG, "Trying to override previous registration: old = "
299 + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
300 + info + ":" + logicalAddress);
301 }
302 mHdmiCecInputIdMap.put(logicalAddress, info.getId());
303 mInputMap.put(info.getId(), info);
304 }
305 }
306
307 public void removeTvInput(String inputId) {
308 synchronized (mLock) {
309 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900310 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900311 if (hardwareIndex >= 0) {
312 mHardwareInputIdMap.removeAt(hardwareIndex);
313 }
Wonsik Kim38feae92014-07-21 21:35:50 +0900314 int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900315 if (cecIndex >= 0) {
316 mHdmiCecInputIdMap.removeAt(cecIndex);
317 }
318 }
319 }
320
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000321 /**
322 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
323 * the object, and if more than one process attempts to create hardware with the same deviceId,
324 * the latest service will get the object and all the other hardware are released. The
325 * release is notified via ITvInputHardwareCallback.onReleased().
326 */
327 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900328 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000329 if (callback == null) {
330 throw new NullPointerException();
331 }
332 synchronized (mLock) {
333 Connection connection = mConnections.get(deviceId);
334 if (connection == null) {
335 Slog.e(TAG, "Invalid deviceId : " + deviceId);
336 return null;
337 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900338 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900339 TvInputHardwareImpl hardware =
340 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000341 try {
342 callback.asBinder().linkToDeath(connection, 0);
343 } catch (RemoteException e) {
344 hardware.release();
345 return null;
346 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900347 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000348 }
349 return connection.getHardwareLocked();
350 }
351 }
352
353 /**
354 * Release the specified hardware.
355 */
356 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
357 int resolvedUserId) {
358 synchronized (mLock) {
359 Connection connection = mConnections.get(deviceId);
360 if (connection == null) {
361 Slog.e(TAG, "Invalid deviceId : " + deviceId);
362 return;
363 }
364 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900365 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000366 return;
367 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900368 connection.resetLocked(null, null, null, null, null);
369 }
370 }
371
Wonsik Kimd922a542014-07-24 18:25:29 +0900372 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
373 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900374 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
375 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900376 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900377 }
378 }
379 return null;
380 }
381
Terry Heoc086a3d2014-06-18 14:26:44 +0900382 private int findDeviceIdForInputIdLocked(String inputId) {
383 for (int i = 0; i < mConnections.size(); ++i) {
384 Connection connection = mConnections.get(i);
385 if (connection.getInfoLocked().getId().equals(inputId)) {
386 return i;
387 }
388 }
389 return -1;
390 }
391
392 /**
393 * Get the list of TvStreamConfig which is buffered mode.
394 */
395 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
396 int resolvedUserId) {
397 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
398 synchronized (mLock) {
399 int deviceId = findDeviceIdForInputIdLocked(inputId);
400 if (deviceId < 0) {
401 Slog.e(TAG, "Invalid inputId : " + inputId);
402 return configsList;
403 }
404 Connection connection = mConnections.get(deviceId);
405 for (TvStreamConfig config : connection.getConfigsLocked()) {
406 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
407 configsList.add(config);
408 }
409 }
410 }
411 return configsList;
412 }
413
414 /**
415 * Take a snapshot of the given TV input into the provided Surface.
416 */
417 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
418 int callingUid, int resolvedUserId) {
419 synchronized (mLock) {
420 int deviceId = findDeviceIdForInputIdLocked(inputId);
421 if (deviceId < 0) {
422 Slog.e(TAG, "Invalid inputId : " + inputId);
423 return false;
424 }
425 Connection connection = mConnections.get(deviceId);
426 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
427 if (hardwareImpl != null) {
428 // Stop previous capture.
429 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
430 if (runnable != null) {
431 runnable.run();
432 connection.setOnFirstFrameCapturedLocked(null);
433 }
434
435 boolean result = hardwareImpl.startCapture(surface, config);
436 if (result) {
437 connection.setOnFirstFrameCapturedLocked(new Runnable() {
438 @Override
439 public void run() {
440 hardwareImpl.stopCapture(config);
441 }
442 });
443 }
444 return result;
445 }
446 }
447 return false;
448 }
449
Wonsik Kimd922a542014-07-24 18:25:29 +0900450 private void processPendingHdmiDeviceEventsLocked() {
451 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
452 Message msg = it.next();
453 HdmiCecDeviceInfo deviceInfo = (HdmiCecDeviceInfo) msg.obj;
454 TvInputHardwareInfo hardwareInfo =
455 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
456 if (hardwareInfo != null) {
457 msg.sendToTarget();
458 it.remove();
459 }
460 }
461 }
462
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000463 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900464 private final TvInputHardwareInfo mHardwareInfo;
465 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000466 private TvInputHardwareImpl mHardware = null;
467 private ITvInputHardwareCallback mCallback;
468 private TvStreamConfig[] mConfigs = null;
469 private Integer mCallingUid = null;
470 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900471 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000472
Wonsik Kim969167d2014-06-24 16:33:17 +0900473 public Connection(TvInputHardwareInfo hardwareInfo) {
474 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000475 }
476
477 // *Locked methods assume TvInputHardwareManager.mLock is held.
478
Wonsik Kim969167d2014-06-24 16:33:17 +0900479 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
480 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000481 if (mHardware != null) {
482 try {
483 mCallback.onReleased();
484 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900485 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000486 }
487 mHardware.release();
488 }
489 mHardware = hardware;
490 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900491 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000492 mCallingUid = callingUid;
493 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900494 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000495
496 if (mHardware != null && mCallback != null) {
497 try {
498 mCallback.onStreamConfigChanged(getConfigsLocked());
499 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900500 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000501 }
502 }
503 }
504
505 public void updateConfigsLocked(TvStreamConfig[] configs) {
506 mConfigs = configs;
507 }
508
Wonsik Kim969167d2014-06-24 16:33:17 +0900509 public TvInputHardwareInfo getHardwareInfoLocked() {
510 return mHardwareInfo;
511 }
512
513 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000514 return mInfo;
515 }
516
517 public ITvInputHardware getHardwareLocked() {
518 return mHardware;
519 }
520
Terry Heoc086a3d2014-06-18 14:26:44 +0900521 public TvInputHardwareImpl getHardwareImplLocked() {
522 return mHardware;
523 }
524
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000525 public ITvInputHardwareCallback getCallbackLocked() {
526 return mCallback;
527 }
528
529 public TvStreamConfig[] getConfigsLocked() {
530 return mConfigs;
531 }
532
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900533 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000534 return mCallingUid;
535 }
536
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900537 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000538 return mResolvedUserId;
539 }
540
Terry Heoc086a3d2014-06-18 14:26:44 +0900541 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
542 mOnFirstFrameCaptured = runnable;
543 }
544
545 public Runnable getOnFirstFrameCapturedLocked() {
546 return mOnFirstFrameCaptured;
547 }
548
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000549 @Override
550 public void binderDied() {
551 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900552 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000553 }
554 }
555 }
556
557 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
558 private final TvInputHardwareInfo mInfo;
559 private boolean mReleased = false;
560 private final Object mImplLock = new Object();
561
Wonsik Kim06c41a32014-08-04 18:57:47 +0900562 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
563 new AudioManager.OnAudioPortUpdateListener() {
564 @Override
565 public void onAudioPortListUpdate(AudioPort[] portList) {
566 synchronized (mImplLock) {
567 updateAudioSinkLocked();
568 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE && mAudioSource == null) {
569 mAudioSource = findAudioDevicePort(mInfo.getAudioType(),
570 mInfo.getAudioAddress());
571 if (mActiveConfig != null) {
572 updateAudioPatchLocked();
573 }
574 }
575 }
576 }
577
578 @Override
579 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
580 // No-op
581 }
582
583 @Override
584 public void onServiceDied() {
585 synchronized (mImplLock) {
586 mAudioSource = null;
587 mAudioSink = null;
588 mAudioPatch = null;
589 }
590 }
591 };
592 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
593 private String mOverrideAudioAddress = "";
594 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900595 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900596 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900597 private float mCommittedVolume = 0.0f;
598 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900599
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900600 private TvStreamConfig mActiveConfig = null;
601
Wonsik Kimca17a902014-07-31 10:09:46 +0900602 private int mDesiredSamplingRate = 0;
603 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
604 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
605
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000606 public TvInputHardwareImpl(TvInputHardwareInfo info) {
607 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900608 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900609 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900610 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900611 mAudioSink = findAudioSinkFromAudioPolicy();
612 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900613 }
614
615 private AudioDevicePort findAudioSinkFromAudioPolicy() {
616 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
617 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
618 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
619 for (AudioPort port : devicePorts) {
620 AudioDevicePort devicePort = (AudioDevicePort) port;
621 if (devicePort.type() == sinkDevice) {
622 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900623 }
624 }
625 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900626 return null;
627 }
628
629 private AudioDevicePort findAudioDevicePort(int type, String address) {
630 if (type == AudioManager.DEVICE_NONE) {
631 return null;
632 }
633 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
634 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
635 return null;
636 }
637 for (AudioPort port : devicePorts) {
638 AudioDevicePort devicePort = (AudioDevicePort) port;
639 if (devicePort.type() == type && devicePort.address().equals(address)) {
640 return devicePort;
641 }
642 }
643 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000644 }
645
646 public void release() {
647 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900648 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900649 if (mAudioPatch != null) {
650 mAudioManager.releaseAudioPatch(mAudioPatch);
651 mAudioPatch = null;
652 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000653 mReleased = true;
654 }
655 }
656
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900657 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
658 // attempts to call setSurface with different TvStreamConfig objects, the last call will
659 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000660 @Override
661 public boolean setSurface(Surface surface, TvStreamConfig config)
662 throws RemoteException {
663 synchronized (mImplLock) {
664 if (mReleased) {
665 throw new IllegalStateException("Device already released.");
666 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900667 if (surface != null && config == null) {
668 return false;
669 }
670 if (surface == null && mActiveConfig == null) {
671 return false;
672 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900673 if (mAudioSource != null && mAudioSink != null) {
674 if (surface != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900675 updateAudioPatchLocked();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900676 } else {
677 mAudioManager.releaseAudioPatch(mAudioPatch);
678 mAudioPatch = null;
679 }
680 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900681 int result = TvInputHal.ERROR_UNKNOWN;
682 if (surface == null) {
683 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
684 mActiveConfig = null;
685 } else {
686 if (config != mActiveConfig && mActiveConfig != null) {
687 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
688 if (result != TvInputHal.SUCCESS) {
689 mActiveConfig = null;
690 return false;
691 }
692 }
693 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
694 if (result == TvInputHal.SUCCESS) {
695 mActiveConfig = config;
696 }
697 }
698 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000699 }
700 }
701
Wonsik Kimca17a902014-07-31 10:09:46 +0900702 private void updateAudioPatchLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900703 if (mAudioSource == null || mAudioSink == null) {
704 if (mAudioPatch != null) {
705 throw new IllegalStateException("Audio patch should be null if audio source "
706 + "or sink is null.");
707 }
708 return;
709 }
710
Wonsik Kim8e45a332014-08-04 10:17:18 +0900711 AudioGainConfig sourceGainConfig = null;
712 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
713 AudioGain sourceGain = null;
714 for (AudioGain gain : mAudioSource.gains()) {
715 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
716 sourceGain = gain;
717 break;
718 }
719 }
720 // NOTE: we only change the source gain in MODE_JOINT here.
721 if (sourceGain != null) {
722 int steps = (sourceGain.maxValue() - sourceGain.minValue())
723 / sourceGain.stepValue();
724 int gainValue = sourceGain.minValue();
725 if (mVolume < 1.0f) {
726 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
727 } else {
728 gainValue = sourceGain.maxValue();
729 }
730 int numChannels = 0;
731 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
732 numChannels += (mask & 1);
733 }
734 int[] gainValues = new int[numChannels];
735 Arrays.fill(gainValues, gainValue);
736 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
737 sourceGain.channelMask(), gainValues, 0);
738 } else {
739 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
740 }
741 }
742
Wonsik Kimca17a902014-07-31 10:09:46 +0900743 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
744 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
745 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900746 boolean shouldRecreateAudioPatch = false;
Wonsik Kimca17a902014-07-31 10:09:46 +0900747 if (sinkConfig == null
748 || (mDesiredSamplingRate != 0
749 && sinkConfig.samplingRate() != mDesiredSamplingRate)
750 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
751 && sinkConfig.channelMask() != mDesiredChannelMask)
752 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
753 && sinkConfig.format() != mDesiredFormat)) {
754 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
755 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900756 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900757 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900758 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900759 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900760 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
761 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900762 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900763 if (shouldRecreateAudioPatch) {
764 mCommittedVolume = mVolume;
765 mAudioManager.createAudioPatch(
766 audioPatchArray,
767 new AudioPortConfig[] { sourceConfig },
768 new AudioPortConfig[] { sinkConfig });
769 mAudioPatch = audioPatchArray[0];
770 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900771 }
772
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000773 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900774 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000775 synchronized (mImplLock) {
776 if (mReleased) {
777 throw new IllegalStateException("Device already released.");
778 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900779 mVolume = volume;
780 updateAudioPatchLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000781 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000782 }
783
784 @Override
785 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
786 synchronized (mImplLock) {
787 if (mReleased) {
788 throw new IllegalStateException("Device already released.");
789 }
790 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900791 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000792 return false;
793 }
794 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
795 return false;
796 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900797
798 private boolean startCapture(Surface surface, TvStreamConfig config) {
799 synchronized (mImplLock) {
800 if (mReleased) {
801 return false;
802 }
803 if (surface == null || config == null) {
804 return false;
805 }
806 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
807 return false;
808 }
809
810 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
811 return result == TvInputHal.SUCCESS;
812 }
813 }
814
815 private boolean stopCapture(TvStreamConfig config) {
816 synchronized (mImplLock) {
817 if (mReleased) {
818 return false;
819 }
820 if (config == null) {
821 return false;
822 }
823
824 int result = mHal.removeStream(mInfo.getDeviceId(), config);
825 return result == TvInputHal.SUCCESS;
826 }
827 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900828
Wonsik Kim06c41a32014-08-04 18:57:47 +0900829 private void updateAudioSinkLocked() {
830 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
831 return;
832 }
833 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
834 mAudioSink = findAudioSinkFromAudioPolicy();
835 } else {
836 AudioDevicePort audioSink =
837 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
838 if (audioSink != null) {
839 mAudioSink = audioSink;
840 }
841 }
842 }
843
Wonsik Kimca17a902014-07-31 10:09:46 +0900844 @Override
845 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
846 int channelMask, int format) {
847 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900848 mOverrideAudioType = audioType;
849 mOverrideAudioAddress = audioAddress;
850 updateAudioSinkLocked();
851
Wonsik Kimca17a902014-07-31 10:09:46 +0900852 mDesiredSamplingRate = samplingRate;
853 mDesiredChannelMask = channelMask;
854 mDesiredFormat = format;
855
856 if (mAudioPatch != null) {
857 updateAudioPatchLocked();
858 }
859 }
860 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000861 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900862
Wonsik Kim187423c2014-06-25 14:12:48 +0900863 interface Listener {
864 public void onStateChanged(String inputId, int state);
865 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900866 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900867 public void onHdmiCecDeviceAdded(HdmiCecDeviceInfo cecDevice);
868 public void onHdmiCecDeviceRemoved(HdmiCecDeviceInfo cecDevice);
869 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900870
Wonsik Kim187423c2014-06-25 14:12:48 +0900871 private class ListenerHandler extends Handler {
872 private static final int STATE_CHANGED = 1;
873 private static final int HARDWARE_DEVICE_ADDED = 2;
874 private static final int HARDWARE_DEVICE_REMOVED = 3;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900875 private static final int HDMI_CEC_DEVICE_ADDED = 4;
876 private static final int HDMI_CEC_DEVICE_REMOVED = 5;
Wonsik Kim969167d2014-06-24 16:33:17 +0900877
878 @Override
879 public final void handleMessage(Message msg) {
880 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900881 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900882 String inputId = (String) msg.obj;
883 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900884 mListener.onStateChanged(inputId, state);
885 break;
886 }
887 case HARDWARE_DEVICE_ADDED: {
888 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
889 mListener.onHardwareDeviceAdded(info);
890 break;
891 }
892 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900893 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
894 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900895 break;
896 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900897 case HDMI_CEC_DEVICE_ADDED: {
Wonsik Kim187423c2014-06-25 14:12:48 +0900898 HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
899 mListener.onHdmiCecDeviceAdded(info);
900 break;
901 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900902 case HDMI_CEC_DEVICE_REMOVED: {
Wonsik Kim187423c2014-06-25 14:12:48 +0900903 HdmiCecDeviceInfo info = (HdmiCecDeviceInfo) msg.obj;
904 mListener.onHdmiCecDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900905 break;
906 }
907 default: {
908 Slog.w(TAG, "Unhandled message: " + msg);
909 break;
910 }
911 }
912 }
913 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900914
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900915 // Listener implementations for HdmiControlService
916
917 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
918 @Override
919 public void onReceived(HdmiHotplugEvent event) {
920 synchronized (mLock) {
921 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900922 TvInputHardwareInfo hardwareInfo =
923 findHardwareInfoForHdmiPortLocked(event.getPort());
924 if (hardwareInfo == null) {
925 return;
926 }
927 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900928 if (inputId == null) {
929 return;
930 }
931 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
932 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
933 }
934 }
935 }
936
Wonsik Kim187423c2014-06-25 14:12:48 +0900937 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
938 @Override
939 public void onStatusChanged(HdmiCecDeviceInfo deviceInfo, boolean activated) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900940 synchronized (mLock) {
941 if (activated) {
942 if (!mHdmiCecDeviceList.contains(deviceInfo)) {
943 mHdmiCecDeviceList.add(deviceInfo);
944 } else {
945 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
946 return;
947 }
948 } else {
949 if (!mHdmiCecDeviceList.remove(deviceInfo)) {
950 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
951 return;
952 }
953 }
954 Message msg = mHandler.obtainMessage(
955 activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED
956 : ListenerHandler.HDMI_CEC_DEVICE_REMOVED,
957 0, 0, deviceInfo);
958 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
959 msg.sendToTarget();
960 } else {
961 mPendingHdmiDeviceEvents.add(msg);
962 }
963 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900964 }
965 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900966
967 private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
968 @Override
969 public void onChanged(HdmiCecDeviceInfo device) throws RemoteException {
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900970 String inputId;
971 synchronized (mLock) {
972 if (device.isCecDevice()) {
973 inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress());
974 } else {
Wonsik Kimd922a542014-07-24 18:25:29 +0900975 TvInputHardwareInfo hardwareInfo =
976 findHardwareInfoForHdmiPortLocked(device.getPortId());
977 inputId = (hardwareInfo == null) ? null
978 : mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900979 }
980 }
981 if (inputId != null) {
982 Intent intent = new Intent(Intent.ACTION_VIEW);
983 intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
Jinsuk Kim58500f42014-08-05 12:48:35 +0900984 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Jinsuk Kimb2b31512014-07-21 17:14:44 +0900985 mContext.startActivity(intent);
986 } else {
987 Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
988 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900989 }
990 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000991}