blob: 8f71943129fa6ef0ec24e092b0c6423e23ea233e [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;
Shubang71d5c762017-02-23 15:46:40 -080020import 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;
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;
Amyed55b9c2020-01-18 19:46:07 -080047import android.media.tv.TvInputService.PriorityHintUseCaseType;
Jae Seod5cc4a22014-05-30 16:57:43 -070048import android.media.tv.TvStreamConfig;
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.Surface;
59
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060060import com.android.internal.util.DumpUtils;
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
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900112 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900113 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900114
Wonsik Kim187423c2014-06-25 14:12:48 +0900115 // Calls to mListener should happen here.
116 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000117
118 private final Object mLock = new Object();
119
Wonsik Kim187423c2014-06-25 14:12:48 +0900120 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900121 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900122 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900123 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000124 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900125 }
126
127 public void onBootPhase(int phase) {
128 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Jae Seo38b32572015-06-05 14:43:11 -0700129 IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
130 ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
131 if (hdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900132 try {
Jae Seo38b32572015-06-05 14:43:11 -0700133 hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
134 hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
135 hdmiControlService.addSystemAudioModeChangeListener(
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900136 mHdmiSystemAudioModeChangeListener);
Jae Seo38b32572015-06-05 14:43:11 -0700137 mHdmiDeviceList.addAll(hdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900138 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900139 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900140 }
Jinsuk Kim08f1ab02014-10-13 10:38:16 +0900141 } else {
142 Slog.w(TAG, "HdmiControlService is not available");
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900143 }
John Spurlockee5ad722015-03-03 16:17:21 -0500144 final IntentFilter filter = new IntentFilter();
145 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
146 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
147 mContext.registerReceiver(mVolumeReceiver, filter);
Wonsik Kim49e55932014-12-04 17:22:09 +0900148 updateVolume();
Wonsik Kim969167d2014-06-24 16:33:17 +0900149 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000150 }
151
152 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900153 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000154 synchronized (mLock) {
155 Connection connection = new Connection(info);
156 connection.updateConfigsLocked(configs);
157 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900158 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900159 mHandler.obtainMessage(
160 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900161 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
162 processPendingHdmiDeviceEventsLocked();
163 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000164 }
165 }
166
Wonsik Kimd922a542014-07-24 18:25:29 +0900167 private void buildHardwareListLocked() {
168 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000169 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900170 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000171 }
172 }
173
174 @Override
175 public void onDeviceUnavailable(int deviceId) {
176 synchronized (mLock) {
177 Connection connection = mConnections.get(deviceId);
178 if (connection == null) {
179 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
180 return;
181 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900182 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000183 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900184 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900185 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900186 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700187 // Remove HDMI devices linked with this hardware.
188 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900189 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900190 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700191 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900192 deviceInfo).sendToTarget();
193 it.remove();
194 }
195 }
196 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900197 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900198 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000199 }
200 }
201
202 @Override
203 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
204 synchronized (mLock) {
205 Connection connection = mConnections.get(deviceId);
206 if (connection == null) {
207 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
208 + deviceId);
209 return;
210 }
Shubang71d5c762017-02-23 15:46:40 -0800211 int previousConfigsLength = connection.getConfigsLengthLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000212 connection.updateConfigsLocked(configs);
Wonsik Kim9a103652014-10-21 17:46:31 +0900213 String inputId = mHardwareInputIdMap.get(deviceId);
Shubang71d5c762017-02-23 15:46:40 -0800214 if (inputId != null
215 && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) {
Wonsik Kim9a103652014-10-21 17:46:31 +0900216 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Shubang71d5c762017-02-23 15:46:40 -0800217 connection.getInputStateLocked(), 0, inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900218 }
Dongwon Kang017bb852014-12-26 14:53:14 +0900219 ITvInputHardwareCallback callback = connection.getCallbackLocked();
220 if (callback != null) {
221 try {
222 callback.onStreamConfigChanged(configs);
223 } catch (RemoteException e) {
224 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
225 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000226 }
227 }
228 }
229
Terry Heoc086a3d2014-06-18 14:26:44 +0900230 @Override
231 public void onFirstFrameCaptured(int deviceId, int streamId) {
232 synchronized (mLock) {
233 Connection connection = mConnections.get(deviceId);
234 if (connection == null) {
235 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
236 + deviceId);
237 return;
238 }
239 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
240 if (runnable != null) {
241 runnable.run();
242 connection.setOnFirstFrameCapturedLocked(null);
243 }
244 }
245 }
246
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000247 public List<TvInputHardwareInfo> getHardwareList() {
248 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900249 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000250 }
251 }
252
Jae Seo546c6352014-08-07 11:57:01 -0700253 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900254 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700255 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900256 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900257 }
258
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900259 private boolean checkUidChangedLocked(
260 Connection connection, int callingUid, int resolvedUserId) {
261 Integer connectionCallingUid = connection.getCallingUidLocked();
262 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700263 return connectionCallingUid == null || connectionResolvedUserId == null
264 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900265 }
266
Jae Seo1abbbcd2016-01-28 22:20:41 -0800267 public void addHardwareInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900268 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900269 String oldInputId = mHardwareInputIdMap.get(deviceId);
270 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900271 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900272 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900273 + info + ":" + deviceId);
274 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900275 mHardwareInputIdMap.put(deviceId, info.getId());
276 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900277
Wonsik Kim9a103652014-10-21 17:46:31 +0900278 // Process pending state changes
279
280 // For logical HDMI devices, they have information from HDMI CEC signals.
Wonsik Kim969167d2014-06-24 16:33:17 +0900281 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900282 TvInputHardwareInfo hardwareInfo =
283 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
284 if (hardwareInfo == null) {
285 continue;
286 }
287 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900288 if (inputId != null && inputId.equals(info.getId())) {
Shubang71d5c762017-02-23 15:46:40 -0800289 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
290 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
291 // denote unknown state.
292 int state = mHdmiStateMap.valueAt(i)
293 ? INPUT_STATE_CONNECTED
294 : INPUT_STATE_CONNECTED_STANDBY;
295 mHandler.obtainMessage(
296 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900297 return;
Wonsik Kim969167d2014-06-24 16:33:17 +0900298 }
299 }
Shubang71d5c762017-02-23 15:46:40 -0800300 // For the rest of the devices, we can tell by the cable connection status.
Wonsik Kim9a103652014-10-21 17:46:31 +0900301 Connection connection = mConnections.get(deviceId);
302 if (connection != null) {
303 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Shubang71d5c762017-02-23 15:46:40 -0800304 connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900305 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900306 }
307 }
308
Wonsik Kim38feae92014-07-21 21:35:50 +0900309 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
310 for (int i = 0; i < map.size(); ++i) {
311 if (map.valueAt(i).equals(value)) {
312 return i;
313 }
314 }
315 return -1;
316 }
317
Wonsik Kim71dfa962014-11-15 14:18:49 +0900318 private static boolean intArrayContains(int[] array, int value) {
319 for (int element : array) {
320 if (element == value) return true;
321 }
322 return false;
323 }
324
Jae Seo1abbbcd2016-01-28 22:20:41 -0800325 public void addHdmiInput(int id, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900326 if (info.getType() != TvInputInfo.TYPE_HDMI) {
327 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
328 }
329 synchronized (mLock) {
330 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900331 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
332 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900333 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
334 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900335 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900336 if (oldInputId != null) {
337 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900338 + mInputMap.get(oldInputId) + ":" + id + ", new = "
339 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900340 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900341 mHdmiInputIdMap.put(id, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900342 mInputMap.put(info.getId(), info);
343 }
344 }
345
Jae Seo1abbbcd2016-01-28 22:20:41 -0800346 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900347 synchronized (mLock) {
348 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900349 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900350 if (hardwareIndex >= 0) {
351 mHardwareInputIdMap.removeAt(hardwareIndex);
352 }
Jae Seo546c6352014-08-07 11:57:01 -0700353 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
354 if (deviceIndex >= 0) {
355 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900356 }
357 }
358 }
359
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000360 /**
361 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
362 * the object, and if more than one process attempts to create hardware with the same deviceId,
363 * the latest service will get the object and all the other hardware are released. The
364 * release is notified via ITvInputHardwareCallback.onReleased().
365 */
366 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Amyed55b9c2020-01-18 19:46:07 -0800367 TvInputInfo info, int callingUid, int resolvedUserId,
368 String tvInputSessionId, @PriorityHintUseCaseType int priorityHint) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000369 if (callback == null) {
370 throw new NullPointerException();
371 }
372 synchronized (mLock) {
373 Connection connection = mConnections.get(deviceId);
374 if (connection == null) {
375 Slog.e(TAG, "Invalid deviceId : " + deviceId);
376 return null;
377 }
Amyed55b9c2020-01-18 19:46:07 -0800378 // TODO: check with TRM to compare the client's priority with the current holder's
379 // priority. If lower, do nothing.
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 }
Kyeongkab.Namc94937c2019-01-16 11:12:23 +0900410 ITvInputHardwareCallback callback = connection.getCallbackLocked();
411 if (callback != null) {
412 callback.asBinder().unlinkToDeath(connection, 0);
413 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900414 connection.resetLocked(null, null, null, null, null);
415 }
416 }
417
Wonsik Kimd922a542014-07-24 18:25:29 +0900418 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
419 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900420 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
421 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900422 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900423 }
424 }
425 return null;
426 }
427
Terry Heoc086a3d2014-06-18 14:26:44 +0900428 private int findDeviceIdForInputIdLocked(String inputId) {
429 for (int i = 0; i < mConnections.size(); ++i) {
430 Connection connection = mConnections.get(i);
431 if (connection.getInfoLocked().getId().equals(inputId)) {
432 return i;
433 }
434 }
435 return -1;
436 }
437
438 /**
439 * Get the list of TvStreamConfig which is buffered mode.
440 */
441 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
442 int resolvedUserId) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700443 List<TvStreamConfig> configsList = new ArrayList<>();
Terry Heoc086a3d2014-06-18 14:26:44 +0900444 synchronized (mLock) {
445 int deviceId = findDeviceIdForInputIdLocked(inputId);
446 if (deviceId < 0) {
447 Slog.e(TAG, "Invalid inputId : " + inputId);
448 return configsList;
449 }
450 Connection connection = mConnections.get(deviceId);
451 for (TvStreamConfig config : connection.getConfigsLocked()) {
452 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
453 configsList.add(config);
454 }
455 }
456 }
457 return configsList;
458 }
459
460 /**
461 * Take a snapshot of the given TV input into the provided Surface.
462 */
463 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
464 int callingUid, int resolvedUserId) {
465 synchronized (mLock) {
466 int deviceId = findDeviceIdForInputIdLocked(inputId);
467 if (deviceId < 0) {
468 Slog.e(TAG, "Invalid inputId : " + inputId);
469 return false;
470 }
471 Connection connection = mConnections.get(deviceId);
472 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
473 if (hardwareImpl != null) {
474 // Stop previous capture.
475 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
476 if (runnable != null) {
477 runnable.run();
478 connection.setOnFirstFrameCapturedLocked(null);
479 }
480
481 boolean result = hardwareImpl.startCapture(surface, config);
482 if (result) {
483 connection.setOnFirstFrameCapturedLocked(new Runnable() {
484 @Override
485 public void run() {
486 hardwareImpl.stopCapture(config);
487 }
488 });
489 }
490 return result;
491 }
492 }
493 return false;
494 }
495
Wonsik Kimd922a542014-07-24 18:25:29 +0900496 private void processPendingHdmiDeviceEventsLocked() {
497 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
498 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900499 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900500 TvInputHardwareInfo hardwareInfo =
501 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
502 if (hardwareInfo != null) {
503 msg.sendToTarget();
504 it.remove();
505 }
506 }
507 }
508
Wonsik Kim49e55932014-12-04 17:22:09 +0900509 private void updateVolume() {
510 mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
511 mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
512 }
513
514 private void handleVolumeChange(Context context, Intent intent) {
515 String action = intent.getAction();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700516 switch (action) {
517 case AudioManager.VOLUME_CHANGED_ACTION: {
518 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
519 if (streamType != AudioManager.STREAM_MUSIC) {
520 return;
521 }
522 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
523 if (index == mCurrentIndex) {
524 return;
525 }
526 mCurrentIndex = index;
527 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900528 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700529 case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
530 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
531 if (streamType != AudioManager.STREAM_MUSIC) {
532 return;
533 }
534 // volume index will be updated at onMediaStreamVolumeChanged() through
535 // updateVolume().
536 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900537 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700538 default:
539 Slog.w(TAG, "Unrecognized intent: " + intent);
Wonsik Kim49e55932014-12-04 17:22:09 +0900540 return;
Wonsik Kim49e55932014-12-04 17:22:09 +0900541 }
542 synchronized (mLock) {
543 for (int i = 0; i < mConnections.size(); ++i) {
544 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
545 if (hardwareImpl != null) {
546 hardwareImpl.onMediaStreamVolumeChanged();
547 }
548 }
549 }
550 }
551
552 private float getMediaStreamVolume() {
John Spurlockee5ad722015-03-03 16:17:21 -0500553 return (float) mCurrentIndex / (float) mCurrentMaxIndex;
Wonsik Kim49e55932014-12-04 17:22:09 +0900554 }
555
yangren8d718e12016-02-15 17:02:49 +0900556 public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
557 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600558 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
yangren8d718e12016-02-15 17:02:49 +0900559
560 synchronized (mLock) {
561 pw.println("TvInputHardwareManager Info:");
562 pw.increaseIndent();
563 pw.println("mConnections: deviceId -> Connection");
564 pw.increaseIndent();
565 for (int i = 0; i < mConnections.size(); i++) {
566 int deviceId = mConnections.keyAt(i);
567 Connection mConnection = mConnections.valueAt(i);
568 pw.println(deviceId + ": " + mConnection);
569
570 }
571 pw.decreaseIndent();
572
573 pw.println("mHardwareList:");
574 pw.increaseIndent();
575 for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
576 pw.println(tvInputHardwareInfo);
577 }
578 pw.decreaseIndent();
579
580 pw.println("mHdmiDeviceList:");
581 pw.increaseIndent();
582 for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
583 pw.println(hdmiDeviceInfo);
584 }
585 pw.decreaseIndent();
586
587 pw.println("mHardwareInputIdMap: deviceId -> inputId");
588 pw.increaseIndent();
589 for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
590 int deviceId = mHardwareInputIdMap.keyAt(i);
591 String inputId = mHardwareInputIdMap.valueAt(i);
592 pw.println(deviceId + ": " + inputId);
593 }
594 pw.decreaseIndent();
595
596 pw.println("mHdmiInputIdMap: id -> inputId");
597 pw.increaseIndent();
598 for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
599 int id = mHdmiInputIdMap.keyAt(i);
600 String inputId = mHdmiInputIdMap.valueAt(i);
601 pw.println(id + ": " + inputId);
602 }
603 pw.decreaseIndent();
604
605 pw.println("mInputMap: inputId -> inputInfo");
606 pw.increaseIndent();
607 for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
608 pw.println(entry.getKey() + ": " + entry.getValue());
609 }
610 pw.decreaseIndent();
611 pw.decreaseIndent();
612 }
613 }
614
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000615 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900616 private final TvInputHardwareInfo mHardwareInfo;
617 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000618 private TvInputHardwareImpl mHardware = null;
619 private ITvInputHardwareCallback mCallback;
620 private TvStreamConfig[] mConfigs = null;
621 private Integer mCallingUid = null;
622 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900623 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000624
Wonsik Kim969167d2014-06-24 16:33:17 +0900625 public Connection(TvInputHardwareInfo hardwareInfo) {
626 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000627 }
628
629 // *Locked methods assume TvInputHardwareManager.mLock is held.
630
Wonsik Kim969167d2014-06-24 16:33:17 +0900631 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
632 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000633 if (mHardware != null) {
634 try {
635 mCallback.onReleased();
636 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900637 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000638 }
639 mHardware.release();
640 }
641 mHardware = hardware;
642 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900643 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000644 mCallingUid = callingUid;
645 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900646 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000647
648 if (mHardware != null && mCallback != null) {
649 try {
650 mCallback.onStreamConfigChanged(getConfigsLocked());
651 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900652 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000653 }
654 }
655 }
656
657 public void updateConfigsLocked(TvStreamConfig[] configs) {
658 mConfigs = configs;
659 }
660
Wonsik Kim969167d2014-06-24 16:33:17 +0900661 public TvInputHardwareInfo getHardwareInfoLocked() {
662 return mHardwareInfo;
663 }
664
665 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000666 return mInfo;
667 }
668
669 public ITvInputHardware getHardwareLocked() {
670 return mHardware;
671 }
672
Terry Heoc086a3d2014-06-18 14:26:44 +0900673 public TvInputHardwareImpl getHardwareImplLocked() {
674 return mHardware;
675 }
676
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000677 public ITvInputHardwareCallback getCallbackLocked() {
678 return mCallback;
679 }
680
681 public TvStreamConfig[] getConfigsLocked() {
682 return mConfigs;
683 }
684
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900685 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000686 return mCallingUid;
687 }
688
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900689 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000690 return mResolvedUserId;
691 }
692
Terry Heoc086a3d2014-06-18 14:26:44 +0900693 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
694 mOnFirstFrameCaptured = runnable;
695 }
696
697 public Runnable getOnFirstFrameCapturedLocked() {
698 return mOnFirstFrameCaptured;
699 }
700
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000701 @Override
702 public void binderDied() {
703 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900704 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000705 }
706 }
yangren8d718e12016-02-15 17:02:49 +0900707
708 public String toString() {
709 return "Connection{"
710 + " mHardwareInfo: " + mHardwareInfo
711 + ", mInfo: " + mInfo
712 + ", mCallback: " + mCallback
713 + ", mConfigs: " + Arrays.toString(mConfigs)
714 + ", mCallingUid: " + mCallingUid
715 + ", mResolvedUserId: " + mResolvedUserId
716 + " }";
717 }
Shubang71d5c762017-02-23 15:46:40 -0800718
719 private int getConfigsLengthLocked() {
720 return mConfigs == null ? 0 : mConfigs.length;
721 }
722
723 private int getInputStateLocked() {
724 int configsLength = getConfigsLengthLocked();
725 if (configsLength > 0) {
726 return INPUT_STATE_CONNECTED;
727 }
728 switch (mHardwareInfo.getCableConnectionStatus()) {
729 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED:
730 return INPUT_STATE_CONNECTED;
731 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED:
732 return INPUT_STATE_DISCONNECTED;
733 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN:
734 default:
735 return INPUT_STATE_CONNECTED_STANDBY;
736 }
737 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000738 }
739
740 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
741 private final TvInputHardwareInfo mInfo;
742 private boolean mReleased = false;
743 private final Object mImplLock = new Object();
744
Wonsik Kim06c41a32014-08-04 18:57:47 +0900745 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
746 new AudioManager.OnAudioPortUpdateListener() {
747 @Override
748 public void onAudioPortListUpdate(AudioPort[] portList) {
749 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900750 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900751 }
752 }
753
754 @Override
755 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
756 // No-op
757 }
758
759 @Override
760 public void onServiceDied() {
761 synchronized (mImplLock) {
762 mAudioSource = null;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900763 mAudioSink.clear();
Jae Seoee32ede2016-05-31 10:31:15 -0700764 if (mAudioPatch != null) {
765 mAudioManager.releaseAudioPatch(mAudioPatch);
766 mAudioPatch = null;
767 }
Wonsik Kim06c41a32014-08-04 18:57:47 +0900768 }
769 }
770 };
771 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
772 private String mOverrideAudioAddress = "";
773 private AudioDevicePort mAudioSource;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900774 private List<AudioDevicePort> mAudioSink = new ArrayList<>();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900775 private AudioPatch mAudioPatch = null;
Wonsik Kim485e7e12015-01-26 17:28:55 +0900776 // Set to an invalid value for a volume, so that current volume can be applied at the
777 // first call to updateAudioConfigLocked().
778 private float mCommittedVolume = -1f;
Wonsik Kim49e55932014-12-04 17:22:09 +0900779 private float mSourceVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900780
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900781 private TvStreamConfig mActiveConfig = null;
782
Wonsik Kimca17a902014-07-31 10:09:46 +0900783 private int mDesiredSamplingRate = 0;
784 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
785 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
786
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000787 public TvInputHardwareImpl(TvInputHardwareInfo info) {
788 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900789 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900790 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900791 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900792 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kimca17a902014-07-31 10:09:46 +0900793 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900794 }
795
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900796 private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
797 sinks.clear();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700798 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900799 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
800 return;
801 }
802 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
Paul McLeanceb47aa2015-02-19 15:09:53 -0800803 for (AudioDevicePort port : devicePorts) {
Wally Yauad36f6472015-10-06 11:33:19 -0700804 if ((port.type() & sinkDevice) != 0 &&
805 (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
Paul McLeanceb47aa2015-02-19 15:09:53 -0800806 sinks.add(port);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900807 }
808 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900809 }
810
811 private AudioDevicePort findAudioDevicePort(int type, String address) {
812 if (type == AudioManager.DEVICE_NONE) {
813 return null;
814 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700815 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900816 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
817 return null;
818 }
Paul McLeanceb47aa2015-02-19 15:09:53 -0800819 for (AudioDevicePort port : devicePorts) {
820 if (port.type() == type && port.address().equals(address)) {
821 return port;
Wonsik Kimca17a902014-07-31 10:09:46 +0900822 }
823 }
824 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000825 }
826
827 public void release() {
828 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900829 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900830 if (mAudioPatch != null) {
831 mAudioManager.releaseAudioPatch(mAudioPatch);
832 mAudioPatch = null;
833 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000834 mReleased = true;
835 }
836 }
837
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900838 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
839 // attempts to call setSurface with different TvStreamConfig objects, the last call will
840 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000841 @Override
842 public boolean setSurface(Surface surface, TvStreamConfig config)
843 throws RemoteException {
844 synchronized (mImplLock) {
845 if (mReleased) {
846 throw new IllegalStateException("Device already released.");
847 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900848
Wonsik Kim102670f2014-11-20 14:38:02 +0900849 int result = TvInputHal.SUCCESS;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900850 if (surface == null) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900851 // The value of config is ignored when surface == null.
852 if (mActiveConfig != null) {
853 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
854 mActiveConfig = null;
855 } else {
856 // We already have no active stream.
857 return true;
858 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900859 } else {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900860 // It's impossible to set a non-null surface with a null config.
861 if (config == null) {
862 return false;
863 }
864 // Remove stream only if we have an existing active configuration.
865 if (mActiveConfig != null && !config.equals(mActiveConfig)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900866 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
867 if (result != TvInputHal.SUCCESS) {
868 mActiveConfig = null;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900869 }
870 }
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900871 // Proceed only if all previous operations succeeded.
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900872 if (result == TvInputHal.SUCCESS) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900873 result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
874 if (result == TvInputHal.SUCCESS) {
875 mActiveConfig = config;
876 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900877 }
878 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900879 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900880 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000881 }
882 }
883
Wonsik Kim7587d292014-08-09 10:01:01 +0900884 /**
885 * Update audio configuration (source, sink, patch) all up to current state.
886 */
887 private void updateAudioConfigLocked() {
888 boolean sinkUpdated = updateAudioSinkLocked();
889 boolean sourceUpdated = updateAudioSourceLocked();
890 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
891 // because Java won't evaluate the latter if the former is true.
892
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900893 if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900894 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900895 mAudioManager.releaseAudioPatch(mAudioPatch);
896 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900897 }
898 return;
899 }
900
Wonsik Kim49e55932014-12-04 17:22:09 +0900901 updateVolume();
902 float volume = mSourceVolume * getMediaStreamVolume();
Wonsik Kim8e45a332014-08-04 10:17:18 +0900903 AudioGainConfig sourceGainConfig = null;
Wonsik Kim49e55932014-12-04 17:22:09 +0900904 if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
Wonsik Kim8e45a332014-08-04 10:17:18 +0900905 AudioGain sourceGain = null;
906 for (AudioGain gain : mAudioSource.gains()) {
907 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
908 sourceGain = gain;
909 break;
910 }
911 }
912 // NOTE: we only change the source gain in MODE_JOINT here.
913 if (sourceGain != null) {
914 int steps = (sourceGain.maxValue() - sourceGain.minValue())
915 / sourceGain.stepValue();
916 int gainValue = sourceGain.minValue();
Wonsik Kim49e55932014-12-04 17:22:09 +0900917 if (volume < 1.0f) {
918 gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900919 } else {
920 gainValue = sourceGain.maxValue();
921 }
Wonsik Kim49e55932014-12-04 17:22:09 +0900922 // size of gain values is 1 in MODE_JOINT
923 int[] gainValues = new int[] { gainValue };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900924 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
925 sourceGain.channelMask(), gainValues, 0);
926 } else {
927 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
928 }
929 }
930
Wonsik Kimca17a902014-07-31 10:09:46 +0900931 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900932 List<AudioPortConfig> sinkConfigs = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900933 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900934 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900935
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900936 for (AudioDevicePort audioSink : mAudioSink) {
937 AudioPortConfig sinkConfig = audioSink.activeConfig();
938 int sinkSamplingRate = mDesiredSamplingRate;
939 int sinkChannelMask = mDesiredChannelMask;
940 int sinkFormat = mDesiredFormat;
941 // If sinkConfig != null and values are set to default,
942 // fill in the sinkConfig values.
943 if (sinkConfig != null) {
944 if (sinkSamplingRate == 0) {
945 sinkSamplingRate = sinkConfig.samplingRate();
946 }
947 if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
948 sinkChannelMask = sinkConfig.channelMask();
949 }
950 if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
Kyeongkab.Nam7a40f702019-02-01 11:51:02 +0900951 sinkFormat = sinkConfig.format();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900952 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900953 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900954
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900955 if (sinkConfig == null
956 || sinkConfig.samplingRate() != sinkSamplingRate
957 || sinkConfig.channelMask() != sinkChannelMask
958 || sinkConfig.format() != sinkFormat) {
959 // Check for compatibility and reset to default if necessary.
960 if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
961 && audioSink.samplingRates().length > 0) {
962 sinkSamplingRate = audioSink.samplingRates()[0];
963 }
964 if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
965 sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
966 }
967 if (!intArrayContains(audioSink.formats(), sinkFormat)) {
968 sinkFormat = AudioFormat.ENCODING_DEFAULT;
969 }
970 sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
971 sinkFormat, null);
972 shouldRecreateAudioPatch = true;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900973 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900974 sinkConfigs.add(sinkConfig);
Wonsik Kimca17a902014-07-31 10:09:46 +0900975 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900976 // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
977 // non-empty at the beginning of this method.
978 AudioPortConfig sinkConfig = sinkConfigs.get(0);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900979 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kim71dfa962014-11-15 14:18:49 +0900980 int sourceSamplingRate = 0;
981 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
982 sourceSamplingRate = sinkConfig.samplingRate();
983 } else if (mAudioSource.samplingRates().length > 0) {
984 // Use any sampling rate and hope audio patch can handle resampling...
985 sourceSamplingRate = mAudioSource.samplingRates()[0];
986 }
987 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
988 for (int inChannelMask : mAudioSource.channelMasks()) {
989 if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
990 == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
991 sourceChannelMask = inChannelMask;
992 break;
993 }
994 }
995 int sourceFormat = AudioFormat.ENCODING_DEFAULT;
996 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
997 sourceFormat = sinkConfig.format();
998 }
999 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
1000 sourceFormat, sourceGainConfig);
Wonsik Kim8e45a332014-08-04 10:17:18 +09001001 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +09001002 }
Wonsik Kim8e45a332014-08-04 10:17:18 +09001003 if (shouldRecreateAudioPatch) {
Wonsik Kim49e55932014-12-04 17:22:09 +09001004 mCommittedVolume = volume;
Jae Seoee32ede2016-05-31 10:31:15 -07001005 if (mAudioPatch != null) {
1006 mAudioManager.releaseAudioPatch(mAudioPatch);
1007 }
Wonsik Kim8e45a332014-08-04 10:17:18 +09001008 mAudioManager.createAudioPatch(
1009 audioPatchArray,
1010 new AudioPortConfig[] { sourceConfig },
Jae Seo93ff14b2015-06-21 14:08:54 -07001011 sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
Wonsik Kim8e45a332014-08-04 10:17:18 +09001012 mAudioPatch = audioPatchArray[0];
Wonsik Kim71dfa962014-11-15 14:18:49 +09001013 if (sourceGainConfig != null) {
1014 mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
1015 }
Wonsik Kim8e45a332014-08-04 10:17:18 +09001016 }
Wonsik Kimca17a902014-07-31 10:09:46 +09001017 }
1018
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001019 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +09001020 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001021 synchronized (mImplLock) {
1022 if (mReleased) {
1023 throw new IllegalStateException("Device already released.");
1024 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001025 mSourceVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +09001026 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001027 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001028 }
1029
Terry Heoc086a3d2014-06-18 14:26:44 +09001030 private boolean startCapture(Surface surface, TvStreamConfig config) {
1031 synchronized (mImplLock) {
1032 if (mReleased) {
1033 return false;
1034 }
1035 if (surface == null || config == null) {
1036 return false;
1037 }
1038 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
1039 return false;
1040 }
1041
Wonsik Kim8f24a8b2014-10-22 16:27:39 +09001042 int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
Terry Heoc086a3d2014-06-18 14:26:44 +09001043 return result == TvInputHal.SUCCESS;
1044 }
1045 }
1046
1047 private boolean stopCapture(TvStreamConfig config) {
1048 synchronized (mImplLock) {
1049 if (mReleased) {
1050 return false;
1051 }
1052 if (config == null) {
1053 return false;
1054 }
1055
1056 int result = mHal.removeStream(mInfo.getDeviceId(), config);
1057 return result == TvInputHal.SUCCESS;
1058 }
1059 }
Wonsik Kimca17a902014-07-31 10:09:46 +09001060
Wonsik Kim7587d292014-08-09 10:01:01 +09001061 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001062 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001063 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001064 }
Wonsik Kim7587d292014-08-09 10:01:01 +09001065 AudioDevicePort previousSource = mAudioSource;
1066 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
1067 return mAudioSource == null ? (previousSource != null)
1068 : !mAudioSource.equals(previousSource);
1069 }
1070
1071 private boolean updateAudioSinkLocked() {
1072 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
1073 return false;
1074 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001075 List<AudioDevicePort> previousSink = mAudioSink;
1076 mAudioSink = new ArrayList<>();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001077 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001078 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001079 } else {
1080 AudioDevicePort audioSink =
1081 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
1082 if (audioSink != null) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001083 mAudioSink.add(audioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001084 }
1085 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001086
1087 // Returns true if mAudioSink and previousSink differs.
1088 if (mAudioSink.size() != previousSink.size()) {
1089 return true;
1090 }
1091 previousSink.removeAll(mAudioSink);
1092 return !previousSink.isEmpty();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001093 }
1094
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001095 private void handleAudioSinkUpdated() {
1096 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001097 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001098 }
1099 }
1100
Wonsik Kimca17a902014-07-31 10:09:46 +09001101 @Override
1102 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1103 int channelMask, int format) {
1104 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001105 mOverrideAudioType = audioType;
1106 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001107
Wonsik Kimca17a902014-07-31 10:09:46 +09001108 mDesiredSamplingRate = samplingRate;
1109 mDesiredChannelMask = channelMask;
1110 mDesiredFormat = format;
1111
Wonsik Kim7587d292014-08-09 10:01:01 +09001112 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +09001113 }
1114 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001115
1116 public void onMediaStreamVolumeChanged() {
1117 synchronized (mImplLock) {
1118 updateAudioConfigLocked();
1119 }
1120 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001121 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001122
Wonsik Kim187423c2014-06-25 14:12:48 +09001123 interface Listener {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001124 void onStateChanged(String inputId, int state);
1125 void onHardwareDeviceAdded(TvInputHardwareInfo info);
1126 void onHardwareDeviceRemoved(TvInputHardwareInfo info);
1127 void onHdmiDeviceAdded(HdmiDeviceInfo device);
1128 void onHdmiDeviceRemoved(HdmiDeviceInfo device);
1129 void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +09001130 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001131
Wonsik Kim187423c2014-06-25 14:12:48 +09001132 private class ListenerHandler extends Handler {
1133 private static final int STATE_CHANGED = 1;
1134 private static final int HARDWARE_DEVICE_ADDED = 2;
1135 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -07001136 private static final int HDMI_DEVICE_ADDED = 4;
1137 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001138 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +09001139
1140 @Override
1141 public final void handleMessage(Message msg) {
1142 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +09001143 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +09001144 String inputId = (String) msg.obj;
1145 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +09001146 mListener.onStateChanged(inputId, state);
1147 break;
1148 }
1149 case HARDWARE_DEVICE_ADDED: {
1150 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1151 mListener.onHardwareDeviceAdded(info);
1152 break;
1153 }
1154 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09001155 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1156 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001157 break;
1158 }
Jae Seo546c6352014-08-07 11:57:01 -07001159 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001160 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001161 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001162 break;
1163 }
Jae Seo546c6352014-08-07 11:57:01 -07001164 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001165 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001166 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +09001167 break;
1168 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001169 case HDMI_DEVICE_UPDATED: {
Wonsik Kim184a6d62014-10-08 13:05:56 +09001170 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001171 String inputId;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001172 synchronized (mLock) {
1173 inputId = mHdmiInputIdMap.get(info.getId());
1174 }
1175 if (inputId != null) {
1176 mListener.onHdmiDeviceUpdated(inputId, info);
1177 } else {
1178 Slog.w(TAG, "Could not resolve input ID matching the device info; "
1179 + "ignoring.");
1180 }
1181 break;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001182 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001183 default: {
1184 Slog.w(TAG, "Unhandled message: " + msg);
1185 break;
1186 }
1187 }
1188 }
1189 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001190
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001191 // Listener implementations for HdmiControlService
1192
1193 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
1194 @Override
1195 public void onReceived(HdmiHotplugEvent event) {
1196 synchronized (mLock) {
1197 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +09001198 TvInputHardwareInfo hardwareInfo =
1199 findHardwareInfoForHdmiPortLocked(event.getPort());
1200 if (hardwareInfo == null) {
1201 return;
1202 }
1203 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001204 if (inputId == null) {
1205 return;
1206 }
Shubang71d5c762017-02-23 15:46:40 -08001207 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
1208 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
1209 // denote unknown state.
1210 int state = event.isConnected()
1211 ? INPUT_STATE_CONNECTED
1212 : INPUT_STATE_CONNECTED_STANDBY;
1213 mHandler.obtainMessage(
1214 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001215 }
1216 }
1217 }
1218
Wonsik Kim187423c2014-06-25 14:12:48 +09001219 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1220 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001221 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Jinsuk Kim98d760e2014-12-23 07:01:51 +09001222 if (!deviceInfo.isSourceType()) return;
Wonsik Kimd922a542014-07-24 18:25:29 +09001223 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001224 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +09001225 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001226 switch (status) {
1227 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001228 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001229 mHdmiDeviceList.add(deviceInfo);
1230 } else {
1231 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
1232 return;
1233 }
1234 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001235 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001236 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001237 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001238 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001239 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1240 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001241 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1242 return;
1243 }
1244 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001245 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001246 break;
1247 }
1248 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001249 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1250 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001251 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1252 return;
1253 }
1254 mHdmiDeviceList.add(deviceInfo);
1255 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001256 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001257 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001258 }
1259 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001260
Wonsik Kime92f8572014-08-12 18:30:24 +09001261 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +09001262 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1263 msg.sendToTarget();
1264 } else {
1265 mPendingHdmiDeviceEvents.add(msg);
1266 }
1267 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001268 }
Jungshik Jangcf445872014-08-20 15:06:01 +09001269
1270 private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1271 for (HdmiDeviceInfo info : mHdmiDeviceList) {
1272 if (info.getId() == id) {
1273 return info;
1274 }
1275 }
1276 return null;
1277 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001278 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001279
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001280 private final class HdmiSystemAudioModeChangeListener extends
1281 IHdmiSystemAudioModeChangeListener.Stub {
1282 @Override
1283 public void onStatusChanged(boolean enabled) throws RemoteException {
1284 synchronized (mLock) {
1285 for (int i = 0; i < mConnections.size(); ++i) {
1286 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
Jungshik Jang44bb3a52014-08-26 17:24:24 +09001287 if (impl != null) {
1288 impl.handleAudioSinkUpdated();
1289 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001290 }
1291 }
1292 }
1293 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001294}