blob: f08e5857997510c8709cc619e402849e269d57d8 [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;
Jae Seod5cc4a22014-05-30 16:57:43 -070047import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090048import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000049import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090050import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000051import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090052import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090053import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000054import android.util.Slog;
55import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090056import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000057import android.view.Surface;
58
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060059import com.android.internal.util.DumpUtils;
yangren8d718e12016-02-15 17:02:49 +090060import com.android.internal.util.IndentingPrintWriter;
Wonsik Kim969167d2014-06-24 16:33:17 +090061import com.android.server.SystemService;
62
yangren8d718e12016-02-15 17:02:49 +090063import java.io.FileDescriptor;
64import java.io.PrintWriter;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000065import java.util.ArrayList;
yangren8d718e12016-02-15 17:02:49 +090066import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090067import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090068import java.util.Iterator;
69import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000070import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090071import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000072
73/**
74 * A helper class for TvInputManagerService to handle TV input hardware.
75 *
76 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
77 * calls to tv_input HAL module.
78 *
79 * @hide
80 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090081class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000082 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090083
Wonsik Kim49e55932014-12-04 17:22:09 +090084 private final Context mContext;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090085 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000086 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090087 private final SparseArray<Connection> mConnections = new SparseArray<>();
88 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070089 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090090 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090091 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090092 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070093 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090094 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090095
Wonsik Kimd7c29182014-05-27 10:38:21 +090096 private final AudioManager mAudioManager;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090097 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
98 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090099 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900100 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
101 new HdmiSystemAudioModeChangeListener();
Wonsik Kim49e55932014-12-04 17:22:09 +0900102 private final BroadcastReceiver mVolumeReceiver = new BroadcastReceiver() {
103 @Override
104 public void onReceive(Context context, Intent intent) {
105 handleVolumeChange(context, intent);
106 }
107 };
108 private int mCurrentIndex = 0;
109 private int mCurrentMaxIndex = 0;
Jinsuk Kimd38bf472014-08-11 11:29:18 +0900110
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900111 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900112 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900113
Wonsik Kim187423c2014-06-25 14:12:48 +0900114 // Calls to mListener should happen here.
115 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000116
117 private final Object mLock = new Object();
118
Wonsik Kim187423c2014-06-25 14:12:48 +0900119 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900120 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900121 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900122 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000123 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900124 }
125
126 public void onBootPhase(int phase) {
127 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Jae Seo38b32572015-06-05 14:43:11 -0700128 IHdmiControlService hdmiControlService = IHdmiControlService.Stub.asInterface(
129 ServiceManager.getService(Context.HDMI_CONTROL_SERVICE));
130 if (hdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900131 try {
Jae Seo38b32572015-06-05 14:43:11 -0700132 hdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
133 hdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
134 hdmiControlService.addSystemAudioModeChangeListener(
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900135 mHdmiSystemAudioModeChangeListener);
Jae Seo38b32572015-06-05 14:43:11 -0700136 mHdmiDeviceList.addAll(hdmiControlService.getInputDevices());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900137 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900138 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900139 }
Jinsuk Kim08f1ab02014-10-13 10:38:16 +0900140 } else {
141 Slog.w(TAG, "HdmiControlService is not available");
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900142 }
John Spurlockee5ad722015-03-03 16:17:21 -0500143 final IntentFilter filter = new IntentFilter();
144 filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
145 filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
146 mContext.registerReceiver(mVolumeReceiver, filter);
Wonsik Kim49e55932014-12-04 17:22:09 +0900147 updateVolume();
Wonsik Kim969167d2014-06-24 16:33:17 +0900148 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000149 }
150
151 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900152 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000153 synchronized (mLock) {
154 Connection connection = new Connection(info);
155 connection.updateConfigsLocked(configs);
156 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900157 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900158 mHandler.obtainMessage(
159 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900160 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
161 processPendingHdmiDeviceEventsLocked();
162 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000163 }
164 }
165
Wonsik Kimd922a542014-07-24 18:25:29 +0900166 private void buildHardwareListLocked() {
167 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000168 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900169 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000170 }
171 }
172
173 @Override
174 public void onDeviceUnavailable(int deviceId) {
175 synchronized (mLock) {
176 Connection connection = mConnections.get(deviceId);
177 if (connection == null) {
178 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
179 return;
180 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900181 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000182 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900183 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900184 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900185 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700186 // Remove HDMI devices linked with this hardware.
187 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900188 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900189 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700190 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900191 deviceInfo).sendToTarget();
192 it.remove();
193 }
194 }
195 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900196 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900197 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000198 }
199 }
200
201 @Override
202 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
203 synchronized (mLock) {
204 Connection connection = mConnections.get(deviceId);
205 if (connection == null) {
206 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
207 + deviceId);
208 return;
209 }
Shubang71d5c762017-02-23 15:46:40 -0800210 int previousConfigsLength = connection.getConfigsLengthLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000211 connection.updateConfigsLocked(configs);
Wonsik Kim9a103652014-10-21 17:46:31 +0900212 String inputId = mHardwareInputIdMap.get(deviceId);
Shubang71d5c762017-02-23 15:46:40 -0800213 if (inputId != null
214 && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) {
Wonsik Kim9a103652014-10-21 17:46:31 +0900215 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Shubang71d5c762017-02-23 15:46:40 -0800216 connection.getInputStateLocked(), 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
Jae Seo1abbbcd2016-01-28 22:20:41 -0800266 public void addHardwareInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900267 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900268 String oldInputId = mHardwareInputIdMap.get(deviceId);
269 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900270 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900271 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900272 + info + ":" + deviceId);
273 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900274 mHardwareInputIdMap.put(deviceId, info.getId());
275 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900276
Wonsik Kim9a103652014-10-21 17:46:31 +0900277 // Process pending state changes
278
279 // For logical HDMI devices, they have information from HDMI CEC signals.
Wonsik Kim969167d2014-06-24 16:33:17 +0900280 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900281 TvInputHardwareInfo hardwareInfo =
282 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
283 if (hardwareInfo == null) {
284 continue;
285 }
286 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900287 if (inputId != null && inputId.equals(info.getId())) {
Shubang71d5c762017-02-23 15:46:40 -0800288 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
289 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
290 // denote unknown state.
291 int state = mHdmiStateMap.valueAt(i)
292 ? INPUT_STATE_CONNECTED
293 : INPUT_STATE_CONNECTED_STANDBY;
294 mHandler.obtainMessage(
295 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900296 return;
Wonsik Kim969167d2014-06-24 16:33:17 +0900297 }
298 }
Shubang71d5c762017-02-23 15:46:40 -0800299 // For the rest of the devices, we can tell by the cable connection status.
Wonsik Kim9a103652014-10-21 17:46:31 +0900300 Connection connection = mConnections.get(deviceId);
301 if (connection != null) {
302 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Shubang71d5c762017-02-23 15:46:40 -0800303 connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
Wonsik Kim9a103652014-10-21 17:46:31 +0900304 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900305 }
306 }
307
Wonsik Kim38feae92014-07-21 21:35:50 +0900308 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
309 for (int i = 0; i < map.size(); ++i) {
310 if (map.valueAt(i).equals(value)) {
311 return i;
312 }
313 }
314 return -1;
315 }
316
Wonsik Kim71dfa962014-11-15 14:18:49 +0900317 private static boolean intArrayContains(int[] array, int value) {
318 for (int element : array) {
319 if (element == value) return true;
320 }
321 return false;
322 }
323
Jae Seo1abbbcd2016-01-28 22:20:41 -0800324 public void addHdmiInput(int id, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900325 if (info.getType() != TvInputInfo.TYPE_HDMI) {
326 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
327 }
328 synchronized (mLock) {
329 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900330 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
331 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900332 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
333 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900334 String oldInputId = mHdmiInputIdMap.get(id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900335 if (oldInputId != null) {
336 Slog.w(TAG, "Trying to override previous registration: old = "
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900337 + mInputMap.get(oldInputId) + ":" + id + ", new = "
338 + info + ":" + id);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900339 }
Jinsuk Kim8960d1b2014-08-13 10:48:30 +0900340 mHdmiInputIdMap.put(id, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900341 mInputMap.put(info.getId(), info);
342 }
343 }
344
Jae Seo1abbbcd2016-01-28 22:20:41 -0800345 public void removeHardwareInput(String inputId) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900346 synchronized (mLock) {
347 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900348 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900349 if (hardwareIndex >= 0) {
350 mHardwareInputIdMap.removeAt(hardwareIndex);
351 }
Jae Seo546c6352014-08-07 11:57:01 -0700352 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
353 if (deviceIndex >= 0) {
354 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900355 }
356 }
357 }
358
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000359 /**
360 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
361 * the object, and if more than one process attempts to create hardware with the same deviceId,
362 * the latest service will get the object and all the other hardware are released. The
363 * release is notified via ITvInputHardwareCallback.onReleased().
364 */
365 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900366 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000367 if (callback == null) {
368 throw new NullPointerException();
369 }
370 synchronized (mLock) {
371 Connection connection = mConnections.get(deviceId);
372 if (connection == null) {
373 Slog.e(TAG, "Invalid deviceId : " + deviceId);
374 return null;
375 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900376 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900377 TvInputHardwareImpl hardware =
378 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000379 try {
380 callback.asBinder().linkToDeath(connection, 0);
381 } catch (RemoteException e) {
382 hardware.release();
383 return null;
384 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900385 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000386 }
387 return connection.getHardwareLocked();
388 }
389 }
390
391 /**
392 * Release the specified hardware.
393 */
394 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
395 int resolvedUserId) {
396 synchronized (mLock) {
397 Connection connection = mConnections.get(deviceId);
398 if (connection == null) {
399 Slog.e(TAG, "Invalid deviceId : " + deviceId);
400 return;
401 }
402 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900403 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000404 return;
405 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900406 connection.resetLocked(null, null, null, null, null);
407 }
408 }
409
Wonsik Kimd922a542014-07-24 18:25:29 +0900410 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
411 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900412 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
413 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900414 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900415 }
416 }
417 return null;
418 }
419
Terry Heoc086a3d2014-06-18 14:26:44 +0900420 private int findDeviceIdForInputIdLocked(String inputId) {
421 for (int i = 0; i < mConnections.size(); ++i) {
422 Connection connection = mConnections.get(i);
423 if (connection.getInfoLocked().getId().equals(inputId)) {
424 return i;
425 }
426 }
427 return -1;
428 }
429
430 /**
431 * Get the list of TvStreamConfig which is buffered mode.
432 */
433 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
434 int resolvedUserId) {
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700435 List<TvStreamConfig> configsList = new ArrayList<>();
Terry Heoc086a3d2014-06-18 14:26:44 +0900436 synchronized (mLock) {
437 int deviceId = findDeviceIdForInputIdLocked(inputId);
438 if (deviceId < 0) {
439 Slog.e(TAG, "Invalid inputId : " + inputId);
440 return configsList;
441 }
442 Connection connection = mConnections.get(deviceId);
443 for (TvStreamConfig config : connection.getConfigsLocked()) {
444 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
445 configsList.add(config);
446 }
447 }
448 }
449 return configsList;
450 }
451
452 /**
453 * Take a snapshot of the given TV input into the provided Surface.
454 */
455 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
456 int callingUid, int resolvedUserId) {
457 synchronized (mLock) {
458 int deviceId = findDeviceIdForInputIdLocked(inputId);
459 if (deviceId < 0) {
460 Slog.e(TAG, "Invalid inputId : " + inputId);
461 return false;
462 }
463 Connection connection = mConnections.get(deviceId);
464 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
465 if (hardwareImpl != null) {
466 // Stop previous capture.
467 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
468 if (runnable != null) {
469 runnable.run();
470 connection.setOnFirstFrameCapturedLocked(null);
471 }
472
473 boolean result = hardwareImpl.startCapture(surface, config);
474 if (result) {
475 connection.setOnFirstFrameCapturedLocked(new Runnable() {
476 @Override
477 public void run() {
478 hardwareImpl.stopCapture(config);
479 }
480 });
481 }
482 return result;
483 }
484 }
485 return false;
486 }
487
Wonsik Kimd922a542014-07-24 18:25:29 +0900488 private void processPendingHdmiDeviceEventsLocked() {
489 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
490 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900491 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900492 TvInputHardwareInfo hardwareInfo =
493 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
494 if (hardwareInfo != null) {
495 msg.sendToTarget();
496 it.remove();
497 }
498 }
499 }
500
Wonsik Kim49e55932014-12-04 17:22:09 +0900501 private void updateVolume() {
502 mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
503 mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
504 }
505
506 private void handleVolumeChange(Context context, Intent intent) {
507 String action = intent.getAction();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700508 switch (action) {
509 case AudioManager.VOLUME_CHANGED_ACTION: {
510 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
511 if (streamType != AudioManager.STREAM_MUSIC) {
512 return;
513 }
514 int index = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
515 if (index == mCurrentIndex) {
516 return;
517 }
518 mCurrentIndex = index;
519 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900520 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700521 case AudioManager.STREAM_MUTE_CHANGED_ACTION: {
522 int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
523 if (streamType != AudioManager.STREAM_MUSIC) {
524 return;
525 }
526 // volume index will be updated at onMediaStreamVolumeChanged() through
527 // updateVolume().
528 break;
Wonsik Kim49e55932014-12-04 17:22:09 +0900529 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700530 default:
531 Slog.w(TAG, "Unrecognized intent: " + intent);
Wonsik Kim49e55932014-12-04 17:22:09 +0900532 return;
Wonsik Kim49e55932014-12-04 17:22:09 +0900533 }
534 synchronized (mLock) {
535 for (int i = 0; i < mConnections.size(); ++i) {
536 TvInputHardwareImpl hardwareImpl = mConnections.valueAt(i).getHardwareImplLocked();
537 if (hardwareImpl != null) {
538 hardwareImpl.onMediaStreamVolumeChanged();
539 }
540 }
541 }
542 }
543
544 private float getMediaStreamVolume() {
John Spurlockee5ad722015-03-03 16:17:21 -0500545 return (float) mCurrentIndex / (float) mCurrentMaxIndex;
Wonsik Kim49e55932014-12-04 17:22:09 +0900546 }
547
yangren8d718e12016-02-15 17:02:49 +0900548 public void dump(FileDescriptor fd, final PrintWriter writer, String[] args) {
549 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -0600550 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
yangren8d718e12016-02-15 17:02:49 +0900551
552 synchronized (mLock) {
553 pw.println("TvInputHardwareManager Info:");
554 pw.increaseIndent();
555 pw.println("mConnections: deviceId -> Connection");
556 pw.increaseIndent();
557 for (int i = 0; i < mConnections.size(); i++) {
558 int deviceId = mConnections.keyAt(i);
559 Connection mConnection = mConnections.valueAt(i);
560 pw.println(deviceId + ": " + mConnection);
561
562 }
563 pw.decreaseIndent();
564
565 pw.println("mHardwareList:");
566 pw.increaseIndent();
567 for (TvInputHardwareInfo tvInputHardwareInfo : mHardwareList) {
568 pw.println(tvInputHardwareInfo);
569 }
570 pw.decreaseIndent();
571
572 pw.println("mHdmiDeviceList:");
573 pw.increaseIndent();
574 for (HdmiDeviceInfo hdmiDeviceInfo : mHdmiDeviceList) {
575 pw.println(hdmiDeviceInfo);
576 }
577 pw.decreaseIndent();
578
579 pw.println("mHardwareInputIdMap: deviceId -> inputId");
580 pw.increaseIndent();
581 for (int i = 0 ; i < mHardwareInputIdMap.size(); i++) {
582 int deviceId = mHardwareInputIdMap.keyAt(i);
583 String inputId = mHardwareInputIdMap.valueAt(i);
584 pw.println(deviceId + ": " + inputId);
585 }
586 pw.decreaseIndent();
587
588 pw.println("mHdmiInputIdMap: id -> inputId");
589 pw.increaseIndent();
590 for (int i = 0; i < mHdmiInputIdMap.size(); i++) {
591 int id = mHdmiInputIdMap.keyAt(i);
592 String inputId = mHdmiInputIdMap.valueAt(i);
593 pw.println(id + ": " + inputId);
594 }
595 pw.decreaseIndent();
596
597 pw.println("mInputMap: inputId -> inputInfo");
598 pw.increaseIndent();
599 for(Map.Entry<String, TvInputInfo> entry : mInputMap.entrySet()) {
600 pw.println(entry.getKey() + ": " + entry.getValue());
601 }
602 pw.decreaseIndent();
603 pw.decreaseIndent();
604 }
605 }
606
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000607 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900608 private final TvInputHardwareInfo mHardwareInfo;
609 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000610 private TvInputHardwareImpl mHardware = null;
611 private ITvInputHardwareCallback mCallback;
612 private TvStreamConfig[] mConfigs = null;
613 private Integer mCallingUid = null;
614 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900615 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000616
Wonsik Kim969167d2014-06-24 16:33:17 +0900617 public Connection(TvInputHardwareInfo hardwareInfo) {
618 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000619 }
620
621 // *Locked methods assume TvInputHardwareManager.mLock is held.
622
Wonsik Kim969167d2014-06-24 16:33:17 +0900623 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
624 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000625 if (mHardware != null) {
626 try {
627 mCallback.onReleased();
628 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900629 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000630 }
631 mHardware.release();
632 }
633 mHardware = hardware;
634 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900635 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000636 mCallingUid = callingUid;
637 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900638 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000639
640 if (mHardware != null && mCallback != null) {
641 try {
642 mCallback.onStreamConfigChanged(getConfigsLocked());
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 }
647 }
648
649 public void updateConfigsLocked(TvStreamConfig[] configs) {
650 mConfigs = configs;
651 }
652
Wonsik Kim969167d2014-06-24 16:33:17 +0900653 public TvInputHardwareInfo getHardwareInfoLocked() {
654 return mHardwareInfo;
655 }
656
657 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000658 return mInfo;
659 }
660
661 public ITvInputHardware getHardwareLocked() {
662 return mHardware;
663 }
664
Terry Heoc086a3d2014-06-18 14:26:44 +0900665 public TvInputHardwareImpl getHardwareImplLocked() {
666 return mHardware;
667 }
668
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000669 public ITvInputHardwareCallback getCallbackLocked() {
670 return mCallback;
671 }
672
673 public TvStreamConfig[] getConfigsLocked() {
674 return mConfigs;
675 }
676
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900677 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000678 return mCallingUid;
679 }
680
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900681 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000682 return mResolvedUserId;
683 }
684
Terry Heoc086a3d2014-06-18 14:26:44 +0900685 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
686 mOnFirstFrameCaptured = runnable;
687 }
688
689 public Runnable getOnFirstFrameCapturedLocked() {
690 return mOnFirstFrameCaptured;
691 }
692
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000693 @Override
694 public void binderDied() {
695 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900696 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000697 }
698 }
yangren8d718e12016-02-15 17:02:49 +0900699
700 public String toString() {
701 return "Connection{"
702 + " mHardwareInfo: " + mHardwareInfo
703 + ", mInfo: " + mInfo
704 + ", mCallback: " + mCallback
705 + ", mConfigs: " + Arrays.toString(mConfigs)
706 + ", mCallingUid: " + mCallingUid
707 + ", mResolvedUserId: " + mResolvedUserId
708 + " }";
709 }
Shubang71d5c762017-02-23 15:46:40 -0800710
711 private int getConfigsLengthLocked() {
712 return mConfigs == null ? 0 : mConfigs.length;
713 }
714
715 private int getInputStateLocked() {
716 int configsLength = getConfigsLengthLocked();
717 if (configsLength > 0) {
718 return INPUT_STATE_CONNECTED;
719 }
720 switch (mHardwareInfo.getCableConnectionStatus()) {
721 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED:
722 return INPUT_STATE_CONNECTED;
723 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED:
724 return INPUT_STATE_DISCONNECTED;
725 case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN:
726 default:
727 return INPUT_STATE_CONNECTED_STANDBY;
728 }
729 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000730 }
731
732 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
733 private final TvInputHardwareInfo mInfo;
734 private boolean mReleased = false;
735 private final Object mImplLock = new Object();
736
Wonsik Kim06c41a32014-08-04 18:57:47 +0900737 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
738 new AudioManager.OnAudioPortUpdateListener() {
739 @Override
740 public void onAudioPortListUpdate(AudioPort[] portList) {
741 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900742 updateAudioConfigLocked();
Wonsik Kim06c41a32014-08-04 18:57:47 +0900743 }
744 }
745
746 @Override
747 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
748 // No-op
749 }
750
751 @Override
752 public void onServiceDied() {
753 synchronized (mImplLock) {
754 mAudioSource = null;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900755 mAudioSink.clear();
Jae Seoee32ede2016-05-31 10:31:15 -0700756 if (mAudioPatch != null) {
757 mAudioManager.releaseAudioPatch(mAudioPatch);
758 mAudioPatch = null;
759 }
Wonsik Kim06c41a32014-08-04 18:57:47 +0900760 }
761 }
762 };
763 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
764 private String mOverrideAudioAddress = "";
765 private AudioDevicePort mAudioSource;
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900766 private List<AudioDevicePort> mAudioSink = new ArrayList<>();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900767 private AudioPatch mAudioPatch = null;
Wonsik Kim485e7e12015-01-26 17:28:55 +0900768 // Set to an invalid value for a volume, so that current volume can be applied at the
769 // first call to updateAudioConfigLocked().
770 private float mCommittedVolume = -1f;
Wonsik Kim49e55932014-12-04 17:22:09 +0900771 private float mSourceVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900772
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900773 private TvStreamConfig mActiveConfig = null;
774
Wonsik Kimca17a902014-07-31 10:09:46 +0900775 private int mDesiredSamplingRate = 0;
776 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
777 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
778
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000779 public TvInputHardwareImpl(TvInputHardwareInfo info) {
780 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900781 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900782 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900783 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900784 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kimca17a902014-07-31 10:09:46 +0900785 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900786 }
787
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900788 private void findAudioSinkFromAudioPolicy(List<AudioDevicePort> sinks) {
789 sinks.clear();
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700790 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900791 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
792 return;
793 }
794 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
Paul McLeanceb47aa2015-02-19 15:09:53 -0800795 for (AudioDevicePort port : devicePorts) {
Wally Yauad36f6472015-10-06 11:33:19 -0700796 if ((port.type() & sinkDevice) != 0 &&
797 (port.type() & AudioSystem.DEVICE_BIT_IN) == 0) {
Paul McLeanceb47aa2015-02-19 15:09:53 -0800798 sinks.add(port);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900799 }
800 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900801 }
802
803 private AudioDevicePort findAudioDevicePort(int type, String address) {
804 if (type == AudioManager.DEVICE_NONE) {
805 return null;
806 }
Jae Seo6e4cbfd2015-06-21 16:40:34 -0700807 ArrayList<AudioDevicePort> devicePorts = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900808 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
809 return null;
810 }
Paul McLeanceb47aa2015-02-19 15:09:53 -0800811 for (AudioDevicePort port : devicePorts) {
812 if (port.type() == type && port.address().equals(address)) {
813 return port;
Wonsik Kimca17a902014-07-31 10:09:46 +0900814 }
815 }
816 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000817 }
818
819 public void release() {
820 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900821 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900822 if (mAudioPatch != null) {
823 mAudioManager.releaseAudioPatch(mAudioPatch);
824 mAudioPatch = null;
825 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000826 mReleased = true;
827 }
828 }
829
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900830 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
831 // attempts to call setSurface with different TvStreamConfig objects, the last call will
832 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000833 @Override
834 public boolean setSurface(Surface surface, TvStreamConfig config)
835 throws RemoteException {
836 synchronized (mImplLock) {
837 if (mReleased) {
838 throw new IllegalStateException("Device already released.");
839 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900840
Wonsik Kim102670f2014-11-20 14:38:02 +0900841 int result = TvInputHal.SUCCESS;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900842 if (surface == null) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900843 // The value of config is ignored when surface == null.
844 if (mActiveConfig != null) {
845 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
846 mActiveConfig = null;
847 } else {
848 // We already have no active stream.
849 return true;
850 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900851 } else {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900852 // It's impossible to set a non-null surface with a null config.
853 if (config == null) {
854 return false;
855 }
856 // Remove stream only if we have an existing active configuration.
857 if (mActiveConfig != null && !config.equals(mActiveConfig)) {
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900858 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
859 if (result != TvInputHal.SUCCESS) {
860 mActiveConfig = null;
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900861 }
862 }
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900863 // Proceed only if all previous operations succeeded.
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900864 if (result == TvInputHal.SUCCESS) {
Wonsik Kimb683e6e2014-11-17 16:31:39 +0900865 result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
866 if (result == TvInputHal.SUCCESS) {
867 mActiveConfig = config;
868 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900869 }
870 }
Wonsik Kim7587d292014-08-09 10:01:01 +0900871 updateAudioConfigLocked();
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900872 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000873 }
874 }
875
Wonsik Kim7587d292014-08-09 10:01:01 +0900876 /**
877 * Update audio configuration (source, sink, patch) all up to current state.
878 */
879 private void updateAudioConfigLocked() {
880 boolean sinkUpdated = updateAudioSinkLocked();
881 boolean sourceUpdated = updateAudioSourceLocked();
882 // We can't do updated = updateAudioSinkLocked() || updateAudioSourceLocked() here
883 // because Java won't evaluate the latter if the former is true.
884
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900885 if (mAudioSource == null || mAudioSink.isEmpty() || mActiveConfig == null) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900886 if (mAudioPatch != null) {
Wonsik Kim7587d292014-08-09 10:01:01 +0900887 mAudioManager.releaseAudioPatch(mAudioPatch);
888 mAudioPatch = null;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900889 }
890 return;
891 }
892
Wonsik Kim49e55932014-12-04 17:22:09 +0900893 updateVolume();
894 float volume = mSourceVolume * getMediaStreamVolume();
Wonsik Kim8e45a332014-08-04 10:17:18 +0900895 AudioGainConfig sourceGainConfig = null;
Wonsik Kim49e55932014-12-04 17:22:09 +0900896 if (mAudioSource.gains().length > 0 && volume != mCommittedVolume) {
Wonsik Kim8e45a332014-08-04 10:17:18 +0900897 AudioGain sourceGain = null;
898 for (AudioGain gain : mAudioSource.gains()) {
899 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
900 sourceGain = gain;
901 break;
902 }
903 }
904 // NOTE: we only change the source gain in MODE_JOINT here.
905 if (sourceGain != null) {
906 int steps = (sourceGain.maxValue() - sourceGain.minValue())
907 / sourceGain.stepValue();
908 int gainValue = sourceGain.minValue();
Wonsik Kim49e55932014-12-04 17:22:09 +0900909 if (volume < 1.0f) {
910 gainValue += sourceGain.stepValue() * (int) (volume * steps + 0.5);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900911 } else {
912 gainValue = sourceGain.maxValue();
913 }
Wonsik Kim49e55932014-12-04 17:22:09 +0900914 // size of gain values is 1 in MODE_JOINT
915 int[] gainValues = new int[] { gainValue };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900916 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
917 sourceGain.channelMask(), gainValues, 0);
918 } else {
919 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
920 }
921 }
922
Wonsik Kimca17a902014-07-31 10:09:46 +0900923 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900924 List<AudioPortConfig> sinkConfigs = new ArrayList<>();
Wonsik Kimca17a902014-07-31 10:09:46 +0900925 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim7587d292014-08-09 10:01:01 +0900926 boolean shouldRecreateAudioPatch = sourceUpdated || sinkUpdated;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900927
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900928 for (AudioDevicePort audioSink : mAudioSink) {
929 AudioPortConfig sinkConfig = audioSink.activeConfig();
930 int sinkSamplingRate = mDesiredSamplingRate;
931 int sinkChannelMask = mDesiredChannelMask;
932 int sinkFormat = mDesiredFormat;
933 // If sinkConfig != null and values are set to default,
934 // fill in the sinkConfig values.
935 if (sinkConfig != null) {
936 if (sinkSamplingRate == 0) {
937 sinkSamplingRate = sinkConfig.samplingRate();
938 }
939 if (sinkChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT) {
940 sinkChannelMask = sinkConfig.channelMask();
941 }
942 if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
Kyeongkab.Nam7a40f702019-02-01 11:51:02 +0900943 sinkFormat = sinkConfig.format();
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900944 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900945 }
Wonsik Kim71dfa962014-11-15 14:18:49 +0900946
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900947 if (sinkConfig == null
948 || sinkConfig.samplingRate() != sinkSamplingRate
949 || sinkConfig.channelMask() != sinkChannelMask
950 || sinkConfig.format() != sinkFormat) {
951 // Check for compatibility and reset to default if necessary.
952 if (!intArrayContains(audioSink.samplingRates(), sinkSamplingRate)
953 && audioSink.samplingRates().length > 0) {
954 sinkSamplingRate = audioSink.samplingRates()[0];
955 }
956 if (!intArrayContains(audioSink.channelMasks(), sinkChannelMask)) {
957 sinkChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
958 }
959 if (!intArrayContains(audioSink.formats(), sinkFormat)) {
960 sinkFormat = AudioFormat.ENCODING_DEFAULT;
961 }
962 sinkConfig = audioSink.buildConfig(sinkSamplingRate, sinkChannelMask,
963 sinkFormat, null);
964 shouldRecreateAudioPatch = true;
Wonsik Kim71dfa962014-11-15 14:18:49 +0900965 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900966 sinkConfigs.add(sinkConfig);
Wonsik Kimca17a902014-07-31 10:09:46 +0900967 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +0900968 // sinkConfigs.size() == mAudioSink.size(), and mAudioSink is guaranteed to be
969 // non-empty at the beginning of this method.
970 AudioPortConfig sinkConfig = sinkConfigs.get(0);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900971 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kim71dfa962014-11-15 14:18:49 +0900972 int sourceSamplingRate = 0;
973 if (intArrayContains(mAudioSource.samplingRates(), sinkConfig.samplingRate())) {
974 sourceSamplingRate = sinkConfig.samplingRate();
975 } else if (mAudioSource.samplingRates().length > 0) {
976 // Use any sampling rate and hope audio patch can handle resampling...
977 sourceSamplingRate = mAudioSource.samplingRates()[0];
978 }
979 int sourceChannelMask = AudioFormat.CHANNEL_IN_DEFAULT;
980 for (int inChannelMask : mAudioSource.channelMasks()) {
981 if (AudioFormat.channelCountFromOutChannelMask(sinkConfig.channelMask())
982 == AudioFormat.channelCountFromInChannelMask(inChannelMask)) {
983 sourceChannelMask = inChannelMask;
984 break;
985 }
986 }
987 int sourceFormat = AudioFormat.ENCODING_DEFAULT;
988 if (intArrayContains(mAudioSource.formats(), sinkConfig.format())) {
989 sourceFormat = sinkConfig.format();
990 }
991 sourceConfig = mAudioSource.buildConfig(sourceSamplingRate, sourceChannelMask,
992 sourceFormat, sourceGainConfig);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900993 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900994 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900995 if (shouldRecreateAudioPatch) {
Wonsik Kim49e55932014-12-04 17:22:09 +0900996 mCommittedVolume = volume;
Jae Seoee32ede2016-05-31 10:31:15 -0700997 if (mAudioPatch != null) {
998 mAudioManager.releaseAudioPatch(mAudioPatch);
999 }
Wonsik Kim8e45a332014-08-04 10:17:18 +09001000 mAudioManager.createAudioPatch(
1001 audioPatchArray,
1002 new AudioPortConfig[] { sourceConfig },
Jae Seo93ff14b2015-06-21 14:08:54 -07001003 sinkConfigs.toArray(new AudioPortConfig[sinkConfigs.size()]));
Wonsik Kim8e45a332014-08-04 10:17:18 +09001004 mAudioPatch = audioPatchArray[0];
Wonsik Kim71dfa962014-11-15 14:18:49 +09001005 if (sourceGainConfig != null) {
1006 mAudioManager.setAudioPortGain(mAudioSource, sourceGainConfig);
1007 }
Wonsik Kim8e45a332014-08-04 10:17:18 +09001008 }
Wonsik Kimca17a902014-07-31 10:09:46 +09001009 }
1010
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001011 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +09001012 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001013 synchronized (mImplLock) {
1014 if (mReleased) {
1015 throw new IllegalStateException("Device already released.");
1016 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001017 mSourceVolume = volume;
Wonsik Kim7587d292014-08-09 10:01:01 +09001018 updateAudioConfigLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001019 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001020 }
1021
Terry Heoc086a3d2014-06-18 14:26:44 +09001022 private boolean startCapture(Surface surface, TvStreamConfig config) {
1023 synchronized (mImplLock) {
1024 if (mReleased) {
1025 return false;
1026 }
1027 if (surface == null || config == null) {
1028 return false;
1029 }
1030 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
1031 return false;
1032 }
1033
Wonsik Kim8f24a8b2014-10-22 16:27:39 +09001034 int result = mHal.addOrUpdateStream(mInfo.getDeviceId(), surface, config);
Terry Heoc086a3d2014-06-18 14:26:44 +09001035 return result == TvInputHal.SUCCESS;
1036 }
1037 }
1038
1039 private boolean stopCapture(TvStreamConfig config) {
1040 synchronized (mImplLock) {
1041 if (mReleased) {
1042 return false;
1043 }
1044 if (config == null) {
1045 return false;
1046 }
1047
1048 int result = mHal.removeStream(mInfo.getDeviceId(), config);
1049 return result == TvInputHal.SUCCESS;
1050 }
1051 }
Wonsik Kimca17a902014-07-31 10:09:46 +09001052
Wonsik Kim7587d292014-08-09 10:01:01 +09001053 private boolean updateAudioSourceLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001054 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001055 return false;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001056 }
Wonsik Kim7587d292014-08-09 10:01:01 +09001057 AudioDevicePort previousSource = mAudioSource;
1058 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
1059 return mAudioSource == null ? (previousSource != null)
1060 : !mAudioSource.equals(previousSource);
1061 }
1062
1063 private boolean updateAudioSinkLocked() {
1064 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
1065 return false;
1066 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001067 List<AudioDevicePort> previousSink = mAudioSink;
1068 mAudioSink = new ArrayList<>();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001069 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001070 findAudioSinkFromAudioPolicy(mAudioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001071 } else {
1072 AudioDevicePort audioSink =
1073 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
1074 if (audioSink != null) {
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001075 mAudioSink.add(audioSink);
Wonsik Kim06c41a32014-08-04 18:57:47 +09001076 }
1077 }
Wonsik Kimb2b85f92015-01-19 16:07:48 +09001078
1079 // Returns true if mAudioSink and previousSink differs.
1080 if (mAudioSink.size() != previousSink.size()) {
1081 return true;
1082 }
1083 previousSink.removeAll(mAudioSink);
1084 return !previousSink.isEmpty();
Wonsik Kim06c41a32014-08-04 18:57:47 +09001085 }
1086
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001087 private void handleAudioSinkUpdated() {
1088 synchronized (mImplLock) {
Wonsik Kim7587d292014-08-09 10:01:01 +09001089 updateAudioConfigLocked();
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001090 }
1091 }
1092
Wonsik Kimca17a902014-07-31 10:09:46 +09001093 @Override
1094 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
1095 int channelMask, int format) {
1096 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +09001097 mOverrideAudioType = audioType;
1098 mOverrideAudioAddress = audioAddress;
Wonsik Kim06c41a32014-08-04 18:57:47 +09001099
Wonsik Kimca17a902014-07-31 10:09:46 +09001100 mDesiredSamplingRate = samplingRate;
1101 mDesiredChannelMask = channelMask;
1102 mDesiredFormat = format;
1103
Wonsik Kim7587d292014-08-09 10:01:01 +09001104 updateAudioConfigLocked();
Wonsik Kimca17a902014-07-31 10:09:46 +09001105 }
1106 }
Wonsik Kim49e55932014-12-04 17:22:09 +09001107
1108 public void onMediaStreamVolumeChanged() {
1109 synchronized (mImplLock) {
1110 updateAudioConfigLocked();
1111 }
1112 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001113 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001114
Wonsik Kim187423c2014-06-25 14:12:48 +09001115 interface Listener {
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001116 void onStateChanged(String inputId, int state);
1117 void onHardwareDeviceAdded(TvInputHardwareInfo info);
1118 void onHardwareDeviceRemoved(TvInputHardwareInfo info);
1119 void onHdmiDeviceAdded(HdmiDeviceInfo device);
1120 void onHdmiDeviceRemoved(HdmiDeviceInfo device);
1121 void onHdmiDeviceUpdated(String inputId, HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +09001122 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001123
Wonsik Kim187423c2014-06-25 14:12:48 +09001124 private class ListenerHandler extends Handler {
1125 private static final int STATE_CHANGED = 1;
1126 private static final int HARDWARE_DEVICE_ADDED = 2;
1127 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -07001128 private static final int HDMI_DEVICE_ADDED = 4;
1129 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001130 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +09001131
1132 @Override
1133 public final void handleMessage(Message msg) {
1134 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +09001135 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +09001136 String inputId = (String) msg.obj;
1137 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +09001138 mListener.onStateChanged(inputId, state);
1139 break;
1140 }
1141 case HARDWARE_DEVICE_ADDED: {
1142 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1143 mListener.onHardwareDeviceAdded(info);
1144 break;
1145 }
1146 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +09001147 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
1148 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001149 break;
1150 }
Jae Seo546c6352014-08-07 11:57:01 -07001151 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001152 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001153 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +09001154 break;
1155 }
Jae Seo546c6352014-08-07 11:57:01 -07001156 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +09001157 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -07001158 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +09001159 break;
1160 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001161 case HDMI_DEVICE_UPDATED: {
Wonsik Kim184a6d62014-10-08 13:05:56 +09001162 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo6e4cbfd2015-06-21 16:40:34 -07001163 String inputId;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001164 synchronized (mLock) {
1165 inputId = mHdmiInputIdMap.get(info.getId());
1166 }
1167 if (inputId != null) {
1168 mListener.onHdmiDeviceUpdated(inputId, info);
1169 } else {
1170 Slog.w(TAG, "Could not resolve input ID matching the device info; "
1171 + "ignoring.");
1172 }
1173 break;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001174 }
Wonsik Kim969167d2014-06-24 16:33:17 +09001175 default: {
1176 Slog.w(TAG, "Unhandled message: " + msg);
1177 break;
1178 }
1179 }
1180 }
1181 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001182
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001183 // Listener implementations for HdmiControlService
1184
1185 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
1186 @Override
1187 public void onReceived(HdmiHotplugEvent event) {
1188 synchronized (mLock) {
1189 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +09001190 TvInputHardwareInfo hardwareInfo =
1191 findHardwareInfoForHdmiPortLocked(event.getPort());
1192 if (hardwareInfo == null) {
1193 return;
1194 }
1195 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001196 if (inputId == null) {
1197 return;
1198 }
Shubang71d5c762017-02-23 15:46:40 -08001199 // No HDMI hotplug does not necessarily mean disconnected, as old devices may
1200 // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
1201 // denote unknown state.
1202 int state = event.isConnected()
1203 ? INPUT_STATE_CONNECTED
1204 : INPUT_STATE_CONNECTED_STANDBY;
1205 mHandler.obtainMessage(
1206 ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001207 }
1208 }
1209 }
1210
Wonsik Kim187423c2014-06-25 14:12:48 +09001211 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
1212 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001213 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Jinsuk Kim98d760e2014-12-23 07:01:51 +09001214 if (!deviceInfo.isSourceType()) return;
Wonsik Kimd922a542014-07-24 18:25:29 +09001215 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001216 int messageType = 0;
Wonsik Kime92f8572014-08-12 18:30:24 +09001217 Object obj = null;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001218 switch (status) {
1219 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001220 if (findHdmiDeviceInfo(deviceInfo.getId()) == null) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001221 mHdmiDeviceList.add(deviceInfo);
1222 } else {
1223 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
1224 return;
1225 }
1226 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001227 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001228 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001229 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001230 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001231 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1232 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001233 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1234 return;
1235 }
1236 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
Wonsik Kime92f8572014-08-12 18:30:24 +09001237 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001238 break;
1239 }
1240 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
Jungshik Jangcf445872014-08-20 15:06:01 +09001241 HdmiDeviceInfo originalDeviceInfo = findHdmiDeviceInfo(deviceInfo.getId());
1242 if (!mHdmiDeviceList.remove(originalDeviceInfo)) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001243 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
1244 return;
1245 }
1246 mHdmiDeviceList.add(deviceInfo);
1247 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
Wonsik Kim184a6d62014-10-08 13:05:56 +09001248 obj = deviceInfo;
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001249 break;
Wonsik Kimd922a542014-07-24 18:25:29 +09001250 }
1251 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +09001252
Wonsik Kime92f8572014-08-12 18:30:24 +09001253 Message msg = mHandler.obtainMessage(messageType, 0, 0, obj);
Wonsik Kimd922a542014-07-24 18:25:29 +09001254 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
1255 msg.sendToTarget();
1256 } else {
1257 mPendingHdmiDeviceEvents.add(msg);
1258 }
1259 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001260 }
Jungshik Jangcf445872014-08-20 15:06:01 +09001261
1262 private HdmiDeviceInfo findHdmiDeviceInfo(int id) {
1263 for (HdmiDeviceInfo info : mHdmiDeviceList) {
1264 if (info.getId() == id) {
1265 return info;
1266 }
1267 }
1268 return null;
1269 }
Wonsik Kim187423c2014-06-25 14:12:48 +09001270 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001271
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001272 private final class HdmiSystemAudioModeChangeListener extends
1273 IHdmiSystemAudioModeChangeListener.Stub {
1274 @Override
1275 public void onStatusChanged(boolean enabled) throws RemoteException {
1276 synchronized (mLock) {
1277 for (int i = 0; i < mConnections.size(); ++i) {
1278 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
Jungshik Jang44bb3a52014-08-26 17:24:24 +09001279 if (impl != null) {
1280 impl.handleAudioSinkUpdated();
1281 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001282 }
1283 }
1284 }
1285 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001286}