blob: bf281d6fb47140f4a01f8c964316cb1f18671552 [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;
Wonsik Kim102d0b72015-11-26 18:51:09 +090020import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
Wonsik Kim969167d2014-06-24 16:33:17 +090021import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
22
Wonsik Kim49e55932014-12-04 17:22:09 +090023import android.content.BroadcastReceiver;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000024import android.content.Context;
Wonsik Kim49e55932014-12-04 17:22:09 +090025import android.content.Intent;
26import android.content.IntentFilter;
yangren8d718e12016-02-15 17:02:49 +090027import android.content.pm.PackageManager;
Jungshik Jang61daf6b2014-08-08 11:38:28 +090028import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090029import android.hardware.hdmi.HdmiDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090030import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090031import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090032import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090033import android.hardware.hdmi.IHdmiHotplugEventListener;
Jungshik Jang1f819bb2014-08-08 10:46:54 +090034import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090035import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090036import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090037import android.media.AudioGain;
38import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090039import android.media.AudioManager;
40import android.media.AudioPatch;
41import android.media.AudioPort;
42import android.media.AudioPortConfig;
Wally Yauad36f6472015-10-06 11:33:19 -070043import android.media.AudioSystem;
Jae Seod5cc4a22014-05-30 16:57:43 -070044import android.media.tv.ITvInputHardware;
45import android.media.tv.ITvInputHardwareCallback;
Jae Seo546c6352014-08-07 11:57:01 -070046import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090047import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070048import android.media.tv.TvStreamConfig;
yangren8d718e12016-02-15 17:02:49 +090049import android.os.Binder;
Wonsik Kim969167d2014-06-24 16:33:17 +090050import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000051import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090052import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000053import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090054import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090055import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000056import android.util.Slog;
57import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090058import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000059import android.view.KeyEvent;
60import android.view.Surface;
61
yangren8d718e12016-02-15 17:02:49 +090062import com.android.internal.util.IndentingPrintWriter;
Wonsik Kim969167d2014-06-24 16:33:17 +090063import com.android.server.SystemService;
64
yangren8d718e12016-02-15 17:02:49 +090065import java.io.FileDescriptor;
66import java.io.PrintWriter;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000067import java.util.ArrayList;
yangren8d718e12016-02-15 17:02:49 +090068import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090069import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090070import java.util.Iterator;
71import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000072import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090073import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000074
75/**
76 * A helper class for TvInputManagerService to handle TV input hardware.
77 *
78 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
79 * calls to tv_input HAL module.
80 *
81 * @hide
82 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090083class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000084 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090085
Wonsik Kim49e55932014-12-04 17:22:09 +090086 private final Context mContext;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090087 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000088 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090089 private final SparseArray<Connection> mConnections = new SparseArray<>();
90 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070091 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090092 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090093 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090094 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070095 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090096 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090097
Wonsik Kimd7c29182014-05-27 10:38:21 +090098 private final AudioManager mAudioManager;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090099 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
100 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900101 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900102 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
103 new HdmiSystemAudioModeChangeListener();
Wonsik Kim49e55932014-12-04 17:22:09 +0900104 private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
105 @Override
106 public void onReceive(Context context, Intent intent) {
107 handleVolumeChange(context, intent);
108 }
109 };
110 private int mCurrentIndex = 0;
111 private int mCurrentMaxIndex = 0;
Jinsuk Kimd38bf472014-08-11 11:29:18 +0900112
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,
Wonsik Kim102d0b72015-11-26 18:51:09 +0900216 obtainStateFromConfigs(configs), 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
Wonsik Kim102d0b72015-11-26 18:51:09 +0900266 private int obtainStateFromConfigs(TvStreamConfig[] configs) {
267 for (TvStreamConfig config : configs) {
268 if ((config.getFlags() & TvStreamConfig.FLAG_MASK_SIGNAL_DETECTION) != 0) {
269 return INPUT_STATE_CONNECTED;
270 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900271 }
Wonsik Kim102d0b72015-11-26 18:51:09 +0900272 return (configs.length > 0) ? INPUT_STATE_CONNECTED_STANDBY : INPUT_STATE_DISCONNECTED;
Wonsik Kim969167d2014-06-24 16:33:17 +0900273 }
274
Jae Seo1abbbcd2016-01-28 22:20:41 -0800275 public void addHardwareInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900276 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900277 String oldInputId = mHardwareInputIdMap.get(deviceId);
278 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900279 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900280 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900281 + info + ":" + deviceId);
282 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900283 mHardwareInputIdMap.put(deviceId, info.getId());
284 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900285
Wonsik Kim9a103652014-10-21 17:46:31 +0900286 // Process pending state changes
287
288 // For logical HDMI devices, they have information from HDMI CEC signals.
Wonsik Kim969167d2014-06-24 16:33:17 +0900289 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900290 TvInputHardwareInfo hardwareInfo =
291 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
292 if (hardwareInfo == null) {
293 continue;
294 }
295 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900296 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim102d0b72015-11-26 18:51:09 +0900297 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
298 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
299 // denote unknown state.
300 int state = mHdmiStateMap.valueAt(i)
301 ? INPUT_STATE_CONNECTED
302 : INPUT_STATE_CONNECTED_STANDBY;
303 mHandler.obtainMessage(
304 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900305 return;
Wonsik Kim969167d2014-06-24 16:33:17 +0900306 }
307 }
Wonsik Kim9a103652014-10-21 17:46:31 +0900308 // For the rest of the devices, we can tell by the number of available streams.
309 Connection connection = mConnections.get(deviceId);
310 if (connection != null) {
311 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim102d0b72015-11-26 18:51:09 +0900312 obtainStateFromConfigs(connection.getConfigsLocked()), 0,
Wonsik Kim9a103652014-10-21 17:46:31 +0900313 info.getId()).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900314 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900315 }
316 }
317
Wonsik Kim38feae92014-07-21 21:35:50 +0900318 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
319 for (int i = 0; i < map.size(); ++i) {
320 if (map.valueAt(i).equals(value)) {
321 return i;
322 }
323 }
324 return -1;
325 }
326
Wonsik Kim71dfa962014-11-15 14:18:49 +0900327 private static boolean intArrayContains(int[] array, int value) {
328 for (int element : array) {
329 if (element == value) return true;
330 }
331 return false;
332 }
333
Jae Seo1abbbcd2016-01-28 22:20:41 -0800334 public void addHdmiInput(int id, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900335 if (info.getType() != TvInputInfo.TYPE_HDMI) {
336 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
337 }
338 synchronized (mLock) {
339 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900340 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
341 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900342 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
343 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900344 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900345 if (oldInputId != null) {
346 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900347 + mInputMap.get(oldInputId) + ":" + id + ", new = "
348 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900349 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900350 mHdmiInputIdMap.put(id, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900351 mInputMap.put(info.getId(), info);
352 }
353 }
354
Jae Seo1abbbcd2016-01-28 22:20:41 -0800355 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900356 synchronized (mLock) {
357 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900358 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900359 if (hardwareIndex >= 0) {
360 mHardwareInputIdMap.removeAt(hardwareIndex);
361 }
Jae Seo546c6352014-08-07 11:57:01 -0700362 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
363 if (deviceIndex >= 0) {
364 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900365 }
366 }
367 }
368
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000369 /**
370 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
371 * the object, and if more than one process attempts to create hardware with the same deviceId,
372 * the latest service will get the object and all the other hardware are released. The
373 * release is notified via ITvInputHardwareCallback.onReleased().
374 */
375 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900376 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000377 if (callback == null) {
378 throw new NullPointerException();
379 }
380 synchronized (mLock) {
381 Connection connection = mConnections.get(deviceId);
382 if (connection == null) {
383 Slog.e(TAG, "Invalid deviceId : " + deviceId);
384 return null;
385 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900386 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900387 TvInputHardwareImpl hardware =
388 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000389 try {
390 callback.asBinder().linkToDeath(connection, 0);
391 } catch (RemoteException e) {
392 hardware.release();
393 return null;
394 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900395 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000396 }
397 return connection.getHardwareLocked();
398 }
399 }
400
401 /**
402 * Release the specified hardware.
403 */
404 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
405 int resolvedUserId) {
406 synchronized (mLock) {
407 Connection connection = mConnections.get(deviceId);
408 if (connection == null) {
409 Slog.e(TAG, "Invalid deviceId : " + deviceId);
410 return;
411 }
412 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900413 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000414 return;
415 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900416 connection.resetLocked(null, null, null, null, null);
417 }
418 }
419
Wonsik Kimd922a542014-07-24 18:25:29 +0900420 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
421 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900422 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
423 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900424 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900425 }
426 }
427 return null;
428 }
429
Terry Heoc086a3d2014-06-18 14:26:44 +0900430 private int findDeviceIdForInputIdLocked(String inputId) {
431 for (int i = 0; i < mConnections.size(); ++i) {
432 Connection connection = mConnections.get(i);
433 if (connection.getInfoLocked().getId().equals(inputId)) {
434 return i;
435 }
436 }
437 return -1;
438 }
439
440 /**
441 * Get the list of TvStreamConfig which is buffered mode.
442 */
443 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
444 int resolvedUserId) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700445 List<TvStreamConfig> configsList = new ArrayList<>();
Terry Heoc086a3d2014-06-18 14:26:44 +0900446 synchronized (mLock) {
447 int deviceId = findDeviceIdForInputIdLocked(inputId);
448 if (deviceId < 0) {
449 Slog.e(TAG, "Invalid inputId : " + inputId);
450 return configsList;
451 }
452 Connection connection = mConnections.get(deviceId);
453 for (TvStreamConfig config : connection.getConfigsLocked()) {
454 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
455 configsList.add(config);
456 }
457 }
458 }
459 return configsList;
460 }
461
462 /**
463 * Take a snapshot of the given TV input into the provided Surface.
464 */
465 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
466 int callingUid, int resolvedUserId) {
467 synchronized (mLock) {
468 int deviceId = findDeviceIdForInputIdLocked(inputId);
469 if (deviceId < 0) {
470 Slog.e(TAG, "Invalid inputId : " + inputId);
471 return false;
472 }
473 Connection connection = mConnections.get(deviceId);
474 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
475 if (hardwareImpl != null) {
476 // Stop previous capture.
477 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
478 if (runnable != null) {
479 runnable.run();
480 connection.setOnFirstFrameCapturedLocked(null);
481 }
482
483 boolean result = hardwareImpl.startCapture(surface, config);
484 if (result) {
485 connection.setOnFirstFrameCapturedLocked(new Runnable() {
486 @Override
487 public void run() {
488 hardwareImpl.stopCapture(config);
489 }
490 });
491 }
492 return result;
493 }
494 }
495 return false;
496 }
497
Wonsik Kimd922a542014-07-24 18:25:29 +0900498 private void processPendingHdmiDeviceEventsLocked() {
499 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
500 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900501 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900502 TvInputHardwareInfo hardwareInfo =
503 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
504 if (hardwareInfo != null) {
505 msg.sendToTarget();
506 it.remove();
507 }
508 }
509 }
510
Wonsik Kim49e55932014-12-04 17:22:09 +0900511 private void updateVolume() {
512 mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
513 mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
514 }
515
516 private void handleVolumeChange(Context context, Intent intent) {
517 String action = intent.getAction();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700518 switch (action) {
519 case AudioManager.VOLUME_CHANGED_ACTION: {
520 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
521 if (streamType != AudioManager.STREAM_MUSIC) {
522 return;
523 }
524 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
525 if (index == mCurrentIndex) {
526 return;
527 }
528 mCurrentIndex = index;
529 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900530 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700531 case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
532 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
533 if (streamType != AudioManager.STREAM_MUSIC) {
534 return;
535 }
536 // volume index will be updated at onMediaStreamVolumeChanged() through
537 // updateVolume().
538 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900539 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700540 default:
541 Slog.w(TAG, "Unrecognized intent: " + intent);
Wonsik Kim49e55932014-12-04 17:22:09 +0900542 return;
Wonsik Kim49e55932014-12-04 17:22:09 +0900543 }
544 synchronized (mLock) {
545 for (int i = 0; i < mConnections.size(); ++i) {
546 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
547 if (hardwareImpl != null) {
548 hardwareImpl.onMediaStreamVolumeChanged();
549 }
550 }
551 }
552 }
553
554 private float getMediaStreamVolume() {
John Spurlockee5ad722015-03-03 16:17:21 -0500555 return (float) mCurrentIndex / (float) mCurrentMaxIndex;
Wonsik Kim49e55932014-12-04 17:22:09 +0900556 }
557
yangren8d718e12016-02-15 17:02:49 +0900558 public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
559 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
560 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
561 != PackageManager.PERMISSION_GRANTED) {
562 pw.println("Permission Denial: can't dump TvInputHardwareManager from pid="
563 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
564 return;
565 }
566
567 synchronized (mLock) {
568 pw.println("TvInputHardwareManager Info:");
569 pw.increaseIndent();
570 pw.println("mConnections: deviceId -> Connection");
571 pw.increaseIndent();
572 for (int i = 0; i < mConnections.size(); i++) {
573 int deviceId = mConnections.keyAt(i);
574 Connection mConnection = mConnections.valueAt(i);
575 pw.println(deviceId + ": " + mConnection);
576
577 }
578 pw.decreaseIndent();
579
580 pw.println("mHardwareList:");
581 pw.increaseIndent();
582 for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
583 pw.println(tvInputHardwareInfo);
584 }
585 pw.decreaseIndent();
586
587 pw.println("mHdmiDeviceList:");
588 pw.increaseIndent();
589 for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
590 pw.println(hdmiDeviceInfo);
591 }
592 pw.decreaseIndent();
593
594 pw.println("mHardwareInputIdMap: deviceId -> inputId");
595 pw.increaseIndent();
596 for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
597 int deviceId = mHardwareInputIdMap.keyAt(i);
598 String inputId = mHardwareInputIdMap.valueAt(i);
599 pw.println(deviceId + ": " + inputId);
600 }
601 pw.decreaseIndent();
602
603 pw.println("mHdmiInputIdMap: id -> inputId");
604 pw.increaseIndent();
605 for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
606 int id = mHdmiInputIdMap.keyAt(i);
607 String inputId = mHdmiInputIdMap.valueAt(i);
608 pw.println(id + ": " + inputId);
609 }
610 pw.decreaseIndent();
611
612 pw.println("mInputMap: inputId -> inputInfo");
613 pw.increaseIndent();
614 for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
615 pw.println(entry.getKey() + ": " + entry.getValue());
616 }
617 pw.decreaseIndent();
618 pw.decreaseIndent();
619 }
620 }
621
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000622 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900623 private final TvInputHardwareInfo mHardwareInfo;
624 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000625 private TvInputHardwareImpl mHardware = null;
626 private ITvInputHardwareCallback mCallback;
627 private TvStreamConfig[] mConfigs = null;
628 private Integer mCallingUid = null;
629 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900630 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000631
Wonsik Kim969167d2014-06-24 16:33:17 +0900632 public Connection(TvInputHardwareInfo hardwareInfo) {
633 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000634 }
635
636 // *Locked methods assume TvInputHardwareManager.mLock is held.
637
Wonsik Kim969167d2014-06-24 16:33:17 +0900638 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
639 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000640 if (mHardware != null) {
641 try {
642 mCallback.onReleased();
643 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900644 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000645 }
646 mHardware.release();
647 }
648 mHardware = hardware;
649 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900650 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000651 mCallingUid = callingUid;
652 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900653 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000654
655 if (mHardware != null && mCallback != null) {
656 try {
657 mCallback.onStreamConfigChanged(getConfigsLocked());
658 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900659 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000660 }
661 }
662 }
663
664 public void updateConfigsLocked(TvStreamConfig[] configs) {
665 mConfigs = configs;
666 }
667
Wonsik Kim969167d2014-06-24 16:33:17 +0900668 public TvInputHardwareInfo getHardwareInfoLocked() {
669 return mHardwareInfo;
670 }
671
672 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000673 return mInfo;
674 }
675
676 public ITvInputHardware getHardwareLocked() {
677 return mHardware;
678 }
679
Terry Heoc086a3d2014-06-18 14:26:44 +0900680 public TvInputHardwareImpl getHardwareImplLocked() {
681 return mHardware;
682 }
683
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000684 public ITvInputHardwareCallback getCallbackLocked() {
685 return mCallback;
686 }
687
688 public TvStreamConfig[] getConfigsLocked() {
689 return mConfigs;
690 }
691
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900692 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000693 return mCallingUid;
694 }
695
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900696 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000697 return mResolvedUserId;
698 }
699
Terry Heoc086a3d2014-06-18 14:26:44 +0900700 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
701 mOnFirstFrameCaptured = runnable;
702 }
703
704 public Runnable getOnFirstFrameCapturedLocked() {
705 return mOnFirstFrameCaptured;
706 }
707
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000708 @Override
709 public void binderDied() {
710 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900711 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000712 }
713 }
yangren8d718e12016-02-15 17:02:49 +0900714
715 public String toString() {
716 return "Connection{"
717 + " mHardwareInfo: " + mHardwareInfo
718 + ", mInfo: " + mInfo
719 + ", mCallback: " + mCallback
720 + ", mConfigs: " + Arrays.toString(mConfigs)
721 + ", mCallingUid: " + mCallingUid
722 + ", mResolvedUserId: " + mResolvedUserId
723 + " }";
724 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000725 }
726
727 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
728 private final TvInputHardwareInfo mInfo;
729 private boolean mReleased = false;
730 private final Object mImplLock = new Object();
731
Wonsik Kim06c41a32014-08-04 18:57:47 +0900732 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
733 new AudioManager.OnAudioPortUpdateListener() {
734 @Override
735 public void onAudioPortListUpdate(AudioPort[] portList) {
736 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900737 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900738 }
739 }
740
741 @Override
742 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
743 // No-op
744 }
745
746 @Override
747 public void onServiceDied() {
748 synchronized (mImplLock) {
749 mAudioSource = null;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900750 mAudioSink.clear();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900751 mAudioPatch = null;
752 }
753 }
754 };
755 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
756 private String mOverrideAudioAddress = "";
757 private AudioDevicePort mAudioSource;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900758 private List<AudioDevicePort> mAudioSink = new ArrayList<>();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900759 private AudioPatch mAudioPatch = null;
Wonsik Kim485e7e12015-01-26 17:28:55 +0900760 // Set to an invalid value for a volume, so that current volume can be applied at the
761 // first call to updateAudioConfigLocked().
762 private float mCommittedVolume = -1f;
Wonsik Kim49e55932014-12-04 17:22:09 +0900763 private float mSourceVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900764
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900765 private TvStreamConfig mActiveConfig = null;
766
Wonsik Kimca17a902014-07-31 10:09:46 +0900767 private int mDesiredSamplingRate = 0;
768 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
769 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
770
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000771 public TvInputHardwareImpl(TvInputHardwareInfo info) {
772 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900773 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900774 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900775 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900776 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kimca17a902014-07-31 10:09:46 +0900777 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900778 }
779
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900780 private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
781 sinks.clear();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700782 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900783 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
784 return;
785 }
786 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
Paul McLeanceb47aa2015-02-19 15:09:53 -0800787 for (AudioDevicePort port : devicePorts) {
Wally Yauad36f6472015-10-06 11:33:19 -0700788 if ((port.type() & sinkDevice) != 0 &&
789 (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
Paul McLeanceb47aa2015-02-19 15:09:53 -0800790 sinks.add(port);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900791 }
792 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900793 }
794
795 private AudioDevicePort findAudioDevicePort(int type, String address) {
796 if (type == AudioManager.DEVICE_NONE) {
797 return null;
798 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700799 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900800 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
801 return null;
802 }
Paul McLeanceb47aa2015-02-19 15:09:53 -0800803 for (AudioDevicePort port : devicePorts) {
804 if (port.type() == type && port.address().equals(address)) {
805 return port;
Wonsik Kimca17a902014-07-31 10:09:46 +0900806 }
807 }
808 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000809 }
810
811 public void release() {
812 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900813 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900814 if (mAudioPatch != null) {
815 mAudioManager.releaseAudioPatch(mAudioPatch);
816 mAudioPatch = null;
817 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000818 mReleased = true;
819 }
820 }
821
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900822 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
823 // attempts to call setSurface with different TvStreamConfig objects, the last call will
824 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000825 @Override
826 public boolean setSurface(Surface surface, TvStreamConfig config)
827 throws RemoteException {
828 synchronized (mImplLock) {
829 if (mReleased) {
830 throw new IllegalStateException("Device already released.");
831 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900832
Wonsik Kim102670f2014-11-20 14:38:02 +0900833 int result = TvInputHal.SUCCESS;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900834 if (surface == null) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900835 // The value of config is ignored when surface == null.
836 if (mActiveConfig != null) {
837 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
838 mActiveConfig = null;
839 } else {
840 // We already have no active stream.
841 return true;
842 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900843 } else {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900844 // It's impossible to set a non-null surface with a null config.
845 if (config == null) {
846 return false;
847 }
848 // Remove stream only if we have an existing active configuration.
849 if (mActiveConfig != null && !config.equals(mActiveConfig)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900850 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
851 if (result != TvInputHal.SUCCESS) {
852 mActiveConfig = null;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900853 }
854 }
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900855 // Proceed only if all previous operations succeeded.
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900856 if (result == TvInputHal.SUCCESS) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900857 result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
858 if (result == TvInputHal.SUCCESS) {
859 mActiveConfig = config;
860 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900861 }
862 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900863 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900864 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000865 }
866 }
867
Wonsik Kim7587d292014-08-09 10:01:01 +0900868 /**
869 * Update audio configuration (source, sink, patch) all up to current state.
870 */
871 private void updateAudioConfigLocked() {
872 boolean sinkUpdated = updateAudioSinkLocked();
873 boolean sourceUpdated = updateAudioSourceLocked();
874 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
875 // because Java won't evaluate the latter if the former is true.
876
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900877 if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900878 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900879 mAudioManager.releaseAudioPatch(mAudioPatch);
880 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900881 }
882 return;
883 }
884
Wonsik Kim49e55932014-12-04 17:22:09 +0900885 updateVolume();
886 float volume = mSourceVolume * getMediaStreamVolume();
Wonsik Kim8e45a332014-08-04 10:17:18 +0900887 AudioGainConfig sourceGainConfig = null;
Wonsik Kim49e55932014-12-04 17:22:09 +0900888 if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
Wonsik Kim8e45a332014-08-04 10:17:18 +0900889 AudioGain sourceGain = null;
890 for (AudioGain gain : mAudioSource.gains()) {
891 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
892 sourceGain = gain;
893 break;
894 }
895 }
896 // NOTE: we only change the source gain in MODE_JOINT here.
897 if (sourceGain != null) {
898 int steps = (sourceGain.maxValue() - sourceGain.minValue())
899 / sourceGain.stepValue();
900 int gainValue = sourceGain.minValue();
Wonsik Kim49e55932014-12-04 17:22:09 +0900901 if (volume < 1.0f) {
902 gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900903 } else {
904 gainValue = sourceGain.maxValue();
905 }
Wonsik Kim49e55932014-12-04 17:22:09 +0900906 // size of gain values is 1 in MODE_JOINT
907 int[] gainValues = new int[] { gainValue };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900908 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
909 sourceGain.channelMask(), gainValues, 0);
910 } else {
911 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
912 }
913 }
914
Wonsik Kimca17a902014-07-31 10:09:46 +0900915 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900916 List<AudioPortConfig> sinkConfigs = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900917 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900918 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900919
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900920 for (AudioDevicePort audioSink : mAudioSink) {
921 AudioPortConfig sinkConfig = audioSink.activeConfig();
922 int sinkSamplingRate = mDesiredSamplingRate;
923 int sinkChannelMask = mDesiredChannelMask;
924 int sinkFormat = mDesiredFormat;
925 // If sinkConfig != null and values are set to default,
926 // fill in the sinkConfig values.
927 if (sinkConfig != null) {
928 if (sinkSamplingRate == 0) {
929 sinkSamplingRate = sinkConfig.samplingRate();
930 }
931 if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
932 sinkChannelMask = sinkConfig.channelMask();
933 }
934 if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
935 sinkChannelMask = sinkConfig.format();
936 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900937 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900938
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900939 if (sinkConfig == null
940 || sinkConfig.samplingRate() != sinkSamplingRate
941 || sinkConfig.channelMask() != sinkChannelMask
942 || sinkConfig.format() != sinkFormat) {
943 // Check for compatibility and reset to default if necessary.
944 if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
945 && audioSink.samplingRates().length > 0) {
946 sinkSamplingRate = audioSink.samplingRates()[0];
947 }
948 if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
949 sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
950 }
951 if (!intArrayContains(audioSink.formats(), sinkFormat)) {
952 sinkFormat = AudioFormat.ENCODING_DEFAULT;
953 }
954 sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
955 sinkFormat, null);
956 shouldRecreateAudioPatch = true;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900957 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900958 sinkConfigs.add(sinkConfig);
Wonsik Kimca17a902014-07-31 10:09:46 +0900959 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900960 // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
961 // non-empty at the beginning of this method.
962 AudioPortConfig sinkConfig = sinkConfigs.get(0);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900963 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kim71dfa962014-11-15 14:18:49 +0900964 int sourceSamplingRate = 0;
965 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
966 sourceSamplingRate = sinkConfig.samplingRate();
967 } else if (mAudioSource.samplingRates().length > 0) {
968 // Use any sampling rate and hope audio patch can handle resampling...
969 sourceSamplingRate = mAudioSource.samplingRates()[0];
970 }
971 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
972 for (int inChannelMask : mAudioSource.channelMasks()) {
973 if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
974 == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
975 sourceChannelMask = inChannelMask;
976 break;
977 }
978 }
979 int sourceFormat = AudioFormat.ENCODING_DEFAULT;
980 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
981 sourceFormat = sinkConfig.format();
982 }
983 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
984 sourceFormat, sourceGainConfig);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900985 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900986 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900987 if (shouldRecreateAudioPatch) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900988 mCommittedVolume = volume;
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 }
Wonsik Kim102d0b72015-11-26 18:51:09 +09001202 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
1203 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
1204 // denote unknown state.
1205 int state = event.isConnected()
1206 ? INPUT_STATE_CONNECTED
1207 : INPUT_STATE_CONNECTED_STANDBY;
1208 mHandler.obtainMessage(
1209 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001210 }
1211 }
1212 }
1213
Wonsik Kim187423c2014-06-25 14:12:48 +09001214 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1215 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001216 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Jinsuk Kim98d760e2014-12-23 07:01:51 +09001217 if (!deviceInfo.isSourceType()) return;
Wonsik Kimd922a542014-07-24 18:25:29 +09001218 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001219 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +09001220 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001221 switch (status) {
1222 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001223 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001224 mHdmiDeviceList.add(deviceInfo);
1225 } else {
1226 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
1227 return;
1228 }
1229 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001230 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001231 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001232 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001233 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001234 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1235 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001236 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1237 return;
1238 }
1239 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001240 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001241 break;
1242 }
1243 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001244 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1245 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001246 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1247 return;
1248 }
1249 mHdmiDeviceList.add(deviceInfo);
1250 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001251 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001252 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001253 }
1254 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001255
Wonsik Kime92f8572014-08-12 18:30:24 +09001256 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +09001257 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1258 msg.sendToTarget();
1259 } else {
1260 mPendingHdmiDeviceEvents.add(msg);
1261 }
1262 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001263 }
Jungshik Jangcf445872014-08-20 15:06:01 +09001264
1265 private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1266 for (HdmiDeviceInfo info : mHdmiDeviceList) {
1267 if (info.getId() == id) {
1268 return info;
1269 }
1270 }
1271 return null;
1272 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001273 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001274
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001275 private final class HdmiSystemAudioModeChangeListener extends
1276 IHdmiSystemAudioModeChangeListener.Stub {
1277 @Override
1278 public void onStatusChanged(boolean enabled) throws RemoteException {
1279 synchronized (mLock) {
1280 for (int i = 0; i < mConnections.size(); ++i) {
1281 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
Jungshik Jang44bb3a52014-08-26 17:24:24 +09001282 if (impl != null) {
1283 impl.handleAudioSinkUpdated();
1284 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001285 }
1286 }
1287 }
1288 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001289}