blob: 77ab33b6eb1dc7de2752adb93aaf37b89a2a7c42 [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;
Jungshik Jang61daf6b2014-08-08 11:38:28 +090023import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090024import android.hardware.hdmi.HdmiDeviceInfo;
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;
Jungshik Jang1f819bb2014-08-08 10:46:54 +090029import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
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;
Jae Seo546c6352014-08-07 11:57:01 -070040import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090041import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070042import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090043import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000044import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090045import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000046import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090047import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090048import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000049import android.util.Slog;
50import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090051import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000052import android.view.KeyEvent;
53import android.view.Surface;
54
Wonsik Kime92f8572014-08-12 18:30:24 +090055import com.android.internal.os.SomeArgs;
Wonsik Kim969167d2014-06-24 16:33:17 +090056import com.android.server.SystemService;
57
Wonsik Kimc22dbb62014-05-26 02:26:04 +000058import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090059import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090060import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090061import java.util.Iterator;
62import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000063import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090064import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000065
66/**
67 * A helper class for TvInputManagerService to handle TV input hardware.
68 *
69 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
70 * calls to tv_input HAL module.
71 *
72 * @hide
73 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090074class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000075 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090076
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090077 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000078 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090079 private final SparseArray<Connection> mConnections = new SparseArray<>();
80 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070081 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090082 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090083 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090084 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070085 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090086 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090087
Wonsik Kimd7c29182014-05-27 10:38:21 +090088 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090089 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090090 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
91 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090092 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +090093 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
94 new HdmiSystemAudioModeChangeListener();
Jinsuk Kimd38bf472014-08-11 11:29:18 +090095
Wonsik Kimd922a542014-07-24 18:25:29 +090096 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090097 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +090098 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +090099
Wonsik Kim187423c2014-06-25 14:12:48 +0900100 // Calls to mListener should happen here.
101 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000102
103 private final Object mLock = new Object();
104
Wonsik Kim187423c2014-06-25 14:12:48 +0900105 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900106 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900107 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000108 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900109 }
110
111 public void onBootPhase(int phase) {
112 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900113 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
114 Context.HDMI_CONTROL_SERVICE));
115 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900116 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900117 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
118 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900119 mHdmiControlService.addSystemAudioModeChangeListener(
120 mHdmiSystemAudioModeChangeListener);
Jae Seo546c6352014-08-07 11:57:01 -0700121 mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900122 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900123 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900124 }
125 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900126 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000127 }
128
129 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900130 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000131 synchronized (mLock) {
132 Connection connection = new Connection(info);
133 connection.updateConfigsLocked(configs);
134 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900135 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900136 mHandler.obtainMessage(
137 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900138 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
139 processPendingHdmiDeviceEventsLocked();
140 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000141 }
142 }
143
Wonsik Kimd922a542014-07-24 18:25:29 +0900144 private void buildHardwareListLocked() {
145 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000146 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900147 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000148 }
149 }
150
151 @Override
152 public void onDeviceUnavailable(int deviceId) {
153 synchronized (mLock) {
154 Connection connection = mConnections.get(deviceId);
155 if (connection == null) {
156 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
157 return;
158 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900159 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000160 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900161 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900162 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900163 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700164 // Remove HDMI devices linked with this hardware.
165 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900166 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900167 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700168 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900169 deviceInfo).sendToTarget();
170 it.remove();
171 }
172 }
173 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900174 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900175 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000176 }
177 }
178
179 @Override
180 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
181 synchronized (mLock) {
182 Connection connection = mConnections.get(deviceId);
183 if (connection == null) {
184 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
185 + deviceId);
186 return;
187 }
188 connection.updateConfigsLocked(configs);
189 try {
190 connection.getCallbackLocked().onStreamConfigChanged(configs);
191 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900192 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000193 }
194 }
195 }
196
Terry Heoc086a3d2014-06-18 14:26:44 +0900197 @Override
198 public void onFirstFrameCaptured(int deviceId, int streamId) {
199 synchronized (mLock) {
200 Connection connection = mConnections.get(deviceId);
201 if (connection == null) {
202 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
203 + deviceId);
204 return;
205 }
206 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
207 if (runnable != null) {
208 runnable.run();
209 connection.setOnFirstFrameCapturedLocked(null);
210 }
211 }
212 }
213
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000214 public List<TvInputHardwareInfo> getHardwareList() {
215 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900216 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000217 }
218 }
219
Jae Seo546c6352014-08-07 11:57:01 -0700220 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900221 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700222 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900223 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900224 }
225
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900226 private boolean checkUidChangedLocked(
227 Connection connection, int callingUid, int resolvedUserId) {
228 Integer connectionCallingUid = connection.getCallingUidLocked();
229 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
230 if (connectionCallingUid == null || connectionResolvedUserId == null) {
231 return true;
232 }
233 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
234 return true;
235 }
236 return false;
237 }
238
Wonsik Kim969167d2014-06-24 16:33:17 +0900239 private int convertConnectedToState(boolean connected) {
240 if (connected) {
241 return INPUT_STATE_CONNECTED;
242 } else {
243 return INPUT_STATE_DISCONNECTED;
244 }
245 }
246
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900247 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900248 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900249 String oldInputId = mHardwareInputIdMap.get(deviceId);
250 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900251 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900252 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900253 + info + ":" + deviceId);
254 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900255 mHardwareInputIdMap.put(deviceId, info.getId());
256 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900257
258 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900259 TvInputHardwareInfo hardwareInfo =
260 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
261 if (hardwareInfo == null) {
262 continue;
263 }
264 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900265 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900266 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900267 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
268 inputId).sendToTarget();
269 }
270 }
271 }
272 }
273
Wonsik Kim38feae92014-07-21 21:35:50 +0900274 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
275 for (int i = 0; i < map.size(); ++i) {
276 if (map.valueAt(i).equals(value)) {
277 return i;
278 }
279 }
280 return -1;
281 }
282
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900283 public void addHdmiTvInput(int id, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900284 if (info.getType() != TvInputInfo.TYPE_HDMI) {
285 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
286 }
287 synchronized (mLock) {
288 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900289 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
290 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900291 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
292 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900293 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900294 if (oldInputId != null) {
295 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900296 + mInputMap.get(oldInputId) + ":" + id + ", new = "
297 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900298 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900299 mHdmiInputIdMap.put(id, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900300 mInputMap.put(info.getId(), info);
301 }
302 }
303
304 public void removeTvInput(String inputId) {
305 synchronized (mLock) {
306 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900307 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900308 if (hardwareIndex >= 0) {
309 mHardwareInputIdMap.removeAt(hardwareIndex);
310 }
Jae Seo546c6352014-08-07 11:57:01 -0700311 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
312 if (deviceIndex >= 0) {
313 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900314 }
315 }
316 }
317
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000318 /**
319 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
320 * the object, and if more than one process attempts to create hardware with the same deviceId,
321 * the latest service will get the object and all the other hardware are released. The
322 * release is notified via ITvInputHardwareCallback.onReleased().
323 */
324 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900325 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000326 if (callback == null) {
327 throw new NullPointerException();
328 }
329 synchronized (mLock) {
330 Connection connection = mConnections.get(deviceId);
331 if (connection == null) {
332 Slog.e(TAG, "Invalid deviceId : " + deviceId);
333 return null;
334 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900335 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900336 TvInputHardwareImpl hardware =
337 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000338 try {
339 callback.asBinder().linkToDeath(connection, 0);
340 } catch (RemoteException e) {
341 hardware.release();
342 return null;
343 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900344 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000345 }
346 return connection.getHardwareLocked();
347 }
348 }
349
350 /**
351 * Release the specified hardware.
352 */
353 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
354 int resolvedUserId) {
355 synchronized (mLock) {
356 Connection connection = mConnections.get(deviceId);
357 if (connection == null) {
358 Slog.e(TAG, "Invalid deviceId : " + deviceId);
359 return;
360 }
361 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900362 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000363 return;
364 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900365 connection.resetLocked(null, null, null, null, null);
366 }
367 }
368
Wonsik Kimd922a542014-07-24 18:25:29 +0900369 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
370 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900371 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
372 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900373 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900374 }
375 }
376 return null;
377 }
378
Terry Heoc086a3d2014-06-18 14:26:44 +0900379 private int findDeviceIdForInputIdLocked(String inputId) {
380 for (int i = 0; i < mConnections.size(); ++i) {
381 Connection connection = mConnections.get(i);
382 if (connection.getInfoLocked().getId().equals(inputId)) {
383 return i;
384 }
385 }
386 return -1;
387 }
388
389 /**
390 * Get the list of TvStreamConfig which is buffered mode.
391 */
392 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
393 int resolvedUserId) {
394 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
395 synchronized (mLock) {
396 int deviceId = findDeviceIdForInputIdLocked(inputId);
397 if (deviceId < 0) {
398 Slog.e(TAG, "Invalid inputId : " + inputId);
399 return configsList;
400 }
401 Connection connection = mConnections.get(deviceId);
402 for (TvStreamConfig config : connection.getConfigsLocked()) {
403 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
404 configsList.add(config);
405 }
406 }
407 }
408 return configsList;
409 }
410
411 /**
412 * Take a snapshot of the given TV input into the provided Surface.
413 */
414 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
415 int callingUid, int resolvedUserId) {
416 synchronized (mLock) {
417 int deviceId = findDeviceIdForInputIdLocked(inputId);
418 if (deviceId < 0) {
419 Slog.e(TAG, "Invalid inputId : " + inputId);
420 return false;
421 }
422 Connection connection = mConnections.get(deviceId);
423 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
424 if (hardwareImpl != null) {
425 // Stop previous capture.
426 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
427 if (runnable != null) {
428 runnable.run();
429 connection.setOnFirstFrameCapturedLocked(null);
430 }
431
432 boolean result = hardwareImpl.startCapture(surface, config);
433 if (result) {
434 connection.setOnFirstFrameCapturedLocked(new Runnable() {
435 @Override
436 public void run() {
437 hardwareImpl.stopCapture(config);
438 }
439 });
440 }
441 return result;
442 }
443 }
444 return false;
445 }
446
Wonsik Kimd922a542014-07-24 18:25:29 +0900447 private void processPendingHdmiDeviceEventsLocked() {
448 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
449 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900450 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900451 TvInputHardwareInfo hardwareInfo =
452 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
453 if (hardwareInfo != null) {
454 msg.sendToTarget();
455 it.remove();
456 }
457 }
458 }
459
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000460 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900461 private final TvInputHardwareInfo mHardwareInfo;
462 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000463 private TvInputHardwareImpl mHardware = null;
464 private ITvInputHardwareCallback mCallback;
465 private TvStreamConfig[] mConfigs = null;
466 private Integer mCallingUid = null;
467 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900468 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000469
Wonsik Kim969167d2014-06-24 16:33:17 +0900470 public Connection(TvInputHardwareInfo hardwareInfo) {
471 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000472 }
473
474 // *Locked methods assume TvInputHardwareManager.mLock is held.
475
Wonsik Kim969167d2014-06-24 16:33:17 +0900476 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
477 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000478 if (mHardware != null) {
479 try {
480 mCallback.onReleased();
481 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900482 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000483 }
484 mHardware.release();
485 }
486 mHardware = hardware;
487 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900488 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000489 mCallingUid = callingUid;
490 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900491 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000492
493 if (mHardware != null && mCallback != null) {
494 try {
495 mCallback.onStreamConfigChanged(getConfigsLocked());
496 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900497 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000498 }
499 }
500 }
501
502 public void updateConfigsLocked(TvStreamConfig[] configs) {
503 mConfigs = configs;
504 }
505
Wonsik Kim969167d2014-06-24 16:33:17 +0900506 public TvInputHardwareInfo getHardwareInfoLocked() {
507 return mHardwareInfo;
508 }
509
510 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000511 return mInfo;
512 }
513
514 public ITvInputHardware getHardwareLocked() {
515 return mHardware;
516 }
517
Terry Heoc086a3d2014-06-18 14:26:44 +0900518 public TvInputHardwareImpl getHardwareImplLocked() {
519 return mHardware;
520 }
521
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000522 public ITvInputHardwareCallback getCallbackLocked() {
523 return mCallback;
524 }
525
526 public TvStreamConfig[] getConfigsLocked() {
527 return mConfigs;
528 }
529
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900530 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000531 return mCallingUid;
532 }
533
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900534 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000535 return mResolvedUserId;
536 }
537
Terry Heoc086a3d2014-06-18 14:26:44 +0900538 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
539 mOnFirstFrameCaptured = runnable;
540 }
541
542 public Runnable getOnFirstFrameCapturedLocked() {
543 return mOnFirstFrameCaptured;
544 }
545
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000546 @Override
547 public void binderDied() {
548 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900549 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000550 }
551 }
552 }
553
554 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
555 private final TvInputHardwareInfo mInfo;
556 private boolean mReleased = false;
557 private final Object mImplLock = new Object();
558
Wonsik Kim06c41a32014-08-04 18:57:47 +0900559 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
560 new AudioManager.OnAudioPortUpdateListener() {
561 @Override
562 public void onAudioPortListUpdate(AudioPort[] portList) {
563 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900564 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900565 }
566 }
567
568 @Override
569 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
570 // No-op
571 }
572
573 @Override
574 public void onServiceDied() {
575 synchronized (mImplLock) {
576 mAudioSource = null;
577 mAudioSink = null;
578 mAudioPatch = null;
579 }
580 }
581 };
582 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
583 private String mOverrideAudioAddress = "";
584 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900585 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900586 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900587 private float mCommittedVolume = 0.0f;
588 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900589
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900590 private TvStreamConfig mActiveConfig = null;
591
Wonsik Kimca17a902014-07-31 10:09:46 +0900592 private int mDesiredSamplingRate = 0;
593 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
594 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
595
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000596 public TvInputHardwareImpl(TvInputHardwareInfo info) {
597 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900598 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900599 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900600 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900601 mAudioSink = findAudioSinkFromAudioPolicy();
602 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900603 }
604
605 private AudioDevicePort findAudioSinkFromAudioPolicy() {
606 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
607 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
608 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
609 for (AudioPort port : devicePorts) {
610 AudioDevicePort devicePort = (AudioDevicePort) port;
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900611 if ((devicePort.type() & sinkDevice) != 0) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900612 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900613 }
614 }
615 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900616 return null;
617 }
618
619 private AudioDevicePort findAudioDevicePort(int type, String address) {
620 if (type == AudioManager.DEVICE_NONE) {
621 return null;
622 }
623 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
624 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
625 return null;
626 }
627 for (AudioPort port : devicePorts) {
628 AudioDevicePort devicePort = (AudioDevicePort) port;
629 if (devicePort.type() == type && devicePort.address().equals(address)) {
630 return devicePort;
631 }
632 }
633 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000634 }
635
636 public void release() {
637 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900638 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900639 if (mAudioPatch != null) {
640 mAudioManager.releaseAudioPatch(mAudioPatch);
641 mAudioPatch = null;
642 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000643 mReleased = true;
644 }
645 }
646
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900647 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
648 // attempts to call setSurface with different TvStreamConfig objects, the last call will
649 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000650 @Override
651 public boolean setSurface(Surface surface, TvStreamConfig config)
652 throws RemoteException {
653 synchronized (mImplLock) {
654 if (mReleased) {
655 throw new IllegalStateException("Device already released.");
656 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900657 if (surface != null && config == null) {
658 return false;
659 }
660 if (surface == null && mActiveConfig == null) {
661 return false;
662 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900663
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900664 int result = TvInputHal.ERROR_UNKNOWN;
665 if (surface == null) {
666 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
667 mActiveConfig = null;
668 } else {
669 if (config != mActiveConfig && mActiveConfig != null) {
670 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
671 if (result != TvInputHal.SUCCESS) {
672 mActiveConfig = null;
673 return false;
674 }
675 }
676 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
677 if (result == TvInputHal.SUCCESS) {
678 mActiveConfig = config;
679 }
680 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900681 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900682 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000683 }
684 }
685
Wonsik Kim7587d292014-08-09 10:01:01 +0900686 /**
687 * Update audio configuration (source, sink, patch) all up to current state.
688 */
689 private void updateAudioConfigLocked() {
690 boolean sinkUpdated = updateAudioSinkLocked();
691 boolean sourceUpdated = updateAudioSourceLocked();
692 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
693 // because Java won't evaluate the latter if the former is true.
694
695 if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900696 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900697 mAudioManager.releaseAudioPatch(mAudioPatch);
698 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900699 }
700 return;
701 }
702
Wonsik Kim8e45a332014-08-04 10:17:18 +0900703 AudioGainConfig sourceGainConfig = null;
704 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
705 AudioGain sourceGain = null;
706 for (AudioGain gain : mAudioSource.gains()) {
707 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
708 sourceGain = gain;
709 break;
710 }
711 }
712 // NOTE: we only change the source gain in MODE_JOINT here.
713 if (sourceGain != null) {
714 int steps = (sourceGain.maxValue() - sourceGain.minValue())
715 / sourceGain.stepValue();
716 int gainValue = sourceGain.minValue();
717 if (mVolume < 1.0f) {
718 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
719 } else {
720 gainValue = sourceGain.maxValue();
721 }
722 int numChannels = 0;
723 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
724 numChannels += (mask & 1);
725 }
726 int[] gainValues = new int[numChannels];
727 Arrays.fill(gainValues, gainValue);
728 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
729 sourceGain.channelMask(), gainValues, 0);
730 } else {
731 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
732 }
733 }
734
Wonsik Kimca17a902014-07-31 10:09:46 +0900735 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
736 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
737 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900738 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kimca17a902014-07-31 10:09:46 +0900739 if (sinkConfig == null
740 || (mDesiredSamplingRate != 0
741 && sinkConfig.samplingRate() != mDesiredSamplingRate)
742 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
743 && sinkConfig.channelMask() != mDesiredChannelMask)
744 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
745 && sinkConfig.format() != mDesiredFormat)) {
746 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
747 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900748 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900749 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900750 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900751 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900752 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
753 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900754 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900755 if (shouldRecreateAudioPatch) {
756 mCommittedVolume = mVolume;
757 mAudioManager.createAudioPatch(
758 audioPatchArray,
759 new AudioPortConfig[] { sourceConfig },
760 new AudioPortConfig[] { sinkConfig });
761 mAudioPatch = audioPatchArray[0];
762 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900763 }
764
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000765 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900766 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000767 synchronized (mImplLock) {
768 if (mReleased) {
769 throw new IllegalStateException("Device already released.");
770 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900771 mVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +0900772 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000773 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000774 }
775
776 @Override
777 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
778 synchronized (mImplLock) {
779 if (mReleased) {
780 throw new IllegalStateException("Device already released.");
781 }
782 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900783 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000784 return false;
785 }
786 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
787 return false;
788 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900789
790 private boolean startCapture(Surface surface, TvStreamConfig config) {
791 synchronized (mImplLock) {
792 if (mReleased) {
793 return false;
794 }
795 if (surface == null || config == null) {
796 return false;
797 }
798 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
799 return false;
800 }
801
802 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
803 return result == TvInputHal.SUCCESS;
804 }
805 }
806
807 private boolean stopCapture(TvStreamConfig config) {
808 synchronized (mImplLock) {
809 if (mReleased) {
810 return false;
811 }
812 if (config == null) {
813 return false;
814 }
815
816 int result = mHal.removeStream(mInfo.getDeviceId(), config);
817 return result == TvInputHal.SUCCESS;
818 }
819 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900820
Wonsik Kim7587d292014-08-09 10:01:01 +0900821 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900822 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900823 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900824 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900825 AudioDevicePort previousSource = mAudioSource;
826 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
827 return mAudioSource == null ? (previousSource != null)
828 : !mAudioSource.equals(previousSource);
829 }
830
831 private boolean updateAudioSinkLocked() {
832 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
833 return false;
834 }
835 AudioDevicePort previousSink = mAudioSink;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900836 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
837 mAudioSink = findAudioSinkFromAudioPolicy();
838 } else {
839 AudioDevicePort audioSink =
840 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
841 if (audioSink != null) {
842 mAudioSink = audioSink;
843 }
844 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900845 return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +0900846 }
847
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900848 private void handleAudioSinkUpdated() {
849 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900850 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900851 }
852 }
853
Wonsik Kimca17a902014-07-31 10:09:46 +0900854 @Override
855 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
856 int channelMask, int format) {
857 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900858 mOverrideAudioType = audioType;
859 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900860
Wonsik Kimca17a902014-07-31 10:09:46 +0900861 mDesiredSamplingRate = samplingRate;
862 mDesiredChannelMask = channelMask;
863 mDesiredFormat = format;
864
Wonsik Kim7587d292014-08-09 10:01:01 +0900865 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +0900866 }
867 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000868 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900869
Wonsik Kim187423c2014-06-25 14:12:48 +0900870 interface Listener {
871 public void onStateChanged(String inputId, int state);
872 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900873 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700874 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
875 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Wonsik Kime92f8572014-08-12 18:30:24 +0900876 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900877 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900878
Wonsik Kim187423c2014-06-25 14:12:48 +0900879 private class ListenerHandler extends Handler {
880 private static final int STATE_CHANGED = 1;
881 private static final int HARDWARE_DEVICE_ADDED = 2;
882 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700883 private static final int HDMI_DEVICE_ADDED = 4;
884 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900885 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +0900886
887 @Override
888 public final void handleMessage(Message msg) {
889 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900890 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900891 String inputId = (String) msg.obj;
892 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900893 mListener.onStateChanged(inputId, state);
894 break;
895 }
896 case HARDWARE_DEVICE_ADDED: {
897 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
898 mListener.onHardwareDeviceAdded(info);
899 break;
900 }
901 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900902 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
903 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900904 break;
905 }
Jae Seo546c6352014-08-07 11:57:01 -0700906 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900907 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700908 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900909 break;
910 }
Jae Seo546c6352014-08-07 11:57:01 -0700911 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900912 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700913 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900914 break;
915 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900916 case HDMI_DEVICE_UPDATED: {
Wonsik Kime92f8572014-08-12 18:30:24 +0900917 SomeArgs args = (SomeArgs) msg.obj;
918 String inputId = (String) args.arg1;
919 HdmiDeviceInfo info = (HdmiDeviceInfo) args.arg2;
920 args.recycle();
921 mListener.onHdmiDeviceUpdated(inputId, info);
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900922 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900923 default: {
924 Slog.w(TAG, "Unhandled message: " + msg);
925 break;
926 }
927 }
928 }
929 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900930
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900931 // Listener implementations for HdmiControlService
932
933 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
934 @Override
935 public void onReceived(HdmiHotplugEvent event) {
936 synchronized (mLock) {
937 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900938 TvInputHardwareInfo hardwareInfo =
939 findHardwareInfoForHdmiPortLocked(event.getPort());
940 if (hardwareInfo == null) {
941 return;
942 }
943 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900944 if (inputId == null) {
945 return;
946 }
947 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
948 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
949 }
950 }
951 }
952
Wonsik Kim187423c2014-06-25 14:12:48 +0900953 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
954 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900955 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900956 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900957 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +0900958 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900959 switch (status) {
960 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +0900961 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900962 mHdmiDeviceList.add(deviceInfo);
963 } else {
964 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
965 return;
966 }
967 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +0900968 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900969 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900970 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900971 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +0900972 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
973 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900974 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
975 return;
976 }
977 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +0900978 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900979 break;
980 }
981 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +0900982 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
983 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900984 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
985 return;
986 }
987 mHdmiDeviceList.add(deviceInfo);
988 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Jungshik Jangcf445872014-08-20 15:06:01 +0900989 String inputId = mHdmiInputIdMap.get(deviceInfo.getId());
Wonsik Kime92f8572014-08-12 18:30:24 +0900990 SomeArgs args = SomeArgs.obtain();
991 args.arg1 = inputId;
992 args.arg2 = deviceInfo;
993 obj = args;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900994 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900995 }
996 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900997
Wonsik Kime92f8572014-08-12 18:30:24 +0900998 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +0900999 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1000 msg.sendToTarget();
1001 } else {
1002 mPendingHdmiDeviceEvents.add(msg);
1003 }
1004 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001005 }
Jungshik Jangcf445872014-08-20 15:06:01 +09001006
1007 private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1008 for (HdmiDeviceInfo info : mHdmiDeviceList) {
1009 if (info.getId() == id) {
1010 return info;
1011 }
1012 }
1013 return null;
1014 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001015 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001016
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001017 private final class HdmiSystemAudioModeChangeListener extends
1018 IHdmiSystemAudioModeChangeListener.Stub {
1019 @Override
1020 public void onStatusChanged(boolean enabled) throws RemoteException {
1021 synchronized (mLock) {
1022 for (int i = 0; i < mConnections.size(); ++i) {
1023 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
Jungshik Jang44bb3a52014-08-26 17:24:24 +09001024 if (impl != null) {
1025 impl.handleAudioSinkUpdated();
1026 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001027 }
1028 }
1029 }
1030 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001031}