blob: 78ea6c9da508cdef943cfa7f6d6d504d971b9503 [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 Kime92f8572014-08-12 18:30:24 +090058import com.android.internal.os.SomeArgs;
Wonsik Kim969167d2014-06-24 16:33:17 +090059import com.android.server.SystemService;
60
Wonsik Kimc22dbb62014-05-26 02:26:04 +000061import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090062import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090063import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090064import java.util.Iterator;
65import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000066import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090067import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000068
69/**
70 * A helper class for TvInputManagerService to handle TV input hardware.
71 *
72 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
73 * calls to tv_input HAL module.
74 *
75 * @hide
76 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090077class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000078 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090079
80 private final Context mContext;
81 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000082 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090083 private final SparseArray<Connection> mConnections = new SparseArray<>();
84 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070085 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090086 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090087 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090088 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070089 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090090 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090091
Wonsik Kimd7c29182014-05-27 10:38:21 +090092 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090093 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090094 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
95 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090096 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +090097 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
98 new HdmiSystemAudioModeChangeListener();
Jinsuk Kimd38bf472014-08-11 11:29:18 +090099
Wonsik Kimd922a542014-07-24 18:25:29 +0900100 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900101 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900102 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900103
Wonsik Kim187423c2014-06-25 14:12:48 +0900104 // Calls to mListener should happen here.
105 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000106
107 private final Object mLock = new Object();
108
Wonsik Kim187423c2014-06-25 14:12:48 +0900109 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000110 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900111 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900112 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000113 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900114 }
115
116 public void onBootPhase(int phase) {
117 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900118 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
119 Context.HDMI_CONTROL_SERVICE));
120 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900121 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900122 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
123 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900124 mHdmiControlService.addSystemAudioModeChangeListener(
125 mHdmiSystemAudioModeChangeListener);
Jae Seo546c6352014-08-07 11:57:01 -0700126 mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900127 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900128 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900129 }
130 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900131 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000132 }
133
134 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900135 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000136 synchronized (mLock) {
137 Connection connection = new Connection(info);
138 connection.updateConfigsLocked(configs);
139 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900140 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900141 mHandler.obtainMessage(
142 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900143 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
144 processPendingHdmiDeviceEventsLocked();
145 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000146 }
147 }
148
Wonsik Kimd922a542014-07-24 18:25:29 +0900149 private void buildHardwareListLocked() {
150 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000151 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900152 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000153 }
154 }
155
156 @Override
157 public void onDeviceUnavailable(int deviceId) {
158 synchronized (mLock) {
159 Connection connection = mConnections.get(deviceId);
160 if (connection == null) {
161 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
162 return;
163 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900164 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000165 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900166 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900167 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900168 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700169 // Remove HDMI devices linked with this hardware.
170 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900171 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900172 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700173 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900174 deviceInfo).sendToTarget();
175 it.remove();
176 }
177 }
178 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900179 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900180 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000181 }
182 }
183
184 @Override
185 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
186 synchronized (mLock) {
187 Connection connection = mConnections.get(deviceId);
188 if (connection == null) {
189 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
190 + deviceId);
191 return;
192 }
193 connection.updateConfigsLocked(configs);
194 try {
195 connection.getCallbackLocked().onStreamConfigChanged(configs);
196 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900197 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000198 }
199 }
200 }
201
Terry Heoc086a3d2014-06-18 14:26:44 +0900202 @Override
203 public void onFirstFrameCaptured(int deviceId, int streamId) {
204 synchronized (mLock) {
205 Connection connection = mConnections.get(deviceId);
206 if (connection == null) {
207 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
208 + deviceId);
209 return;
210 }
211 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
212 if (runnable != null) {
213 runnable.run();
214 connection.setOnFirstFrameCapturedLocked(null);
215 }
216 }
217 }
218
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000219 public List<TvInputHardwareInfo> getHardwareList() {
220 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900221 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000222 }
223 }
224
Jae Seo546c6352014-08-07 11:57:01 -0700225 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900226 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700227 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900228 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900229 }
230
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900231 private boolean checkUidChangedLocked(
232 Connection connection, int callingUid, int resolvedUserId) {
233 Integer connectionCallingUid = connection.getCallingUidLocked();
234 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
235 if (connectionCallingUid == null || connectionResolvedUserId == null) {
236 return true;
237 }
238 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
239 return true;
240 }
241 return false;
242 }
243
Wonsik Kim969167d2014-06-24 16:33:17 +0900244 private int convertConnectedToState(boolean connected) {
245 if (connected) {
246 return INPUT_STATE_CONNECTED;
247 } else {
248 return INPUT_STATE_DISCONNECTED;
249 }
250 }
251
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900252 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900253 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900254 String oldInputId = mHardwareInputIdMap.get(deviceId);
255 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900256 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900257 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900258 + info + ":" + deviceId);
259 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900260 mHardwareInputIdMap.put(deviceId, info.getId());
261 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900262
263 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900264 TvInputHardwareInfo hardwareInfo =
265 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
266 if (hardwareInfo == null) {
267 continue;
268 }
269 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900270 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900271 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900272 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
273 inputId).sendToTarget();
274 }
275 }
276 }
277 }
278
Wonsik Kim38feae92014-07-21 21:35:50 +0900279 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
280 for (int i = 0; i < map.size(); ++i) {
281 if (map.valueAt(i).equals(value)) {
282 return i;
283 }
284 }
285 return -1;
286 }
287
Jae Seo546c6352014-08-07 11:57:01 -0700288 public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900289 if (info.getType() != TvInputInfo.TYPE_HDMI) {
290 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
291 }
292 synchronized (mLock) {
293 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900294 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
295 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900296 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
297 }
Jae Seo546c6352014-08-07 11:57:01 -0700298 String oldInputId = mHdmiInputIdMap.get(logicalAddress);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900299 if (oldInputId != null) {
300 Slog.w(TAG, "Trying to override previous registration: old = "
301 + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
302 + info + ":" + logicalAddress);
303 }
Jae Seo546c6352014-08-07 11:57:01 -0700304 mHdmiInputIdMap.put(logicalAddress, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900305 mInputMap.put(info.getId(), info);
306 }
307 }
308
309 public void removeTvInput(String inputId) {
310 synchronized (mLock) {
311 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900312 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900313 if (hardwareIndex >= 0) {
314 mHardwareInputIdMap.removeAt(hardwareIndex);
315 }
Jae Seo546c6352014-08-07 11:57:01 -0700316 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
317 if (deviceIndex >= 0) {
318 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900319 }
320 }
321 }
322
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000323 /**
324 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
325 * the object, and if more than one process attempts to create hardware with the same deviceId,
326 * the latest service will get the object and all the other hardware are released. The
327 * release is notified via ITvInputHardwareCallback.onReleased().
328 */
329 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900330 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000331 if (callback == null) {
332 throw new NullPointerException();
333 }
334 synchronized (mLock) {
335 Connection connection = mConnections.get(deviceId);
336 if (connection == null) {
337 Slog.e(TAG, "Invalid deviceId : " + deviceId);
338 return null;
339 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900340 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900341 TvInputHardwareImpl hardware =
342 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000343 try {
344 callback.asBinder().linkToDeath(connection, 0);
345 } catch (RemoteException e) {
346 hardware.release();
347 return null;
348 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900349 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000350 }
351 return connection.getHardwareLocked();
352 }
353 }
354
355 /**
356 * Release the specified hardware.
357 */
358 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
359 int resolvedUserId) {
360 synchronized (mLock) {
361 Connection connection = mConnections.get(deviceId);
362 if (connection == null) {
363 Slog.e(TAG, "Invalid deviceId : " + deviceId);
364 return;
365 }
366 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900367 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000368 return;
369 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900370 connection.resetLocked(null, null, null, null, null);
371 }
372 }
373
Wonsik Kimd922a542014-07-24 18:25:29 +0900374 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
375 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900376 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
377 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900378 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900379 }
380 }
381 return null;
382 }
383
Terry Heoc086a3d2014-06-18 14:26:44 +0900384 private int findDeviceIdForInputIdLocked(String inputId) {
385 for (int i = 0; i < mConnections.size(); ++i) {
386 Connection connection = mConnections.get(i);
387 if (connection.getInfoLocked().getId().equals(inputId)) {
388 return i;
389 }
390 }
391 return -1;
392 }
393
394 /**
395 * Get the list of TvStreamConfig which is buffered mode.
396 */
397 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
398 int resolvedUserId) {
399 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
400 synchronized (mLock) {
401 int deviceId = findDeviceIdForInputIdLocked(inputId);
402 if (deviceId < 0) {
403 Slog.e(TAG, "Invalid inputId : " + inputId);
404 return configsList;
405 }
406 Connection connection = mConnections.get(deviceId);
407 for (TvStreamConfig config : connection.getConfigsLocked()) {
408 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
409 configsList.add(config);
410 }
411 }
412 }
413 return configsList;
414 }
415
416 /**
417 * Take a snapshot of the given TV input into the provided Surface.
418 */
419 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
420 int callingUid, int resolvedUserId) {
421 synchronized (mLock) {
422 int deviceId = findDeviceIdForInputIdLocked(inputId);
423 if (deviceId < 0) {
424 Slog.e(TAG, "Invalid inputId : " + inputId);
425 return false;
426 }
427 Connection connection = mConnections.get(deviceId);
428 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
429 if (hardwareImpl != null) {
430 // Stop previous capture.
431 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
432 if (runnable != null) {
433 runnable.run();
434 connection.setOnFirstFrameCapturedLocked(null);
435 }
436
437 boolean result = hardwareImpl.startCapture(surface, config);
438 if (result) {
439 connection.setOnFirstFrameCapturedLocked(new Runnable() {
440 @Override
441 public void run() {
442 hardwareImpl.stopCapture(config);
443 }
444 });
445 }
446 return result;
447 }
448 }
449 return false;
450 }
451
Wonsik Kimd922a542014-07-24 18:25:29 +0900452 private void processPendingHdmiDeviceEventsLocked() {
453 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
454 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900455 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900456 TvInputHardwareInfo hardwareInfo =
457 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
458 if (hardwareInfo != null) {
459 msg.sendToTarget();
460 it.remove();
461 }
462 }
463 }
464
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000465 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900466 private final TvInputHardwareInfo mHardwareInfo;
467 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000468 private TvInputHardwareImpl mHardware = null;
469 private ITvInputHardwareCallback mCallback;
470 private TvStreamConfig[] mConfigs = null;
471 private Integer mCallingUid = null;
472 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900473 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000474
Wonsik Kim969167d2014-06-24 16:33:17 +0900475 public Connection(TvInputHardwareInfo hardwareInfo) {
476 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000477 }
478
479 // *Locked methods assume TvInputHardwareManager.mLock is held.
480
Wonsik Kim969167d2014-06-24 16:33:17 +0900481 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
482 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000483 if (mHardware != null) {
484 try {
485 mCallback.onReleased();
486 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900487 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000488 }
489 mHardware.release();
490 }
491 mHardware = hardware;
492 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900493 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000494 mCallingUid = callingUid;
495 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900496 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000497
498 if (mHardware != null && mCallback != null) {
499 try {
500 mCallback.onStreamConfigChanged(getConfigsLocked());
501 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900502 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000503 }
504 }
505 }
506
507 public void updateConfigsLocked(TvStreamConfig[] configs) {
508 mConfigs = configs;
509 }
510
Wonsik Kim969167d2014-06-24 16:33:17 +0900511 public TvInputHardwareInfo getHardwareInfoLocked() {
512 return mHardwareInfo;
513 }
514
515 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000516 return mInfo;
517 }
518
519 public ITvInputHardware getHardwareLocked() {
520 return mHardware;
521 }
522
Terry Heoc086a3d2014-06-18 14:26:44 +0900523 public TvInputHardwareImpl getHardwareImplLocked() {
524 return mHardware;
525 }
526
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000527 public ITvInputHardwareCallback getCallbackLocked() {
528 return mCallback;
529 }
530
531 public TvStreamConfig[] getConfigsLocked() {
532 return mConfigs;
533 }
534
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900535 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000536 return mCallingUid;
537 }
538
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900539 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000540 return mResolvedUserId;
541 }
542
Terry Heoc086a3d2014-06-18 14:26:44 +0900543 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
544 mOnFirstFrameCaptured = runnable;
545 }
546
547 public Runnable getOnFirstFrameCapturedLocked() {
548 return mOnFirstFrameCaptured;
549 }
550
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000551 @Override
552 public void binderDied() {
553 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900554 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000555 }
556 }
557 }
558
559 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
560 private final TvInputHardwareInfo mInfo;
561 private boolean mReleased = false;
562 private final Object mImplLock = new Object();
563
Wonsik Kim06c41a32014-08-04 18:57:47 +0900564 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
565 new AudioManager.OnAudioPortUpdateListener() {
566 @Override
567 public void onAudioPortListUpdate(AudioPort[] portList) {
568 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900569 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900570 }
571 }
572
573 @Override
574 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
575 // No-op
576 }
577
578 @Override
579 public void onServiceDied() {
580 synchronized (mImplLock) {
581 mAudioSource = null;
582 mAudioSink = null;
583 mAudioPatch = null;
584 }
585 }
586 };
587 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
588 private String mOverrideAudioAddress = "";
589 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900590 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900591 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900592 private float mCommittedVolume = 0.0f;
593 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900594
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900595 private TvStreamConfig mActiveConfig = null;
596
Wonsik Kimca17a902014-07-31 10:09:46 +0900597 private int mDesiredSamplingRate = 0;
598 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
599 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
600
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000601 public TvInputHardwareImpl(TvInputHardwareInfo info) {
602 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900603 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900604 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900605 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900606 mAudioSink = findAudioSinkFromAudioPolicy();
607 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900608 }
609
610 private AudioDevicePort findAudioSinkFromAudioPolicy() {
611 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
612 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
613 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
614 for (AudioPort port : devicePorts) {
615 AudioDevicePort devicePort = (AudioDevicePort) port;
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900616 if ((devicePort.type() & sinkDevice) != 0) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900617 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900618 }
619 }
620 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900621 return null;
622 }
623
624 private AudioDevicePort findAudioDevicePort(int type, String address) {
625 if (type == AudioManager.DEVICE_NONE) {
626 return null;
627 }
628 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
629 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
630 return null;
631 }
632 for (AudioPort port : devicePorts) {
633 AudioDevicePort devicePort = (AudioDevicePort) port;
634 if (devicePort.type() == type && devicePort.address().equals(address)) {
635 return devicePort;
636 }
637 }
638 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000639 }
640
641 public void release() {
642 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900643 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900644 if (mAudioPatch != null) {
645 mAudioManager.releaseAudioPatch(mAudioPatch);
646 mAudioPatch = null;
647 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000648 mReleased = true;
649 }
650 }
651
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900652 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
653 // attempts to call setSurface with different TvStreamConfig objects, the last call will
654 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000655 @Override
656 public boolean setSurface(Surface surface, TvStreamConfig config)
657 throws RemoteException {
658 synchronized (mImplLock) {
659 if (mReleased) {
660 throw new IllegalStateException("Device already released.");
661 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900662 if (surface != null && config == null) {
663 return false;
664 }
665 if (surface == null && mActiveConfig == null) {
666 return false;
667 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900668
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900669 int result = TvInputHal.ERROR_UNKNOWN;
670 if (surface == null) {
671 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
672 mActiveConfig = null;
673 } else {
674 if (config != mActiveConfig && mActiveConfig != null) {
675 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
676 if (result != TvInputHal.SUCCESS) {
677 mActiveConfig = null;
678 return false;
679 }
680 }
681 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
682 if (result == TvInputHal.SUCCESS) {
683 mActiveConfig = config;
684 }
685 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900686 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900687 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000688 }
689 }
690
Wonsik Kim7587d292014-08-09 10:01:01 +0900691 /**
692 * Update audio configuration (source, sink, patch) all up to current state.
693 */
694 private void updateAudioConfigLocked() {
695 boolean sinkUpdated = updateAudioSinkLocked();
696 boolean sourceUpdated = updateAudioSourceLocked();
697 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
698 // because Java won't evaluate the latter if the former is true.
699
700 if (mAudioSource == null || mAudioSink == null || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900701 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900702 mAudioManager.releaseAudioPatch(mAudioPatch);
703 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900704 }
705 return;
706 }
707
Wonsik Kim8e45a332014-08-04 10:17:18 +0900708 AudioGainConfig sourceGainConfig = null;
709 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
710 AudioGain sourceGain = null;
711 for (AudioGain gain : mAudioSource.gains()) {
712 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
713 sourceGain = gain;
714 break;
715 }
716 }
717 // NOTE: we only change the source gain in MODE_JOINT here.
718 if (sourceGain != null) {
719 int steps = (sourceGain.maxValue() - sourceGain.minValue())
720 / sourceGain.stepValue();
721 int gainValue = sourceGain.minValue();
722 if (mVolume < 1.0f) {
723 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
724 } else {
725 gainValue = sourceGain.maxValue();
726 }
727 int numChannels = 0;
728 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
729 numChannels += (mask & 1);
730 }
731 int[] gainValues = new int[numChannels];
732 Arrays.fill(gainValues, gainValue);
733 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
734 sourceGain.channelMask(), gainValues, 0);
735 } else {
736 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
737 }
738 }
739
Wonsik Kimca17a902014-07-31 10:09:46 +0900740 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
741 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
742 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900743 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kimca17a902014-07-31 10:09:46 +0900744 if (sinkConfig == null
745 || (mDesiredSamplingRate != 0
746 && sinkConfig.samplingRate() != mDesiredSamplingRate)
747 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
748 && sinkConfig.channelMask() != mDesiredChannelMask)
749 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
750 && sinkConfig.format() != mDesiredFormat)) {
751 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
752 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900753 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900754 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900755 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900756 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900757 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
758 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900759 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900760 if (shouldRecreateAudioPatch) {
761 mCommittedVolume = mVolume;
762 mAudioManager.createAudioPatch(
763 audioPatchArray,
764 new AudioPortConfig[] { sourceConfig },
765 new AudioPortConfig[] { sinkConfig });
766 mAudioPatch = audioPatchArray[0];
767 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900768 }
769
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000770 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900771 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000772 synchronized (mImplLock) {
773 if (mReleased) {
774 throw new IllegalStateException("Device already released.");
775 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900776 mVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +0900777 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000778 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000779 }
780
781 @Override
782 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
783 synchronized (mImplLock) {
784 if (mReleased) {
785 throw new IllegalStateException("Device already released.");
786 }
787 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900788 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000789 return false;
790 }
791 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
792 return false;
793 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900794
795 private boolean startCapture(Surface surface, TvStreamConfig config) {
796 synchronized (mImplLock) {
797 if (mReleased) {
798 return false;
799 }
800 if (surface == null || config == null) {
801 return false;
802 }
803 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
804 return false;
805 }
806
807 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
808 return result == TvInputHal.SUCCESS;
809 }
810 }
811
812 private boolean stopCapture(TvStreamConfig config) {
813 synchronized (mImplLock) {
814 if (mReleased) {
815 return false;
816 }
817 if (config == null) {
818 return false;
819 }
820
821 int result = mHal.removeStream(mInfo.getDeviceId(), config);
822 return result == TvInputHal.SUCCESS;
823 }
824 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900825
Wonsik Kim7587d292014-08-09 10:01:01 +0900826 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900827 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900828 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900829 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900830 AudioDevicePort previousSource = mAudioSource;
831 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
832 return mAudioSource == null ? (previousSource != null)
833 : !mAudioSource.equals(previousSource);
834 }
835
836 private boolean updateAudioSinkLocked() {
837 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
838 return false;
839 }
840 AudioDevicePort previousSink = mAudioSink;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900841 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
842 mAudioSink = findAudioSinkFromAudioPolicy();
843 } else {
844 AudioDevicePort audioSink =
845 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
846 if (audioSink != null) {
847 mAudioSink = audioSink;
848 }
849 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900850 return mAudioSink == null ? (previousSink != null) : !mAudioSink.equals(previousSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +0900851 }
852
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900853 private void handleAudioSinkUpdated() {
854 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900855 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900856 }
857 }
858
Wonsik Kimca17a902014-07-31 10:09:46 +0900859 @Override
860 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
861 int channelMask, int format) {
862 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900863 mOverrideAudioType = audioType;
864 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900865
Wonsik Kimca17a902014-07-31 10:09:46 +0900866 mDesiredSamplingRate = samplingRate;
867 mDesiredChannelMask = channelMask;
868 mDesiredFormat = format;
869
Wonsik Kim7587d292014-08-09 10:01:01 +0900870 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +0900871 }
872 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000873 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900874
Wonsik Kim187423c2014-06-25 14:12:48 +0900875 interface Listener {
876 public void onStateChanged(String inputId, int state);
877 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900878 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700879 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
880 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Wonsik Kime92f8572014-08-12 18:30:24 +0900881 public void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900882 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900883
Wonsik Kim187423c2014-06-25 14:12:48 +0900884 private class ListenerHandler extends Handler {
885 private static final int STATE_CHANGED = 1;
886 private static final int HARDWARE_DEVICE_ADDED = 2;
887 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700888 private static final int HDMI_DEVICE_ADDED = 4;
889 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900890 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +0900891
892 @Override
893 public final void handleMessage(Message msg) {
894 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900895 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900896 String inputId = (String) msg.obj;
897 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900898 mListener.onStateChanged(inputId, state);
899 break;
900 }
901 case HARDWARE_DEVICE_ADDED: {
902 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
903 mListener.onHardwareDeviceAdded(info);
904 break;
905 }
906 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900907 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
908 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900909 break;
910 }
Jae Seo546c6352014-08-07 11:57:01 -0700911 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900912 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700913 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900914 break;
915 }
Jae Seo546c6352014-08-07 11:57:01 -0700916 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900917 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700918 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900919 break;
920 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900921 case HDMI_DEVICE_UPDATED: {
Wonsik Kime92f8572014-08-12 18:30:24 +0900922 SomeArgs args = (SomeArgs) msg.obj;
923 String inputId = (String) args.arg1;
924 HdmiDeviceInfo info = (HdmiDeviceInfo) args.arg2;
925 args.recycle();
926 mListener.onHdmiDeviceUpdated(inputId, info);
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900927 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900928 default: {
929 Slog.w(TAG, "Unhandled message: " + msg);
930 break;
931 }
932 }
933 }
934 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900935
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900936 // Listener implementations for HdmiControlService
937
938 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
939 @Override
940 public void onReceived(HdmiHotplugEvent event) {
941 synchronized (mLock) {
942 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900943 TvInputHardwareInfo hardwareInfo =
944 findHardwareInfoForHdmiPortLocked(event.getPort());
945 if (hardwareInfo == null) {
946 return;
947 }
948 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900949 if (inputId == null) {
950 return;
951 }
952 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
953 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
954 }
955 }
956 }
957
Wonsik Kim187423c2014-06-25 14:12:48 +0900958 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
959 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900960 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900961 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900962 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +0900963 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900964 switch (status) {
965 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
966 if (!mHdmiDeviceList.contains(deviceInfo)) {
967 mHdmiDeviceList.add(deviceInfo);
968 } else {
969 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
970 return;
971 }
972 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +0900973 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900974 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900975 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900976 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
977 if (!mHdmiDeviceList.remove(deviceInfo)) {
978 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
979 return;
980 }
981 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +0900982 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900983 break;
984 }
985 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
986 if (!mHdmiDeviceList.remove(deviceInfo)) {
987 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
988 return;
989 }
990 mHdmiDeviceList.add(deviceInfo);
991 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Wonsik Kime92f8572014-08-12 18:30:24 +0900992 String inputId = mHdmiInputIdMap.get(deviceInfo.getLogicalAddress());
993 SomeArgs args = SomeArgs.obtain();
994 args.arg1 = inputId;
995 args.arg2 = deviceInfo;
996 obj = args;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900997 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900998 }
999 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001000
Wonsik Kime92f8572014-08-12 18:30:24 +09001001 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +09001002 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1003 msg.sendToTarget();
1004 } else {
1005 mPendingHdmiDeviceEvents.add(msg);
1006 }
1007 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001008 }
1009 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001010
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001011 private final class HdmiSystemAudioModeChangeListener extends
1012 IHdmiSystemAudioModeChangeListener.Stub {
1013 @Override
1014 public void onStatusChanged(boolean enabled) throws RemoteException {
1015 synchronized (mLock) {
1016 for (int i = 0; i < mConnections.size(); ++i) {
1017 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
1018 impl.handleAudioSinkUpdated();
1019 }
1020 }
1021 }
1022 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001023}