blob: 7f1248990fc62cbe670df223a913794a83635588 [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;
Jungshik Jang61daf6b2014-08-08 11:38:28 +090024import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090025import android.hardware.hdmi.HdmiDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090026import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090027import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090028import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090029import android.hardware.hdmi.IHdmiHotplugEventListener;
30import android.hardware.hdmi.IHdmiInputChangeListener;
Jungshik Jang1f819bb2014-08-08 10:46:54 +090031import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090032import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090033import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090034import android.media.AudioGain;
35import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090036import android.media.AudioManager;
37import android.media.AudioPatch;
38import android.media.AudioPort;
39import android.media.AudioPortConfig;
Jae Seod5cc4a22014-05-30 16:57:43 -070040import android.media.tv.ITvInputHardware;
41import android.media.tv.ITvInputHardwareCallback;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090042import android.media.tv.TvContract;
Jae Seo546c6352014-08-07 11:57:01 -070043import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090044import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070045import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090046import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000047import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090048import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000049import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090050import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090051import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000052import android.util.Slog;
53import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090054import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000055import android.view.KeyEvent;
56import android.view.Surface;
57
Wonsik Kim969167d2014-06-24 16:33:17 +090058import com.android.server.SystemService;
59
Wonsik Kimc22dbb62014-05-26 02:26:04 +000060import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090061import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090062import java.util.Collections;
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 +000067
68/**
69 * A helper class for TvInputManagerService to handle TV input hardware.
70 *
71 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
72 * calls to tv_input HAL module.
73 *
74 * @hide
75 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090076class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000077 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090078
79 private final Context mContext;
80 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000081 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090082 private final SparseArray<Connection> mConnections = new SparseArray<>();
83 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070084 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090085 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090086 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090087 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070088 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090089 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090090
Wonsik Kimd7c29182014-05-27 10:38:21 +090091 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090092 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090093 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
94 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090095 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +090096 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
97 new HdmiSystemAudioModeChangeListener();
Jinsuk Kimd38bf472014-08-11 11:29:18 +090098
Wonsik Kimd922a542014-07-24 18:25:29 +090099 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900100 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900101 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900102
Wonsik Kim187423c2014-06-25 14:12:48 +0900103 // Calls to mListener should happen here.
104 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000105
106 private final Object mLock = new Object();
107
Wonsik Kim187423c2014-06-25 14:12:48 +0900108 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000109 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900110 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900111 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000112 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900113 }
114
115 public void onBootPhase(int phase) {
116 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900117 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
118 Context.HDMI_CONTROL_SERVICE));
119 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900120 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900121 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
122 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900123 mHdmiControlService.addSystemAudioModeChangeListener(
124 mHdmiSystemAudioModeChangeListener);
Jae Seo546c6352014-08-07 11:57:01 -0700125 mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900126 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900127 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900128 }
129 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900130 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000131 }
132
133 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900134 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000135 synchronized (mLock) {
136 Connection connection = new Connection(info);
137 connection.updateConfigsLocked(configs);
138 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900139 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900140 mHandler.obtainMessage(
141 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900142 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
143 processPendingHdmiDeviceEventsLocked();
144 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000145 }
146 }
147
Wonsik Kimd922a542014-07-24 18:25:29 +0900148 private void buildHardwareListLocked() {
149 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000150 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900151 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000152 }
153 }
154
155 @Override
156 public void onDeviceUnavailable(int deviceId) {
157 synchronized (mLock) {
158 Connection connection = mConnections.get(deviceId);
159 if (connection == null) {
160 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
161 return;
162 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900163 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000164 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900165 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900166 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900167 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700168 // Remove HDMI devices linked with this hardware.
169 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900170 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900171 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700172 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900173 deviceInfo).sendToTarget();
174 it.remove();
175 }
176 }
177 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900178 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900179 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000180 }
181 }
182
183 @Override
184 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
185 synchronized (mLock) {
186 Connection connection = mConnections.get(deviceId);
187 if (connection == null) {
188 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
189 + deviceId);
190 return;
191 }
192 connection.updateConfigsLocked(configs);
193 try {
194 connection.getCallbackLocked().onStreamConfigChanged(configs);
195 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900196 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000197 }
198 }
199 }
200
Terry Heoc086a3d2014-06-18 14:26:44 +0900201 @Override
202 public void onFirstFrameCaptured(int deviceId, int streamId) {
203 synchronized (mLock) {
204 Connection connection = mConnections.get(deviceId);
205 if (connection == null) {
206 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
207 + deviceId);
208 return;
209 }
210 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
211 if (runnable != null) {
212 runnable.run();
213 connection.setOnFirstFrameCapturedLocked(null);
214 }
215 }
216 }
217
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000218 public List<TvInputHardwareInfo> getHardwareList() {
219 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900220 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000221 }
222 }
223
Jae Seo546c6352014-08-07 11:57:01 -0700224 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900225 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700226 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900227 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900228 }
229
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900230 private boolean checkUidChangedLocked(
231 Connection connection, int callingUid, int resolvedUserId) {
232 Integer connectionCallingUid = connection.getCallingUidLocked();
233 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
234 if (connectionCallingUid == null || connectionResolvedUserId == null) {
235 return true;
236 }
237 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
238 return true;
239 }
240 return false;
241 }
242
Wonsik Kim969167d2014-06-24 16:33:17 +0900243 private int convertConnectedToState(boolean connected) {
244 if (connected) {
245 return INPUT_STATE_CONNECTED;
246 } else {
247 return INPUT_STATE_DISCONNECTED;
248 }
249 }
250
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900251 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900252 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900253 String oldInputId = mHardwareInputIdMap.get(deviceId);
254 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900255 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900256 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900257 + info + ":" + deviceId);
258 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900259 mHardwareInputIdMap.put(deviceId, info.getId());
260 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900261
262 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900263 TvInputHardwareInfo hardwareInfo =
264 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
265 if (hardwareInfo == null) {
266 continue;
267 }
268 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900269 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900270 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900271 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
272 inputId).sendToTarget();
273 }
274 }
275 }
276 }
277
Wonsik Kim38feae92014-07-21 21:35:50 +0900278 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
279 for (int i = 0; i < map.size(); ++i) {
280 if (map.valueAt(i).equals(value)) {
281 return i;
282 }
283 }
284 return -1;
285 }
286
Jae Seo546c6352014-08-07 11:57:01 -0700287 public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900288 if (info.getType() != TvInputInfo.TYPE_HDMI) {
289 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
290 }
291 synchronized (mLock) {
292 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900293 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
294 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900295 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
296 }
Jae Seo546c6352014-08-07 11:57:01 -0700297 String oldInputId = mHdmiInputIdMap.get(logicalAddress);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900298 if (oldInputId != null) {
299 Slog.w(TAG, "Trying to override previous registration: old = "
300 + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
301 + info + ":" + logicalAddress);
302 }
Jae Seo546c6352014-08-07 11:57:01 -0700303 mHdmiInputIdMap.put(logicalAddress, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900304 mInputMap.put(info.getId(), info);
305 }
306 }
307
308 public void removeTvInput(String inputId) {
309 synchronized (mLock) {
310 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900311 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900312 if (hardwareIndex >= 0) {
313 mHardwareInputIdMap.removeAt(hardwareIndex);
314 }
Jae Seo546c6352014-08-07 11:57:01 -0700315 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
316 if (deviceIndex >= 0) {
317 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900318 }
319 }
320 }
321
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000322 /**
323 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
324 * the object, and if more than one process attempts to create hardware with the same deviceId,
325 * the latest service will get the object and all the other hardware are released. The
326 * release is notified via ITvInputHardwareCallback.onReleased().
327 */
328 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900329 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000330 if (callback == null) {
331 throw new NullPointerException();
332 }
333 synchronized (mLock) {
334 Connection connection = mConnections.get(deviceId);
335 if (connection == null) {
336 Slog.e(TAG, "Invalid deviceId : " + deviceId);
337 return null;
338 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900339 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900340 TvInputHardwareImpl hardware =
341 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000342 try {
343 callback.asBinder().linkToDeath(connection, 0);
344 } catch (RemoteException e) {
345 hardware.release();
346 return null;
347 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900348 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000349 }
350 return connection.getHardwareLocked();
351 }
352 }
353
354 /**
355 * Release the specified hardware.
356 */
357 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
358 int resolvedUserId) {
359 synchronized (mLock) {
360 Connection connection = mConnections.get(deviceId);
361 if (connection == null) {
362 Slog.e(TAG, "Invalid deviceId : " + deviceId);
363 return;
364 }
365 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900366 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000367 return;
368 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900369 connection.resetLocked(null, null, null, null, null);
370 }
371 }
372
Wonsik Kimd922a542014-07-24 18:25:29 +0900373 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
374 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900375 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
376 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900377 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900378 }
379 }
380 return null;
381 }
382
Terry Heoc086a3d2014-06-18 14:26:44 +0900383 private int findDeviceIdForInputIdLocked(String inputId) {
384 for (int i = 0; i < mConnections.size(); ++i) {
385 Connection connection = mConnections.get(i);
386 if (connection.getInfoLocked().getId().equals(inputId)) {
387 return i;
388 }
389 }
390 return -1;
391 }
392
393 /**
394 * Get the list of TvStreamConfig which is buffered mode.
395 */
396 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
397 int resolvedUserId) {
398 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
399 synchronized (mLock) {
400 int deviceId = findDeviceIdForInputIdLocked(inputId);
401 if (deviceId < 0) {
402 Slog.e(TAG, "Invalid inputId : " + inputId);
403 return configsList;
404 }
405 Connection connection = mConnections.get(deviceId);
406 for (TvStreamConfig config : connection.getConfigsLocked()) {
407 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
408 configsList.add(config);
409 }
410 }
411 }
412 return configsList;
413 }
414
415 /**
416 * Take a snapshot of the given TV input into the provided Surface.
417 */
418 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
419 int callingUid, int resolvedUserId) {
420 synchronized (mLock) {
421 int deviceId = findDeviceIdForInputIdLocked(inputId);
422 if (deviceId < 0) {
423 Slog.e(TAG, "Invalid inputId : " + inputId);
424 return false;
425 }
426 Connection connection = mConnections.get(deviceId);
427 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
428 if (hardwareImpl != null) {
429 // Stop previous capture.
430 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
431 if (runnable != null) {
432 runnable.run();
433 connection.setOnFirstFrameCapturedLocked(null);
434 }
435
436 boolean result = hardwareImpl.startCapture(surface, config);
437 if (result) {
438 connection.setOnFirstFrameCapturedLocked(new Runnable() {
439 @Override
440 public void run() {
441 hardwareImpl.stopCapture(config);
442 }
443 });
444 }
445 return result;
446 }
447 }
448 return false;
449 }
450
Wonsik Kimd922a542014-07-24 18:25:29 +0900451 private void processPendingHdmiDeviceEventsLocked() {
452 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
453 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900454 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900455 TvInputHardwareInfo hardwareInfo =
456 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
457 if (hardwareInfo != null) {
458 msg.sendToTarget();
459 it.remove();
460 }
461 }
462 }
463
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000464 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900465 private final TvInputHardwareInfo mHardwareInfo;
466 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000467 private TvInputHardwareImpl mHardware = null;
468 private ITvInputHardwareCallback mCallback;
469 private TvStreamConfig[] mConfigs = null;
470 private Integer mCallingUid = null;
471 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900472 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000473
Wonsik Kim969167d2014-06-24 16:33:17 +0900474 public Connection(TvInputHardwareInfo hardwareInfo) {
475 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000476 }
477
478 // *Locked methods assume TvInputHardwareManager.mLock is held.
479
Wonsik Kim969167d2014-06-24 16:33:17 +0900480 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
481 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000482 if (mHardware != null) {
483 try {
484 mCallback.onReleased();
485 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900486 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000487 }
488 mHardware.release();
489 }
490 mHardware = hardware;
491 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900492 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000493 mCallingUid = callingUid;
494 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900495 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000496
497 if (mHardware != null && mCallback != null) {
498 try {
499 mCallback.onStreamConfigChanged(getConfigsLocked());
500 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900501 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000502 }
503 }
504 }
505
506 public void updateConfigsLocked(TvStreamConfig[] configs) {
507 mConfigs = configs;
508 }
509
Wonsik Kim969167d2014-06-24 16:33:17 +0900510 public TvInputHardwareInfo getHardwareInfoLocked() {
511 return mHardwareInfo;
512 }
513
514 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000515 return mInfo;
516 }
517
518 public ITvInputHardware getHardwareLocked() {
519 return mHardware;
520 }
521
Terry Heoc086a3d2014-06-18 14:26:44 +0900522 public TvInputHardwareImpl getHardwareImplLocked() {
523 return mHardware;
524 }
525
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000526 public ITvInputHardwareCallback getCallbackLocked() {
527 return mCallback;
528 }
529
530 public TvStreamConfig[] getConfigsLocked() {
531 return mConfigs;
532 }
533
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900534 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000535 return mCallingUid;
536 }
537
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900538 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000539 return mResolvedUserId;
540 }
541
Terry Heoc086a3d2014-06-18 14:26:44 +0900542 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
543 mOnFirstFrameCaptured = runnable;
544 }
545
546 public Runnable getOnFirstFrameCapturedLocked() {
547 return mOnFirstFrameCaptured;
548 }
549
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000550 @Override
551 public void binderDied() {
552 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900553 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000554 }
555 }
556 }
557
558 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
559 private final TvInputHardwareInfo mInfo;
560 private boolean mReleased = false;
561 private final Object mImplLock = new Object();
562
Wonsik Kim06c41a32014-08-04 18:57:47 +0900563 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
564 new AudioManager.OnAudioPortUpdateListener() {
565 @Override
566 public void onAudioPortListUpdate(AudioPort[] portList) {
567 synchronized (mImplLock) {
568 updateAudioSinkLocked();
569 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE && mAudioSource == null) {
570 mAudioSource = findAudioDevicePort(mInfo.getAudioType(),
571 mInfo.getAudioAddress());
572 if (mActiveConfig != null) {
573 updateAudioPatchLocked();
574 }
575 }
576 }
577 }
578
579 @Override
580 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
581 // No-op
582 }
583
584 @Override
585 public void onServiceDied() {
586 synchronized (mImplLock) {
587 mAudioSource = null;
588 mAudioSink = null;
589 mAudioPatch = null;
590 }
591 }
592 };
593 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
594 private String mOverrideAudioAddress = "";
595 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900596 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900597 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900598 private float mCommittedVolume = 0.0f;
599 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900600
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900601 private TvStreamConfig mActiveConfig = null;
602
Wonsik Kimca17a902014-07-31 10:09:46 +0900603 private int mDesiredSamplingRate = 0;
604 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
605 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
606
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000607 public TvInputHardwareImpl(TvInputHardwareInfo info) {
608 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900609 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900610 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900611 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900612 mAudioSink = findAudioSinkFromAudioPolicy();
613 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900614 }
615
616 private AudioDevicePort findAudioSinkFromAudioPolicy() {
617 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
618 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
619 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
620 for (AudioPort port : devicePorts) {
621 AudioDevicePort devicePort = (AudioDevicePort) port;
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900622 if ((devicePort.type() & sinkDevice) != 0) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900623 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900624 }
625 }
626 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900627 return null;
628 }
629
630 private AudioDevicePort findAudioDevicePort(int type, String address) {
631 if (type == AudioManager.DEVICE_NONE) {
632 return null;
633 }
634 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
635 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
636 return null;
637 }
638 for (AudioPort port : devicePorts) {
639 AudioDevicePort devicePort = (AudioDevicePort) port;
640 if (devicePort.type() == type && devicePort.address().equals(address)) {
641 return devicePort;
642 }
643 }
644 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000645 }
646
647 public void release() {
648 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900649 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900650 if (mAudioPatch != null) {
651 mAudioManager.releaseAudioPatch(mAudioPatch);
652 mAudioPatch = null;
653 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000654 mReleased = true;
655 }
656 }
657
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900658 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
659 // attempts to call setSurface with different TvStreamConfig objects, the last call will
660 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000661 @Override
662 public boolean setSurface(Surface surface, TvStreamConfig config)
663 throws RemoteException {
664 synchronized (mImplLock) {
665 if (mReleased) {
666 throw new IllegalStateException("Device already released.");
667 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900668 if (surface != null && config == null) {
669 return false;
670 }
671 if (surface == null && mActiveConfig == null) {
672 return false;
673 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900674 if (mAudioSource != null && mAudioSink != null) {
675 if (surface != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900676 updateAudioPatchLocked();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900677 } else {
678 mAudioManager.releaseAudioPatch(mAudioPatch);
679 mAudioPatch = null;
680 }
681 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900682 int result = TvInputHal.ERROR_UNKNOWN;
683 if (surface == null) {
684 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
685 mActiveConfig = null;
686 } else {
687 if (config != mActiveConfig && mActiveConfig != null) {
688 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
689 if (result != TvInputHal.SUCCESS) {
690 mActiveConfig = null;
691 return false;
692 }
693 }
694 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
695 if (result == TvInputHal.SUCCESS) {
696 mActiveConfig = config;
697 }
698 }
699 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000700 }
701 }
702
Wonsik Kimca17a902014-07-31 10:09:46 +0900703 private void updateAudioPatchLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900704 if (mAudioSource == null || mAudioSink == null) {
705 if (mAudioPatch != null) {
706 throw new IllegalStateException("Audio patch should be null if audio source "
707 + "or sink is null.");
708 }
709 return;
710 }
711
Wonsik Kim8e45a332014-08-04 10:17:18 +0900712 AudioGainConfig sourceGainConfig = null;
713 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
714 AudioGain sourceGain = null;
715 for (AudioGain gain : mAudioSource.gains()) {
716 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
717 sourceGain = gain;
718 break;
719 }
720 }
721 // NOTE: we only change the source gain in MODE_JOINT here.
722 if (sourceGain != null) {
723 int steps = (sourceGain.maxValue() - sourceGain.minValue())
724 / sourceGain.stepValue();
725 int gainValue = sourceGain.minValue();
726 if (mVolume < 1.0f) {
727 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
728 } else {
729 gainValue = sourceGain.maxValue();
730 }
731 int numChannels = 0;
732 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
733 numChannels += (mask & 1);
734 }
735 int[] gainValues = new int[numChannels];
736 Arrays.fill(gainValues, gainValue);
737 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
738 sourceGain.channelMask(), gainValues, 0);
739 } else {
740 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
741 }
742 }
743
Wonsik Kimca17a902014-07-31 10:09:46 +0900744 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
745 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
746 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900747 boolean shouldRecreateAudioPatch = false;
Wonsik Kimca17a902014-07-31 10:09:46 +0900748 if (sinkConfig == null
749 || (mDesiredSamplingRate != 0
750 && sinkConfig.samplingRate() != mDesiredSamplingRate)
751 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
752 && sinkConfig.channelMask() != mDesiredChannelMask)
753 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
754 && sinkConfig.format() != mDesiredFormat)) {
755 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
756 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900757 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900758 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900759 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900760 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900761 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
762 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900763 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900764 if (shouldRecreateAudioPatch) {
765 mCommittedVolume = mVolume;
766 mAudioManager.createAudioPatch(
767 audioPatchArray,
768 new AudioPortConfig[] { sourceConfig },
769 new AudioPortConfig[] { sinkConfig });
770 mAudioPatch = audioPatchArray[0];
771 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900772 }
773
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000774 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900775 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000776 synchronized (mImplLock) {
777 if (mReleased) {
778 throw new IllegalStateException("Device already released.");
779 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900780 mVolume = volume;
781 updateAudioPatchLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000782 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000783 }
784
785 @Override
786 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
787 synchronized (mImplLock) {
788 if (mReleased) {
789 throw new IllegalStateException("Device already released.");
790 }
791 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900792 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000793 return false;
794 }
795 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
796 return false;
797 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900798
799 private boolean startCapture(Surface surface, TvStreamConfig config) {
800 synchronized (mImplLock) {
801 if (mReleased) {
802 return false;
803 }
804 if (surface == null || config == null) {
805 return false;
806 }
807 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
808 return false;
809 }
810
811 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
812 return result == TvInputHal.SUCCESS;
813 }
814 }
815
816 private boolean stopCapture(TvStreamConfig config) {
817 synchronized (mImplLock) {
818 if (mReleased) {
819 return false;
820 }
821 if (config == null) {
822 return false;
823 }
824
825 int result = mHal.removeStream(mInfo.getDeviceId(), config);
826 return result == TvInputHal.SUCCESS;
827 }
828 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900829
Wonsik Kim06c41a32014-08-04 18:57:47 +0900830 private void updateAudioSinkLocked() {
831 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
832 return;
833 }
834 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
835 mAudioSink = findAudioSinkFromAudioPolicy();
836 } else {
837 AudioDevicePort audioSink =
838 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
839 if (audioSink != null) {
840 mAudioSink = audioSink;
841 }
842 }
843 }
844
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900845 private void handleAudioSinkUpdated() {
846 synchronized (mImplLock) {
847 updateAudioSinkLocked();
848 updateAudioPatchLocked();
849 }
850 }
851
Wonsik Kimca17a902014-07-31 10:09:46 +0900852 @Override
853 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
854 int channelMask, int format) {
855 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900856 mOverrideAudioType = audioType;
857 mOverrideAudioAddress = audioAddress;
858 updateAudioSinkLocked();
859
Wonsik Kimca17a902014-07-31 10:09:46 +0900860 mDesiredSamplingRate = samplingRate;
861 mDesiredChannelMask = channelMask;
862 mDesiredFormat = format;
863
864 if (mAudioPatch != null) {
865 updateAudioPatchLocked();
866 }
867 }
868 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000869 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900870
Wonsik Kim187423c2014-06-25 14:12:48 +0900871 interface Listener {
872 public void onStateChanged(String inputId, int state);
873 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900874 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700875 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
876 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900877 public void onHdmiDeviceUpdated(HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900878 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900879
Wonsik Kim187423c2014-06-25 14:12:48 +0900880 private class ListenerHandler extends Handler {
881 private static final int STATE_CHANGED = 1;
882 private static final int HARDWARE_DEVICE_ADDED = 2;
883 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700884 private static final int HDMI_DEVICE_ADDED = 4;
885 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900886 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +0900887
888 @Override
889 public final void handleMessage(Message msg) {
890 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900891 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900892 String inputId = (String) msg.obj;
893 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900894 mListener.onStateChanged(inputId, state);
895 break;
896 }
897 case HARDWARE_DEVICE_ADDED: {
898 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
899 mListener.onHardwareDeviceAdded(info);
900 break;
901 }
902 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900903 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
904 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900905 break;
906 }
Jae Seo546c6352014-08-07 11:57:01 -0700907 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900908 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700909 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900910 break;
911 }
Jae Seo546c6352014-08-07 11:57:01 -0700912 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900913 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700914 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900915 break;
916 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900917 case HDMI_DEVICE_UPDATED: {
918 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
919 mListener.onHdmiDeviceUpdated(info);
920 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900921 default: {
922 Slog.w(TAG, "Unhandled message: " + msg);
923 break;
924 }
925 }
926 }
927 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900928
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900929 // Listener implementations for HdmiControlService
930
931 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
932 @Override
933 public void onReceived(HdmiHotplugEvent event) {
934 synchronized (mLock) {
935 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900936 TvInputHardwareInfo hardwareInfo =
937 findHardwareInfoForHdmiPortLocked(event.getPort());
938 if (hardwareInfo == null) {
939 return;
940 }
941 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900942 if (inputId == null) {
943 return;
944 }
945 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
946 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
947 }
948 }
949 }
950
Wonsik Kim187423c2014-06-25 14:12:48 +0900951 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
952 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900953 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900954 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900955 int messageType = 0;
956 switch (status) {
957 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
958 if (!mHdmiDeviceList.contains(deviceInfo)) {
959 mHdmiDeviceList.add(deviceInfo);
960 } else {
961 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
962 return;
963 }
964 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
965 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900966 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900967 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
968 if (!mHdmiDeviceList.remove(deviceInfo)) {
969 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
970 return;
971 }
972 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
973 break;
974 }
975 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
976 if (!mHdmiDeviceList.remove(deviceInfo)) {
977 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
978 return;
979 }
980 mHdmiDeviceList.add(deviceInfo);
981 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
982 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900983 }
984 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900985
986 Message msg = mHandler.obtainMessage(messageType, 0, 0, deviceInfo);
Wonsik Kimd922a542014-07-24 18:25:29 +0900987 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
988 msg.sendToTarget();
989 } else {
990 mPendingHdmiDeviceEvents.add(msg);
991 }
992 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900993 }
994 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900995
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900996 private final class HdmiSystemAudioModeChangeListener extends
997 IHdmiSystemAudioModeChangeListener.Stub {
998 @Override
999 public void onStatusChanged(boolean enabled) throws RemoteException {
1000 synchronized (mLock) {
1001 for (int i = 0; i < mConnections.size(); ++i) {
1002 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
1003 impl.handleAudioSinkUpdated();
1004 }
1005 }
1006 }
1007 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001008}