blob: 8043c651751ce49d9dac958c210fd41646d52ed1 [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 Kim49e55932014-12-04 17:22:09 +090022import android.content.BroadcastReceiver;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000023import android.content.Context;
Wonsik Kim49e55932014-12-04 17:22:09 +090024import android.content.Intent;
25import android.content.IntentFilter;
yangren8d718e12016-02-15 17:02:49 +090026import android.content.pm.PackageManager;
Jungshik Jang61daf6b2014-08-08 11:38:28 +090027import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090028import android.hardware.hdmi.HdmiDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090029import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090030import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090031import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090032import android.hardware.hdmi.IHdmiHotplugEventListener;
Jungshik Jang1f819bb2014-08-08 10:46:54 +090033import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090034import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090035import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090036import android.media.AudioGain;
37import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090038import android.media.AudioManager;
39import android.media.AudioPatch;
40import android.media.AudioPort;
41import android.media.AudioPortConfig;
Wally Yauad36f6472015-10-06 11:33:19 -070042import android.media.AudioSystem;
Jae Seod5cc4a22014-05-30 16:57:43 -070043import android.media.tv.ITvInputHardware;
44import android.media.tv.ITvInputHardwareCallback;
Jae Seo546c6352014-08-07 11:57:01 -070045import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090046import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070047import android.media.tv.TvStreamConfig;
yangren8d718e12016-02-15 17:02:49 +090048import android.os.Binder;
Wonsik Kim969167d2014-06-24 16:33:17 +090049import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000050import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090051import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000052import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090053import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090054import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000055import android.util.Slog;
56import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090057import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000058import android.view.KeyEvent;
59import android.view.Surface;
60
yangren8d718e12016-02-15 17:02:49 +090061import com.android.internal.util.IndentingPrintWriter;
Wonsik Kim969167d2014-06-24 16:33:17 +090062import com.android.server.SystemService;
63
yangren8d718e12016-02-15 17:02:49 +090064import java.io.FileDescriptor;
65import java.io.PrintWriter;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000066import java.util.ArrayList;
yangren8d718e12016-02-15 17:02:49 +090067import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090068import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090069import java.util.Iterator;
70import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000071import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090072import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000073
74/**
75 * A helper class for TvInputManagerService to handle TV input hardware.
76 *
77 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
78 * calls to tv_input HAL module.
79 *
80 * @hide
81 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090082class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000083 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090084
Wonsik Kim49e55932014-12-04 17:22:09 +090085 private final Context mContext;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090086 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000087 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090088 private final SparseArray<Connection> mConnections = new SparseArray<>();
89 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070090 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090091 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090092 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090093 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070094 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090095 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090096
Wonsik Kimd7c29182014-05-27 10:38:21 +090097 private final AudioManager mAudioManager;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090098 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
99 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900100 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900101 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
102 new HdmiSystemAudioModeChangeListener();
Wonsik Kim49e55932014-12-04 17:22:09 +0900103 private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
104 @Override
105 public void onReceive(Context context, Intent intent) {
106 handleVolumeChange(context, intent);
107 }
108 };
109 private int mCurrentIndex = 0;
110 private int mCurrentMaxIndex = 0;
Jinsuk Kimd38bf472014-08-11 11:29:18 +0900111
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700112 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900113 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900114 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900115
Wonsik Kim187423c2014-06-25 14:12:48 +0900116 // Calls to mListener should happen here.
117 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000118
119 private final Object mLock = new Object();
120
Wonsik Kim187423c2014-06-25 14:12:48 +0900121 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900122 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900123 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900124 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000125 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900126 }
127
128 public void onBootPhase(int phase) {
129 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Jae Seo38b32572015-06-05 14:43:11 -0700130 IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
131 ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
132 if (hdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900133 try {
Jae Seo38b32572015-06-05 14:43:11 -0700134 hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
135 hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
136 hdmiControlService.addSystemAudioModeChangeListener(
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900137 mHdmiSystemAudioModeChangeListener);
Jae Seo38b32572015-06-05 14:43:11 -0700138 mHdmiDeviceList.addAll(hdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900139 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900140 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900141 }
Jinsuk Kim08f1ab02014-10-13 10:38:16 +0900142 } else {
143 Slog.w(TAG, "HdmiControlService is not available");
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900144 }
John Spurlockee5ad722015-03-03 16:17:21 -0500145 final IntentFilter filter = new IntentFilter();
146 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
147 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
148 mContext.registerReceiver(mVolumeReceiver, filter);
Wonsik Kim49e55932014-12-04 17:22:09 +0900149 updateVolume();
Wonsik Kim969167d2014-06-24 16:33:17 +0900150 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000151 }
152
153 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900154 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000155 synchronized (mLock) {
156 Connection connection = new Connection(info);
157 connection.updateConfigsLocked(configs);
158 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900159 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900160 mHandler.obtainMessage(
161 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900162 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
163 processPendingHdmiDeviceEventsLocked();
164 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000165 }
166 }
167
Wonsik Kimd922a542014-07-24 18:25:29 +0900168 private void buildHardwareListLocked() {
169 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000170 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900171 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000172 }
173 }
174
175 @Override
176 public void onDeviceUnavailable(int deviceId) {
177 synchronized (mLock) {
178 Connection connection = mConnections.get(deviceId);
179 if (connection == null) {
180 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
181 return;
182 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900183 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000184 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900185 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900186 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900187 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700188 // Remove HDMI devices linked with this hardware.
189 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900190 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900191 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700192 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900193 deviceInfo).sendToTarget();
194 it.remove();
195 }
196 }
197 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900198 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900199 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000200 }
201 }
202
203 @Override
204 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
205 synchronized (mLock) {
206 Connection connection = mConnections.get(deviceId);
207 if (connection == null) {
208 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
209 + deviceId);
210 return;
211 }
212 connection.updateConfigsLocked(configs);
Wonsik Kim9a103652014-10-21 17:46:31 +0900213 String inputId = mHardwareInputIdMap.get(deviceId);
214 if (inputId != null) {
215 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700216 convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900217 }
Dongwon Kang017bb852014-12-26 14:53:14 +0900218 ITvInputHardwareCallback callback = connection.getCallbackLocked();
219 if (callback != null) {
220 try {
221 callback.onStreamConfigChanged(configs);
222 } catch (RemoteException e) {
223 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
224 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000225 }
226 }
227 }
228
Terry Heoc086a3d2014-06-18 14:26:44 +0900229 @Override
230 public void onFirstFrameCaptured(int deviceId, int streamId) {
231 synchronized (mLock) {
232 Connection connection = mConnections.get(deviceId);
233 if (connection == null) {
234 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
235 + deviceId);
236 return;
237 }
238 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
239 if (runnable != null) {
240 runnable.run();
241 connection.setOnFirstFrameCapturedLocked(null);
242 }
243 }
244 }
245
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000246 public List<TvInputHardwareInfo> getHardwareList() {
247 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900248 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000249 }
250 }
251
Jae Seo546c6352014-08-07 11:57:01 -0700252 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900253 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700254 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900255 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900256 }
257
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900258 private boolean checkUidChangedLocked(
259 Connection connection, int callingUid, int resolvedUserId) {
260 Integer connectionCallingUid = connection.getCallingUidLocked();
261 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700262 return connectionCallingUid == null || connectionResolvedUserId == null
263 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900264 }
265
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700266 private int convertConnectedToState(boolean connected) {
267 if (connected) {
268 return INPUT_STATE_CONNECTED;
269 } else {
270 return INPUT_STATE_DISCONNECTED;
Wonsik Kim969167d2014-06-24 16:33:17 +0900271 }
272 }
273
Jae Seo1abbbcd2016-01-28 22:20:41 -0800274 public void addHardwareInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900275 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900276 String oldInputId = mHardwareInputIdMap.get(deviceId);
277 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900278 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900279 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900280 + info + ":" + deviceId);
281 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900282 mHardwareInputIdMap.put(deviceId, info.getId());
283 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900284
Wonsik Kim9a103652014-10-21 17:46:31 +0900285 // Process pending state changes
286
287 // For logical HDMI devices, they have information from HDMI CEC signals.
Wonsik Kim969167d2014-06-24 16:33:17 +0900288 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900289 TvInputHardwareInfo hardwareInfo =
290 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
291 if (hardwareInfo == null) {
292 continue;
293 }
294 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900295 if (inputId != null && inputId.equals(info.getId())) {
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700296 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
297 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
298 inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900299 return;
Wonsik Kim969167d2014-06-24 16:33:17 +0900300 }
301 }
Wonsik Kim9a103652014-10-21 17:46:31 +0900302 // For the rest of the devices, we can tell by the number of available streams.
303 Connection connection = mConnections.get(deviceId);
304 if (connection != null) {
305 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Dongwon Kang4a3a6012016-05-03 16:52:55 -0700306 convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
Wonsik Kim9a103652014-10-21 17:46:31 +0900307 info.getId()).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900308 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900309 }
310 }
311
Wonsik Kim38feae92014-07-21 21:35:50 +0900312 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
313 for (int i = 0; i < map.size(); ++i) {
314 if (map.valueAt(i).equals(value)) {
315 return i;
316 }
317 }
318 return -1;
319 }
320
Wonsik Kim71dfa962014-11-15 14:18:49 +0900321 private static boolean intArrayContains(int[] array, int value) {
322 for (int element : array) {
323 if (element == value) return true;
324 }
325 return false;
326 }
327
Jae Seo1abbbcd2016-01-28 22:20:41 -0800328 public void addHdmiInput(int id, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900329 if (info.getType() != TvInputInfo.TYPE_HDMI) {
330 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
331 }
332 synchronized (mLock) {
333 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900334 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
335 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900336 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
337 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900338 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900339 if (oldInputId != null) {
340 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900341 + mInputMap.get(oldInputId) + ":" + id + ", new = "
342 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900343 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900344 mHdmiInputIdMap.put(id, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900345 mInputMap.put(info.getId(), info);
346 }
347 }
348
Jae Seo1abbbcd2016-01-28 22:20:41 -0800349 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900350 synchronized (mLock) {
351 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900352 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900353 if (hardwareIndex >= 0) {
354 mHardwareInputIdMap.removeAt(hardwareIndex);
355 }
Jae Seo546c6352014-08-07 11:57:01 -0700356 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
357 if (deviceIndex >= 0) {
358 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900359 }
360 }
361 }
362
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000363 /**
364 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
365 * the object, and if more than one process attempts to create hardware with the same deviceId,
366 * the latest service will get the object and all the other hardware are released. The
367 * release is notified via ITvInputHardwareCallback.onReleased().
368 */
369 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900370 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000371 if (callback == null) {
372 throw new NullPointerException();
373 }
374 synchronized (mLock) {
375 Connection connection = mConnections.get(deviceId);
376 if (connection == null) {
377 Slog.e(TAG, "Invalid deviceId : " + deviceId);
378 return null;
379 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900380 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900381 TvInputHardwareImpl hardware =
382 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000383 try {
384 callback.asBinder().linkToDeath(connection, 0);
385 } catch (RemoteException e) {
386 hardware.release();
387 return null;
388 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900389 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000390 }
391 return connection.getHardwareLocked();
392 }
393 }
394
395 /**
396 * Release the specified hardware.
397 */
398 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
399 int resolvedUserId) {
400 synchronized (mLock) {
401 Connection connection = mConnections.get(deviceId);
402 if (connection == null) {
403 Slog.e(TAG, "Invalid deviceId : " + deviceId);
404 return;
405 }
406 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900407 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000408 return;
409 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900410 connection.resetLocked(null, null, null, null, null);
411 }
412 }
413
Wonsik Kimd922a542014-07-24 18:25:29 +0900414 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
415 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900416 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
417 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900418 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900419 }
420 }
421 return null;
422 }
423
Terry Heoc086a3d2014-06-18 14:26:44 +0900424 private int findDeviceIdForInputIdLocked(String inputId) {
425 for (int i = 0; i < mConnections.size(); ++i) {
426 Connection connection = mConnections.get(i);
427 if (connection.getInfoLocked().getId().equals(inputId)) {
428 return i;
429 }
430 }
431 return -1;
432 }
433
434 /**
435 * Get the list of TvStreamConfig which is buffered mode.
436 */
437 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
438 int resolvedUserId) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700439 List<TvStreamConfig> configsList = new ArrayList<>();
Terry Heoc086a3d2014-06-18 14:26:44 +0900440 synchronized (mLock) {
441 int deviceId = findDeviceIdForInputIdLocked(inputId);
442 if (deviceId < 0) {
443 Slog.e(TAG, "Invalid inputId : " + inputId);
444 return configsList;
445 }
446 Connection connection = mConnections.get(deviceId);
447 for (TvStreamConfig config : connection.getConfigsLocked()) {
448 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
449 configsList.add(config);
450 }
451 }
452 }
453 return configsList;
454 }
455
456 /**
457 * Take a snapshot of the given TV input into the provided Surface.
458 */
459 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
460 int callingUid, int resolvedUserId) {
461 synchronized (mLock) {
462 int deviceId = findDeviceIdForInputIdLocked(inputId);
463 if (deviceId < 0) {
464 Slog.e(TAG, "Invalid inputId : " + inputId);
465 return false;
466 }
467 Connection connection = mConnections.get(deviceId);
468 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
469 if (hardwareImpl != null) {
470 // Stop previous capture.
471 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
472 if (runnable != null) {
473 runnable.run();
474 connection.setOnFirstFrameCapturedLocked(null);
475 }
476
477 boolean result = hardwareImpl.startCapture(surface, config);
478 if (result) {
479 connection.setOnFirstFrameCapturedLocked(new Runnable() {
480 @Override
481 public void run() {
482 hardwareImpl.stopCapture(config);
483 }
484 });
485 }
486 return result;
487 }
488 }
489 return false;
490 }
491
Wonsik Kimd922a542014-07-24 18:25:29 +0900492 private void processPendingHdmiDeviceEventsLocked() {
493 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
494 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900495 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900496 TvInputHardwareInfo hardwareInfo =
497 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
498 if (hardwareInfo != null) {
499 msg.sendToTarget();
500 it.remove();
501 }
502 }
503 }
504
Wonsik Kim49e55932014-12-04 17:22:09 +0900505 private void updateVolume() {
506 mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
507 mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
508 }
509
510 private void handleVolumeChange(Context context, Intent intent) {
511 String action = intent.getAction();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700512 switch (action) {
513 case AudioManager.VOLUME_CHANGED_ACTION: {
514 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
515 if (streamType != AudioManager.STREAM_MUSIC) {
516 return;
517 }
518 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
519 if (index == mCurrentIndex) {
520 return;
521 }
522 mCurrentIndex = index;
523 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900524 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700525 case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
526 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
527 if (streamType != AudioManager.STREAM_MUSIC) {
528 return;
529 }
530 // volume index will be updated at onMediaStreamVolumeChanged() through
531 // updateVolume().
532 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900533 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700534 default:
535 Slog.w(TAG, "Unrecognized intent: " + intent);
Wonsik Kim49e55932014-12-04 17:22:09 +0900536 return;
Wonsik Kim49e55932014-12-04 17:22:09 +0900537 }
538 synchronized (mLock) {
539 for (int i = 0; i < mConnections.size(); ++i) {
540 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
541 if (hardwareImpl != null) {
542 hardwareImpl.onMediaStreamVolumeChanged();
543 }
544 }
545 }
546 }
547
548 private float getMediaStreamVolume() {
John Spurlockee5ad722015-03-03 16:17:21 -0500549 return (float) mCurrentIndex / (float) mCurrentMaxIndex;
Wonsik Kim49e55932014-12-04 17:22:09 +0900550 }
551
yangren8d718e12016-02-15 17:02:49 +0900552 public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
553 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
554 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
555 != PackageManager.PERMISSION_GRANTED) {
556 pw.println("Permission Denial: can't dump TvInputHardwareManager from pid="
557 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
558 return;
559 }
560
561 synchronized (mLock) {
562 pw.println("TvInputHardwareManager Info:");
563 pw.increaseIndent();
564 pw.println("mConnections: deviceId -> Connection");
565 pw.increaseIndent();
566 for (int i = 0; i < mConnections.size(); i++) {
567 int deviceId = mConnections.keyAt(i);
568 Connection mConnection = mConnections.valueAt(i);
569 pw.println(deviceId + ": " + mConnection);
570
571 }
572 pw.decreaseIndent();
573
574 pw.println("mHardwareList:");
575 pw.increaseIndent();
576 for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
577 pw.println(tvInputHardwareInfo);
578 }
579 pw.decreaseIndent();
580
581 pw.println("mHdmiDeviceList:");
582 pw.increaseIndent();
583 for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
584 pw.println(hdmiDeviceInfo);
585 }
586 pw.decreaseIndent();
587
588 pw.println("mHardwareInputIdMap: deviceId -> inputId");
589 pw.increaseIndent();
590 for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
591 int deviceId = mHardwareInputIdMap.keyAt(i);
592 String inputId = mHardwareInputIdMap.valueAt(i);
593 pw.println(deviceId + ": " + inputId);
594 }
595 pw.decreaseIndent();
596
597 pw.println("mHdmiInputIdMap: id -> inputId");
598 pw.increaseIndent();
599 for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
600 int id = mHdmiInputIdMap.keyAt(i);
601 String inputId = mHdmiInputIdMap.valueAt(i);
602 pw.println(id + ": " + inputId);
603 }
604 pw.decreaseIndent();
605
606 pw.println("mInputMap: inputId -> inputInfo");
607 pw.increaseIndent();
608 for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
609 pw.println(entry.getKey() + ": " + entry.getValue());
610 }
611 pw.decreaseIndent();
612 pw.decreaseIndent();
613 }
614 }
615
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000616 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900617 private final TvInputHardwareInfo mHardwareInfo;
618 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000619 private TvInputHardwareImpl mHardware = null;
620 private ITvInputHardwareCallback mCallback;
621 private TvStreamConfig[] mConfigs = null;
622 private Integer mCallingUid = null;
623 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900624 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000625
Wonsik Kim969167d2014-06-24 16:33:17 +0900626 public Connection(TvInputHardwareInfo hardwareInfo) {
627 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000628 }
629
630 // *Locked methods assume TvInputHardwareManager.mLock is held.
631
Wonsik Kim969167d2014-06-24 16:33:17 +0900632 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
633 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000634 if (mHardware != null) {
635 try {
636 mCallback.onReleased();
637 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900638 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000639 }
640 mHardware.release();
641 }
642 mHardware = hardware;
643 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900644 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000645 mCallingUid = callingUid;
646 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900647 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000648
649 if (mHardware != null && mCallback != null) {
650 try {
651 mCallback.onStreamConfigChanged(getConfigsLocked());
652 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900653 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000654 }
655 }
656 }
657
658 public void updateConfigsLocked(TvStreamConfig[] configs) {
659 mConfigs = configs;
660 }
661
Wonsik Kim969167d2014-06-24 16:33:17 +0900662 public TvInputHardwareInfo getHardwareInfoLocked() {
663 return mHardwareInfo;
664 }
665
666 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000667 return mInfo;
668 }
669
670 public ITvInputHardware getHardwareLocked() {
671 return mHardware;
672 }
673
Terry Heoc086a3d2014-06-18 14:26:44 +0900674 public TvInputHardwareImpl getHardwareImplLocked() {
675 return mHardware;
676 }
677
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000678 public ITvInputHardwareCallback getCallbackLocked() {
679 return mCallback;
680 }
681
682 public TvStreamConfig[] getConfigsLocked() {
683 return mConfigs;
684 }
685
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900686 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000687 return mCallingUid;
688 }
689
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900690 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000691 return mResolvedUserId;
692 }
693
Terry Heoc086a3d2014-06-18 14:26:44 +0900694 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
695 mOnFirstFrameCaptured = runnable;
696 }
697
698 public Runnable getOnFirstFrameCapturedLocked() {
699 return mOnFirstFrameCaptured;
700 }
701
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000702 @Override
703 public void binderDied() {
704 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900705 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000706 }
707 }
yangren8d718e12016-02-15 17:02:49 +0900708
709 public String toString() {
710 return "Connection{"
711 + " mHardwareInfo: " + mHardwareInfo
712 + ", mInfo: " + mInfo
713 + ", mCallback: " + mCallback
714 + ", mConfigs: " + Arrays.toString(mConfigs)
715 + ", mCallingUid: " + mCallingUid
716 + ", mResolvedUserId: " + mResolvedUserId
717 + " }";
718 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000719 }
720
721 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
722 private final TvInputHardwareInfo mInfo;
723 private boolean mReleased = false;
724 private final Object mImplLock = new Object();
725
Wonsik Kim06c41a32014-08-04 18:57:47 +0900726 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
727 new AudioManager.OnAudioPortUpdateListener() {
728 @Override
729 public void onAudioPortListUpdate(AudioPort[] portList) {
730 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900731 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900732 }
733 }
734
735 @Override
736 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
737 // No-op
738 }
739
740 @Override
741 public void onServiceDied() {
742 synchronized (mImplLock) {
743 mAudioSource = null;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900744 mAudioSink.clear();
Jae Seoee32ede2016-05-31 10:31:15 -0700745 if (mAudioPatch != null) {
746 mAudioManager.releaseAudioPatch(mAudioPatch);
747 mAudioPatch = null;
748 }
Wonsik Kim06c41a32014-08-04 18:57:47 +0900749 }
750 }
751 };
752 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
753 private String mOverrideAudioAddress = "";
754 private AudioDevicePort mAudioSource;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900755 private List<AudioDevicePort> mAudioSink = new ArrayList<>();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900756 private AudioPatch mAudioPatch = null;
Wonsik Kim485e7e12015-01-26 17:28:55 +0900757 // Set to an invalid value for a volume, so that current volume can be applied at the
758 // first call to updateAudioConfigLocked().
759 private float mCommittedVolume = -1f;
Wonsik Kim49e55932014-12-04 17:22:09 +0900760 private float mSourceVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900761
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900762 private TvStreamConfig mActiveConfig = null;
763
Wonsik Kimca17a902014-07-31 10:09:46 +0900764 private int mDesiredSamplingRate = 0;
765 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
766 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
767
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000768 public TvInputHardwareImpl(TvInputHardwareInfo info) {
769 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900770 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900771 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900772 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900773 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kimca17a902014-07-31 10:09:46 +0900774 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900775 }
776
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900777 private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
778 sinks.clear();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700779 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900780 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
781 return;
782 }
783 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
Paul McLeanceb47aa2015-02-19 15:09:53 -0800784 for (AudioDevicePort port : devicePorts) {
Wally Yauad36f6472015-10-06 11:33:19 -0700785 if ((port.type() & sinkDevice) != 0 &&
786 (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
Paul McLeanceb47aa2015-02-19 15:09:53 -0800787 sinks.add(port);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900788 }
789 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900790 }
791
792 private AudioDevicePort findAudioDevicePort(int type, String address) {
793 if (type == AudioManager.DEVICE_NONE) {
794 return null;
795 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700796 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900797 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
798 return null;
799 }
Paul McLeanceb47aa2015-02-19 15:09:53 -0800800 for (AudioDevicePort port : devicePorts) {
801 if (port.type() == type && port.address().equals(address)) {
802 return port;
Wonsik Kimca17a902014-07-31 10:09:46 +0900803 }
804 }
805 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000806 }
807
808 public void release() {
809 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900810 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900811 if (mAudioPatch != null) {
812 mAudioManager.releaseAudioPatch(mAudioPatch);
813 mAudioPatch = null;
814 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000815 mReleased = true;
816 }
817 }
818
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900819 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
820 // attempts to call setSurface with different TvStreamConfig objects, the last call will
821 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000822 @Override
823 public boolean setSurface(Surface surface, TvStreamConfig config)
824 throws RemoteException {
825 synchronized (mImplLock) {
826 if (mReleased) {
827 throw new IllegalStateException("Device already released.");
828 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900829
Wonsik Kim102670f2014-11-20 14:38:02 +0900830 int result = TvInputHal.SUCCESS;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900831 if (surface == null) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900832 // The value of config is ignored when surface == null.
833 if (mActiveConfig != null) {
834 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
835 mActiveConfig = null;
836 } else {
837 // We already have no active stream.
838 return true;
839 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900840 } else {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900841 // It's impossible to set a non-null surface with a null config.
842 if (config == null) {
843 return false;
844 }
845 // Remove stream only if we have an existing active configuration.
846 if (mActiveConfig != null && !config.equals(mActiveConfig)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900847 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
848 if (result != TvInputHal.SUCCESS) {
849 mActiveConfig = null;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900850 }
851 }
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900852 // Proceed only if all previous operations succeeded.
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900853 if (result == TvInputHal.SUCCESS) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900854 result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
855 if (result == TvInputHal.SUCCESS) {
856 mActiveConfig = config;
857 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900858 }
859 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900860 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900861 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000862 }
863 }
864
Wonsik Kim7587d292014-08-09 10:01:01 +0900865 /**
866 * Update audio configuration (source, sink, patch) all up to current state.
867 */
868 private void updateAudioConfigLocked() {
869 boolean sinkUpdated = updateAudioSinkLocked();
870 boolean sourceUpdated = updateAudioSourceLocked();
871 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
872 // because Java won't evaluate the latter if the former is true.
873
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900874 if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900875 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900876 mAudioManager.releaseAudioPatch(mAudioPatch);
877 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900878 }
879 return;
880 }
881
Wonsik Kim49e55932014-12-04 17:22:09 +0900882 updateVolume();
883 float volume = mSourceVolume * getMediaStreamVolume();
Wonsik Kim8e45a332014-08-04 10:17:18 +0900884 AudioGainConfig sourceGainConfig = null;
Wonsik Kim49e55932014-12-04 17:22:09 +0900885 if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
Wonsik Kim8e45a332014-08-04 10:17:18 +0900886 AudioGain sourceGain = null;
887 for (AudioGain gain : mAudioSource.gains()) {
888 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
889 sourceGain = gain;
890 break;
891 }
892 }
893 // NOTE: we only change the source gain in MODE_JOINT here.
894 if (sourceGain != null) {
895 int steps = (sourceGain.maxValue() - sourceGain.minValue())
896 / sourceGain.stepValue();
897 int gainValue = sourceGain.minValue();
Wonsik Kim49e55932014-12-04 17:22:09 +0900898 if (volume < 1.0f) {
899 gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900900 } else {
901 gainValue = sourceGain.maxValue();
902 }
Wonsik Kim49e55932014-12-04 17:22:09 +0900903 // size of gain values is 1 in MODE_JOINT
904 int[] gainValues = new int[] { gainValue };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900905 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
906 sourceGain.channelMask(), gainValues, 0);
907 } else {
908 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
909 }
910 }
911
Wonsik Kimca17a902014-07-31 10:09:46 +0900912 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900913 List<AudioPortConfig> sinkConfigs = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900914 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900915 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900916
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900917 for (AudioDevicePort audioSink : mAudioSink) {
918 AudioPortConfig sinkConfig = audioSink.activeConfig();
919 int sinkSamplingRate = mDesiredSamplingRate;
920 int sinkChannelMask = mDesiredChannelMask;
921 int sinkFormat = mDesiredFormat;
922 // If sinkConfig != null and values are set to default,
923 // fill in the sinkConfig values.
924 if (sinkConfig != null) {
925 if (sinkSamplingRate == 0) {
926 sinkSamplingRate = sinkConfig.samplingRate();
927 }
928 if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
929 sinkChannelMask = sinkConfig.channelMask();
930 }
931 if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
932 sinkChannelMask = sinkConfig.format();
933 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900934 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900935
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900936 if (sinkConfig == null
937 || sinkConfig.samplingRate() != sinkSamplingRate
938 || sinkConfig.channelMask() != sinkChannelMask
939 || sinkConfig.format() != sinkFormat) {
940 // Check for compatibility and reset to default if necessary.
941 if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
942 && audioSink.samplingRates().length > 0) {
943 sinkSamplingRate = audioSink.samplingRates()[0];
944 }
945 if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
946 sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
947 }
948 if (!intArrayContains(audioSink.formats(), sinkFormat)) {
949 sinkFormat = AudioFormat.ENCODING_DEFAULT;
950 }
951 sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
952 sinkFormat, null);
953 shouldRecreateAudioPatch = true;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900954 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900955 sinkConfigs.add(sinkConfig);
Wonsik Kimca17a902014-07-31 10:09:46 +0900956 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900957 // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
958 // non-empty at the beginning of this method.
959 AudioPortConfig sinkConfig = sinkConfigs.get(0);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900960 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kim71dfa962014-11-15 14:18:49 +0900961 int sourceSamplingRate = 0;
962 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
963 sourceSamplingRate = sinkConfig.samplingRate();
964 } else if (mAudioSource.samplingRates().length > 0) {
965 // Use any sampling rate and hope audio patch can handle resampling...
966 sourceSamplingRate = mAudioSource.samplingRates()[0];
967 }
968 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
969 for (int inChannelMask : mAudioSource.channelMasks()) {
970 if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
971 == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
972 sourceChannelMask = inChannelMask;
973 break;
974 }
975 }
976 int sourceFormat = AudioFormat.ENCODING_DEFAULT;
977 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
978 sourceFormat = sinkConfig.format();
979 }
980 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
981 sourceFormat, sourceGainConfig);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900982 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900983 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900984 if (shouldRecreateAudioPatch) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900985 mCommittedVolume = volume;
Jae Seoee32ede2016-05-31 10:31:15 -0700986 if (mAudioPatch != null) {
987 mAudioManager.releaseAudioPatch(mAudioPatch);
988 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900989 mAudioManager.createAudioPatch(
990 audioPatchArray,
991 new AudioPortConfig[] { sourceConfig },
Jae Seo93ff14b2015-06-21 14:08:54 -0700992 sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
Wonsik Kim8e45a332014-08-04 10:17:18 +0900993 mAudioPatch = audioPatchArray[0];
Wonsik Kim71dfa962014-11-15 14:18:49 +0900994 if (sourceGainConfig != null) {
995 mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
996 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900997 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900998 }
999
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001000 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +09001001 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001002 synchronized (mImplLock) {
1003 if (mReleased) {
1004 throw new IllegalStateException("Device already released.");
1005 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001006 mSourceVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +09001007 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001008 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001009 }
1010
1011 @Override
1012 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
1013 synchronized (mImplLock) {
1014 if (mReleased) {
1015 throw new IllegalStateException("Device already released.");
1016 }
1017 }
Wonsik Kim610ccd92014-07-19 18:49:49 +09001018 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001019 return false;
1020 }
1021 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
1022 return false;
1023 }
Terry Heoc086a3d2014-06-18 14:26:44 +09001024
1025 private boolean startCapture(Surface surface, TvStreamConfig config) {
1026 synchronized (mImplLock) {
1027 if (mReleased) {
1028 return false;
1029 }
1030 if (surface == null || config == null) {
1031 return false;
1032 }
1033 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
1034 return false;
1035 }
1036
Wonsik Kim8f24a8b2014-10-22 16:27:39 +09001037 int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
Terry Heoc086a3d2014-06-18 14:26:44 +09001038 return result == TvInputHal.SUCCESS;
1039 }
1040 }
1041
1042 private boolean stopCapture(TvStreamConfig config) {
1043 synchronized (mImplLock) {
1044 if (mReleased) {
1045 return false;
1046 }
1047 if (config == null) {
1048 return false;
1049 }
1050
1051 int result = mHal.removeStream(mInfo.getDeviceId(), config);
1052 return result == TvInputHal.SUCCESS;
1053 }
1054 }
Wonsik Kimca17a902014-07-31 10:09:46 +09001055
Wonsik Kim7587d292014-08-09 10:01:01 +09001056 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001057 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001058 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001059 }
Wonsik Kim7587d292014-08-09 10:01:01 +09001060 AudioDevicePort previousSource = mAudioSource;
1061 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
1062 return mAudioSource == null ? (previousSource != null)
1063 : !mAudioSource.equals(previousSource);
1064 }
1065
1066 private boolean updateAudioSinkLocked() {
1067 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
1068 return false;
1069 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001070 List<AudioDevicePort> previousSink = mAudioSink;
1071 mAudioSink = new ArrayList<>();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001072 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001073 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001074 } else {
1075 AudioDevicePort audioSink =
1076 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
1077 if (audioSink != null) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001078 mAudioSink.add(audioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001079 }
1080 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001081
1082 // Returns true if mAudioSink and previousSink differs.
1083 if (mAudioSink.size() != previousSink.size()) {
1084 return true;
1085 }
1086 previousSink.removeAll(mAudioSink);
1087 return !previousSink.isEmpty();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001088 }
1089
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001090 private void handleAudioSinkUpdated() {
1091 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001092 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001093 }
1094 }
1095
Wonsik Kimca17a902014-07-31 10:09:46 +09001096 @Override
1097 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1098 int channelMask, int format) {
1099 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001100 mOverrideAudioType = audioType;
1101 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001102
Wonsik Kimca17a902014-07-31 10:09:46 +09001103 mDesiredSamplingRate = samplingRate;
1104 mDesiredChannelMask = channelMask;
1105 mDesiredFormat = format;
1106
Wonsik Kim7587d292014-08-09 10:01:01 +09001107 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +09001108 }
1109 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001110
1111 public void onMediaStreamVolumeChanged() {
1112 synchronized (mImplLock) {
1113 updateAudioConfigLocked();
1114 }
1115 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001116 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001117
Wonsik Kim187423c2014-06-25 14:12:48 +09001118 interface Listener {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001119 void onStateChanged(String inputId, int state);
1120 void onHardwareDeviceAdded(TvInputHardwareInfo info);
1121 void onHardwareDeviceRemoved(TvInputHardwareInfo info);
1122 void onHdmiDeviceAdded(HdmiDeviceInfo device);
1123 void onHdmiDeviceRemoved(HdmiDeviceInfo device);
1124 void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +09001125 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001126
Wonsik Kim187423c2014-06-25 14:12:48 +09001127 private class ListenerHandler extends Handler {
1128 private static final int STATE_CHANGED = 1;
1129 private static final int HARDWARE_DEVICE_ADDED = 2;
1130 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -07001131 private static final int HDMI_DEVICE_ADDED = 4;
1132 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001133 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +09001134
1135 @Override
1136 public final void handleMessage(Message msg) {
1137 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +09001138 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +09001139 String inputId = (String) msg.obj;
1140 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +09001141 mListener.onStateChanged(inputId, state);
1142 break;
1143 }
1144 case HARDWARE_DEVICE_ADDED: {
1145 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1146 mListener.onHardwareDeviceAdded(info);
1147 break;
1148 }
1149 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09001150 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1151 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001152 break;
1153 }
Jae Seo546c6352014-08-07 11:57:01 -07001154 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001155 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001156 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001157 break;
1158 }
Jae Seo546c6352014-08-07 11:57:01 -07001159 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001160 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001161 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +09001162 break;
1163 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001164 case HDMI_DEVICE_UPDATED: {
Wonsik Kim184a6d62014-10-08 13:05:56 +09001165 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001166 String inputId;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001167 synchronized (mLock) {
1168 inputId = mHdmiInputIdMap.get(info.getId());
1169 }
1170 if (inputId != null) {
1171 mListener.onHdmiDeviceUpdated(inputId, info);
1172 } else {
1173 Slog.w(TAG, "Could not resolve input ID matching the device info; "
1174 + "ignoring.");
1175 }
1176 break;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001177 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001178 default: {
1179 Slog.w(TAG, "Unhandled message: " + msg);
1180 break;
1181 }
1182 }
1183 }
1184 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001185
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001186 // Listener implementations for HdmiControlService
1187
1188 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
1189 @Override
1190 public void onReceived(HdmiHotplugEvent event) {
1191 synchronized (mLock) {
1192 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +09001193 TvInputHardwareInfo hardwareInfo =
1194 findHardwareInfoForHdmiPortLocked(event.getPort());
1195 if (hardwareInfo == null) {
1196 return;
1197 }
1198 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001199 if (inputId == null) {
1200 return;
1201 }
Dongwon Kang4a3a6012016-05-03 16:52:55 -07001202 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
1203 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001204 }
1205 }
1206 }
1207
Wonsik Kim187423c2014-06-25 14:12:48 +09001208 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1209 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001210 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Jinsuk Kim98d760e2014-12-23 07:01:51 +09001211 if (!deviceInfo.isSourceType()) return;
Wonsik Kimd922a542014-07-24 18:25:29 +09001212 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001213 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +09001214 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001215 switch (status) {
1216 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001217 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001218 mHdmiDeviceList.add(deviceInfo);
1219 } else {
1220 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
1221 return;
1222 }
1223 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001224 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001225 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001226 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001227 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001228 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1229 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001230 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1231 return;
1232 }
1233 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001234 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001235 break;
1236 }
1237 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001238 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1239 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001240 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1241 return;
1242 }
1243 mHdmiDeviceList.add(deviceInfo);
1244 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001245 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001246 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001247 }
1248 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001249
Wonsik Kime92f8572014-08-12 18:30:24 +09001250 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +09001251 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1252 msg.sendToTarget();
1253 } else {
1254 mPendingHdmiDeviceEvents.add(msg);
1255 }
1256 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001257 }
Jungshik Jangcf445872014-08-20 15:06:01 +09001258
1259 private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1260 for (HdmiDeviceInfo info : mHdmiDeviceList) {
1261 if (info.getId() == id) {
1262 return info;
1263 }
1264 }
1265 return null;
1266 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001267 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001268
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001269 private final class HdmiSystemAudioModeChangeListener extends
1270 IHdmiSystemAudioModeChangeListener.Stub {
1271 @Override
1272 public void onStatusChanged(boolean enabled) throws RemoteException {
1273 synchronized (mLock) {
1274 for (int i = 0; i < mConnections.size(); ++i) {
1275 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
Jungshik Jang44bb3a52014-08-26 17:24:24 +09001276 if (impl != null) {
1277 impl.handleAudioSinkUpdated();
1278 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001279 }
1280 }
1281 }
1282 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001283}