blob: 1a08559b022e7d4dd874123466fd21811d62b9e9 [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
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900287 public void addHdmiTvInput(int id, 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 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900297 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900298 if (oldInputId != null) {
299 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900300 + mInputMap.get(oldInputId) + ":" + id + ", new = "
301 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900302 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900303 mHdmiInputIdMap.put(id, 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) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900568 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900569 }
570 }
571
572 @Override
573 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
574 // No-op
575 }
576
577 @Override
578 public void onServiceDied() {
579 synchronized (mImplLock) {
580 mAudioSource = null;
581 mAudioSink = null;
582 mAudioPatch = null;
583 }
584 }
585 };
586 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
587 private String mOverrideAudioAddress = "";
588 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900589 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900590 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900591 private float mCommittedVolume = 0.0f;
592 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900593
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900594 private TvStreamConfig mActiveConfig = null;
595
Wonsik Kimca17a902014-07-31 10:09:46 +0900596 private int mDesiredSamplingRate = 0;
597 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
598 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
599
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000600 public TvInputHardwareImpl(TvInputHardwareInfo info) {
601 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900602 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900603 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900604 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900605 mAudioSink = findAudioSinkFromAudioPolicy();
606 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900607 }
608
609 private AudioDevicePort findAudioSinkFromAudioPolicy() {
610 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
611 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
612 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
613 for (AudioPort port : devicePorts) {
614 AudioDevicePort devicePort = (AudioDevicePort) port;
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900615 if ((devicePort.type() & sinkDevice) != 0) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900616 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900617 }
618 }
619 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900620 return null;
621 }
622
623 private AudioDevicePort findAudioDevicePort(int type, String address) {
624 if (type == AudioManager.DEVICE_NONE) {
625 return null;
626 }
627 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
628 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
629 return null;
630 }
631 for (AudioPort port : devicePorts) {
632 AudioDevicePort devicePort = (AudioDevicePort) port;
633 if (devicePort.type() == type && devicePort.address().equals(address)) {
634 return devicePort;
635 }
636 }
637 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000638 }
639
640 public void release() {
641 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900642 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900643 if (mAudioPatch != null) {
644 mAudioManager.releaseAudioPatch(mAudioPatch);
645 mAudioPatch = null;
646 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000647 mReleased = true;
648 }
649 }
650
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900651 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
652 // attempts to call setSurface with different TvStreamConfig objects, the last call will
653 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000654 @Override
655 public boolean setSurface(Surface surface, TvStreamConfig config)
656 throws RemoteException {
657 synchronized (mImplLock) {
658 if (mReleased) {
659 throw new IllegalStateException("Device already released.");
660 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900661 if (surface != null && config == null) {
662 return false;
663 }
664 if (surface == null && mActiveConfig == null) {
665 return false;
666 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900667
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900668 int result = TvInputHal.ERROR_UNKNOWN;
669 if (surface == null) {
670 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
671 mActiveConfig = null;
672 } else {
673 if (config != mActiveConfig && mActiveConfig != null) {
674 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
675 if (result != TvInputHal.SUCCESS) {
676 mActiveConfig = null;
677 return false;
678 }
679 }
680 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
681 if (result == TvInputHal.SUCCESS) {
682 mActiveConfig = config;
683 }
684 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900685 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900686 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000687 }
688 }
689
Wonsik Kim7587d292014-08-09 10:01:01 +0900690 /**
691 * Update audio configuration (source, sink, patch) all up to current state.
692 */
693 private void updateAudioConfigLocked() {
694 boolean sinkUpdated = updateAudioSinkLocked();
695 boolean sourceUpdated = updateAudioSourceLocked();
696 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
697 // because Java won't evaluate the latter if the former is true.
698
699 if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900700 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900701 mAudioManager.releaseAudioPatch(mAudioPatch);
702 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900703 }
704 return;
705 }
706
Wonsik Kim8e45a332014-08-04 10:17:18 +0900707 AudioGainConfig sourceGainConfig = null;
708 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
709 AudioGain sourceGain = null;
710 for (AudioGain gain : mAudioSource.gains()) {
711 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
712 sourceGain = gain;
713 break;
714 }
715 }
716 // NOTE: we only change the source gain in MODE_JOINT here.
717 if (sourceGain != null) {
718 int steps = (sourceGain.maxValue() - sourceGain.minValue())
719 / sourceGain.stepValue();
720 int gainValue = sourceGain.minValue();
721 if (mVolume < 1.0f) {
722 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
723 } else {
724 gainValue = sourceGain.maxValue();
725 }
726 int numChannels = 0;
727 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
728 numChannels += (mask & 1);
729 }
730 int[] gainValues = new int[numChannels];
731 Arrays.fill(gainValues, gainValue);
732 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
733 sourceGain.channelMask(), gainValues, 0);
734 } else {
735 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
736 }
737 }
738
Wonsik Kimca17a902014-07-31 10:09:46 +0900739 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
740 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
741 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900742 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kimca17a902014-07-31 10:09:46 +0900743 if (sinkConfig == null
744 || (mDesiredSamplingRate != 0
745 && sinkConfig.samplingRate() != mDesiredSamplingRate)
746 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
747 && sinkConfig.channelMask() != mDesiredChannelMask)
748 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
749 && sinkConfig.format() != mDesiredFormat)) {
750 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
751 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900752 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900753 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900754 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900755 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900756 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
757 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900758 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900759 if (shouldRecreateAudioPatch) {
760 mCommittedVolume = mVolume;
761 mAudioManager.createAudioPatch(
762 audioPatchArray,
763 new AudioPortConfig[] { sourceConfig },
764 new AudioPortConfig[] { sinkConfig });
765 mAudioPatch = audioPatchArray[0];
766 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900767 }
768
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000769 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900770 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000771 synchronized (mImplLock) {
772 if (mReleased) {
773 throw new IllegalStateException("Device already released.");
774 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900775 mVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +0900776 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000777 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000778 }
779
780 @Override
781 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
782 synchronized (mImplLock) {
783 if (mReleased) {
784 throw new IllegalStateException("Device already released.");
785 }
786 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900787 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000788 return false;
789 }
790 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
791 return false;
792 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900793
794 private boolean startCapture(Surface surface, TvStreamConfig config) {
795 synchronized (mImplLock) {
796 if (mReleased) {
797 return false;
798 }
799 if (surface == null || config == null) {
800 return false;
801 }
802 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
803 return false;
804 }
805
806 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
807 return result == TvInputHal.SUCCESS;
808 }
809 }
810
811 private boolean stopCapture(TvStreamConfig config) {
812 synchronized (mImplLock) {
813 if (mReleased) {
814 return false;
815 }
816 if (config == null) {
817 return false;
818 }
819
820 int result = mHal.removeStream(mInfo.getDeviceId(), config);
821 return result == TvInputHal.SUCCESS;
822 }
823 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900824
Wonsik Kim7587d292014-08-09 10:01:01 +0900825 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900826 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900827 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900828 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900829 AudioDevicePort previousSource = mAudioSource;
830 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
831 return mAudioSource == null ? (previousSource != null)
832 : !mAudioSource.equals(previousSource);
833 }
834
835 private boolean updateAudioSinkLocked() {
836 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
837 return false;
838 }
839 AudioDevicePort previousSink = mAudioSink;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900840 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
841 mAudioSink = findAudioSinkFromAudioPolicy();
842 } else {
843 AudioDevicePort audioSink =
844 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
845 if (audioSink != null) {
846 mAudioSink = audioSink;
847 }
848 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900849 return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +0900850 }
851
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900852 private void handleAudioSinkUpdated() {
853 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900854 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900855 }
856 }
857
Wonsik Kimca17a902014-07-31 10:09:46 +0900858 @Override
859 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
860 int channelMask, int format) {
861 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900862 mOverrideAudioType = audioType;
863 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900864
Wonsik Kimca17a902014-07-31 10:09:46 +0900865 mDesiredSamplingRate = samplingRate;
866 mDesiredChannelMask = channelMask;
867 mDesiredFormat = format;
868
Wonsik Kim7587d292014-08-09 10:01:01 +0900869 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +0900870 }
871 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000872 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900873
Wonsik Kim187423c2014-06-25 14:12:48 +0900874 interface Listener {
875 public void onStateChanged(String inputId, int state);
876 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900877 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700878 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
879 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900880 public void onHdmiDeviceUpdated(HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900881 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900882
Wonsik Kim187423c2014-06-25 14:12:48 +0900883 private class ListenerHandler extends Handler {
884 private static final int STATE_CHANGED = 1;
885 private static final int HARDWARE_DEVICE_ADDED = 2;
886 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700887 private static final int HDMI_DEVICE_ADDED = 4;
888 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900889 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +0900890
891 @Override
892 public final void handleMessage(Message msg) {
893 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900894 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900895 String inputId = (String) msg.obj;
896 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900897 mListener.onStateChanged(inputId, state);
898 break;
899 }
900 case HARDWARE_DEVICE_ADDED: {
901 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
902 mListener.onHardwareDeviceAdded(info);
903 break;
904 }
905 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900906 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
907 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900908 break;
909 }
Jae Seo546c6352014-08-07 11:57:01 -0700910 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900911 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700912 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900913 break;
914 }
Jae Seo546c6352014-08-07 11:57:01 -0700915 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900916 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700917 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900918 break;
919 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900920 case HDMI_DEVICE_UPDATED: {
921 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
922 mListener.onHdmiDeviceUpdated(info);
923 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900924 default: {
925 Slog.w(TAG, "Unhandled message: " + msg);
926 break;
927 }
928 }
929 }
930 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900931
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900932 // Listener implementations for HdmiControlService
933
934 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
935 @Override
936 public void onReceived(HdmiHotplugEvent event) {
937 synchronized (mLock) {
938 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900939 TvInputHardwareInfo hardwareInfo =
940 findHardwareInfoForHdmiPortLocked(event.getPort());
941 if (hardwareInfo == null) {
942 return;
943 }
944 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900945 if (inputId == null) {
946 return;
947 }
948 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
949 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
950 }
951 }
952 }
953
Wonsik Kim187423c2014-06-25 14:12:48 +0900954 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
955 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900956 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900957 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900958 int messageType = 0;
959 switch (status) {
960 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
961 if (!mHdmiDeviceList.contains(deviceInfo)) {
962 mHdmiDeviceList.add(deviceInfo);
963 } else {
964 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
965 return;
966 }
967 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
968 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900969 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900970 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
971 if (!mHdmiDeviceList.remove(deviceInfo)) {
972 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
973 return;
974 }
975 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
976 break;
977 }
978 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
979 if (!mHdmiDeviceList.remove(deviceInfo)) {
980 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
981 return;
982 }
983 mHdmiDeviceList.add(deviceInfo);
984 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
985 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900986 }
987 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900988
989 Message msg = mHandler.obtainMessage(messageType, 0, 0, deviceInfo);
Wonsik Kimd922a542014-07-24 18:25:29 +0900990 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
991 msg.sendToTarget();
992 } else {
993 mPendingHdmiDeviceEvents.add(msg);
994 }
995 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900996 }
997 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900998
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900999 private final class HdmiSystemAudioModeChangeListener extends
1000 IHdmiSystemAudioModeChangeListener.Stub {
1001 @Override
1002 public void onStatusChanged(boolean enabled) throws RemoteException {
1003 synchronized (mLock) {
1004 for (int i = 0; i < mConnections.size(); ++i) {
1005 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
1006 impl.handleAudioSinkUpdated();
1007 }
1008 }
1009 }
1010 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001011}