blob: 0aa3d813df384f3b5ae44a5a075e0cfc2ebb7d35 [file] [log] [blame]
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.tv;
18
Wonsik Kim969167d2014-06-24 16:33:17 +090019import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
20import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
21
Wonsik Kimc22dbb62014-05-26 02:26:04 +000022import android.content.Context;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090023import android.content.Intent;
Jungshik Jang61daf6b2014-08-08 11:38:28 +090024import android.hardware.hdmi.HdmiControlManager;
Jungshik Jang61f4fbd2014-08-06 19:21:12 +090025import android.hardware.hdmi.HdmiDeviceInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090026import android.hardware.hdmi.HdmiHotplugEvent;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090027import android.hardware.hdmi.IHdmiControlService;
Wonsik Kim187423c2014-06-25 14:12:48 +090028import android.hardware.hdmi.IHdmiDeviceEventListener;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090029import android.hardware.hdmi.IHdmiHotplugEventListener;
30import android.hardware.hdmi.IHdmiInputChangeListener;
Jungshik Jang1f819bb2014-08-08 10:46:54 +090031import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
Wonsik Kimd7c29182014-05-27 10:38:21 +090032import android.media.AudioDevicePort;
Wonsik Kimca17a902014-07-31 10:09:46 +090033import android.media.AudioFormat;
Wonsik Kim8e45a332014-08-04 10:17:18 +090034import android.media.AudioGain;
35import android.media.AudioGainConfig;
Wonsik Kimd7c29182014-05-27 10:38:21 +090036import android.media.AudioManager;
37import android.media.AudioPatch;
38import android.media.AudioPort;
39import android.media.AudioPortConfig;
Jae Seod5cc4a22014-05-30 16:57:43 -070040import android.media.tv.ITvInputHardware;
41import android.media.tv.ITvInputHardwareCallback;
Jinsuk Kimb2b31512014-07-21 17:14:44 +090042import android.media.tv.TvContract;
Jae Seo546c6352014-08-07 11:57:01 -070043import android.media.tv.TvInputHardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +090044import android.media.tv.TvInputInfo;
Jae Seod5cc4a22014-05-30 16:57:43 -070045import android.media.tv.TvStreamConfig;
Wonsik Kim969167d2014-06-24 16:33:17 +090046import android.os.Handler;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000047import android.os.IBinder;
Wonsik Kim969167d2014-06-24 16:33:17 +090048import android.os.Message;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000049import android.os.RemoteException;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090050import android.os.ServiceManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090051import android.util.ArrayMap;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000052import android.util.Slog;
53import android.util.SparseArray;
Wonsik Kim969167d2014-06-24 16:33:17 +090054import android.util.SparseBooleanArray;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000055import android.view.KeyEvent;
56import android.view.Surface;
57
Wonsik Kim969167d2014-06-24 16:33:17 +090058import com.android.server.SystemService;
59
Wonsik Kimc22dbb62014-05-26 02:26:04 +000060import java.util.ArrayList;
Wonsik Kim8e45a332014-08-04 10:17:18 +090061import java.util.Arrays;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090062import java.util.Collections;
Wonsik Kimd922a542014-07-24 18:25:29 +090063import java.util.Iterator;
64import java.util.LinkedList;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000065import java.util.List;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090066import java.util.Map;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000067
68/**
69 * A helper class for TvInputManagerService to handle TV input hardware.
70 *
71 * This class does a basic connection management and forwarding calls to TvInputHal which eventually
72 * calls to tv_input HAL module.
73 *
74 * @hide
75 */
Jinsuk Kim7474fac2014-07-18 17:22:26 +090076class TvInputHardwareManager implements TvInputHal.Callback {
Wonsik Kimc22dbb62014-05-26 02:26:04 +000077 private static final String TAG = TvInputHardwareManager.class.getSimpleName();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090078
79 private final Context mContext;
80 private final Listener mListener;
Wonsik Kimc22dbb62014-05-26 02:26:04 +000081 private final TvInputHal mHal = new TvInputHal(this);
Wonsik Kimd922a542014-07-24 18:25:29 +090082 private final SparseArray<Connection> mConnections = new SparseArray<>();
83 private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
Jae Seo546c6352014-08-07 11:57:01 -070084 private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090085 /* A map from a device ID to the matching TV input ID. */
Wonsik Kimd922a542014-07-24 18:25:29 +090086 private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090087 /* A map from a HDMI logical address to the matching TV input ID. */
Jae Seo546c6352014-08-07 11:57:01 -070088 private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
Wonsik Kimd922a542014-07-24 18:25:29 +090089 private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090090
Wonsik Kimd7c29182014-05-27 10:38:21 +090091 private final AudioManager mAudioManager;
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090092 private IHdmiControlService mHdmiControlService;
Jinsuk Kim7474fac2014-07-18 17:22:26 +090093 private final IHdmiHotplugEventListener mHdmiHotplugEventListener =
94 new HdmiHotplugEventListener();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +090095 private final IHdmiDeviceEventListener mHdmiDeviceEventListener = new HdmiDeviceEventListener();
Jinsuk Kim7474fac2014-07-18 17:22:26 +090096 private final IHdmiInputChangeListener mHdmiInputChangeListener = new HdmiInputChangeListener();
Jungshik Jang1f819bb2014-08-08 10:46:54 +090097 private final IHdmiSystemAudioModeChangeListener mHdmiSystemAudioModeChangeListener =
98 new HdmiSystemAudioModeChangeListener();
Wonsik Kimd922a542014-07-24 18:25:29 +090099 // TODO: Should handle STANDBY case.
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900100 private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
Wonsik Kimd922a542014-07-24 18:25:29 +0900101 private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
Wonsik Kim969167d2014-06-24 16:33:17 +0900102
Wonsik Kim187423c2014-06-25 14:12:48 +0900103 // Calls to mListener should happen here.
104 private final Handler mHandler = new ListenerHandler();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000105
106 private final Object mLock = new Object();
107
Wonsik Kim187423c2014-06-25 14:12:48 +0900108 public TvInputHardwareManager(Context context, Listener listener) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000109 mContext = context;
Wonsik Kim187423c2014-06-25 14:12:48 +0900110 mListener = listener;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900111 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000112 mHal.init();
Wonsik Kim969167d2014-06-24 16:33:17 +0900113 }
114
115 public void onBootPhase(int phase) {
116 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900117 mHdmiControlService = IHdmiControlService.Stub.asInterface(ServiceManager.getService(
118 Context.HDMI_CONTROL_SERVICE));
119 if (mHdmiControlService != null) {
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900120 try {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900121 mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
122 mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900123 mHdmiControlService.addSystemAudioModeChangeListener(
124 mHdmiSystemAudioModeChangeListener);
Jae Seo546c6352014-08-07 11:57:01 -0700125 mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900126 mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900127 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900128 Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900129 }
130 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900131 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000132 }
133
134 @Override
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900135 public void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000136 synchronized (mLock) {
137 Connection connection = new Connection(info);
138 connection.updateConfigsLocked(configs);
139 mConnections.put(info.getDeviceId(), connection);
Wonsik Kimd922a542014-07-24 18:25:29 +0900140 buildHardwareListLocked();
Wonsik Kim187423c2014-06-25 14:12:48 +0900141 mHandler.obtainMessage(
142 ListenerHandler.HARDWARE_DEVICE_ADDED, 0, 0, info).sendToTarget();
Wonsik Kimd922a542014-07-24 18:25:29 +0900143 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
144 processPendingHdmiDeviceEventsLocked();
145 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000146 }
147 }
148
Wonsik Kimd922a542014-07-24 18:25:29 +0900149 private void buildHardwareListLocked() {
150 mHardwareList.clear();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000151 for (int i = 0; i < mConnections.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900152 mHardwareList.add(mConnections.valueAt(i).getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000153 }
154 }
155
156 @Override
157 public void onDeviceUnavailable(int deviceId) {
158 synchronized (mLock) {
159 Connection connection = mConnections.get(deviceId);
160 if (connection == null) {
161 Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
162 return;
163 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900164 connection.resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000165 mConnections.remove(deviceId);
Wonsik Kimd922a542014-07-24 18:25:29 +0900166 buildHardwareListLocked();
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900167 TvInputHardwareInfo info = connection.getHardwareInfoLocked();
Wonsik Kimd922a542014-07-24 18:25:29 +0900168 if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Jae Seo546c6352014-08-07 11:57:01 -0700169 // Remove HDMI devices linked with this hardware.
170 for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900171 HdmiDeviceInfo deviceInfo = it.next();
Wonsik Kimd922a542014-07-24 18:25:29 +0900172 if (deviceInfo.getPortId() == info.getHdmiPortId()) {
Jae Seo546c6352014-08-07 11:57:01 -0700173 mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
Wonsik Kimd922a542014-07-24 18:25:29 +0900174 deviceInfo).sendToTarget();
175 it.remove();
176 }
177 }
178 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900179 mHandler.obtainMessage(
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900180 ListenerHandler.HARDWARE_DEVICE_REMOVED, 0, 0, info).sendToTarget();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000181 }
182 }
183
184 @Override
185 public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
186 synchronized (mLock) {
187 Connection connection = mConnections.get(deviceId);
188 if (connection == null) {
189 Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
190 + deviceId);
191 return;
192 }
193 connection.updateConfigsLocked(configs);
194 try {
195 connection.getCallbackLocked().onStreamConfigChanged(configs);
196 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900197 Slog.e(TAG, "error in onStreamConfigurationChanged", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000198 }
199 }
200 }
201
Terry Heoc086a3d2014-06-18 14:26:44 +0900202 @Override
203 public void onFirstFrameCaptured(int deviceId, int streamId) {
204 synchronized (mLock) {
205 Connection connection = mConnections.get(deviceId);
206 if (connection == null) {
207 Slog.e(TAG, "FirstFrameCaptured: Cannot find a connection with "
208 + deviceId);
209 return;
210 }
211 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
212 if (runnable != null) {
213 runnable.run();
214 connection.setOnFirstFrameCapturedLocked(null);
215 }
216 }
217 }
218
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000219 public List<TvInputHardwareInfo> getHardwareList() {
220 synchronized (mLock) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900221 return Collections.unmodifiableList(mHardwareList);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000222 }
223 }
224
Jae Seo546c6352014-08-07 11:57:01 -0700225 public List<HdmiDeviceInfo> getHdmiDeviceList() {
Wonsik Kimd922a542014-07-24 18:25:29 +0900226 synchronized (mLock) {
Jae Seo546c6352014-08-07 11:57:01 -0700227 return Collections.unmodifiableList(mHdmiDeviceList);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900228 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900229 }
230
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900231 private boolean checkUidChangedLocked(
232 Connection connection, int callingUid, int resolvedUserId) {
233 Integer connectionCallingUid = connection.getCallingUidLocked();
234 Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
235 if (connectionCallingUid == null || connectionResolvedUserId == null) {
236 return true;
237 }
238 if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
239 return true;
240 }
241 return false;
242 }
243
Wonsik Kim969167d2014-06-24 16:33:17 +0900244 private int convertConnectedToState(boolean connected) {
245 if (connected) {
246 return INPUT_STATE_CONNECTED;
247 } else {
248 return INPUT_STATE_DISCONNECTED;
249 }
250 }
251
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900252 public void addHardwareTvInput(int deviceId, TvInputInfo info) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900253 synchronized (mLock) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900254 String oldInputId = mHardwareInputIdMap.get(deviceId);
255 if (oldInputId != null) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900256 Slog.w(TAG, "Trying to override previous registration: old = "
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900257 + mInputMap.get(oldInputId) + ":" + deviceId + ", new = "
Wonsik Kim969167d2014-06-24 16:33:17 +0900258 + info + ":" + deviceId);
259 }
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900260 mHardwareInputIdMap.put(deviceId, info.getId());
261 mInputMap.put(info.getId(), info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900262
263 for (int i = 0; i < mHdmiStateMap.size(); ++i) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900264 TvInputHardwareInfo hardwareInfo =
265 findHardwareInfoForHdmiPortLocked(mHdmiStateMap.keyAt(i));
266 if (hardwareInfo == null) {
267 continue;
268 }
269 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Wonsik Kim969167d2014-06-24 16:33:17 +0900270 if (inputId != null && inputId.equals(info.getId())) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900271 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
Wonsik Kim969167d2014-06-24 16:33:17 +0900272 convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
273 inputId).sendToTarget();
274 }
275 }
276 }
277 }
278
Wonsik Kim38feae92014-07-21 21:35:50 +0900279 private static <T> int indexOfEqualValue(SparseArray<T> map, T value) {
280 for (int i = 0; i < map.size(); ++i) {
281 if (map.valueAt(i).equals(value)) {
282 return i;
283 }
284 }
285 return -1;
286 }
287
Jae Seo546c6352014-08-07 11:57:01 -0700288 public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900289 if (info.getType() != TvInputInfo.TYPE_HDMI) {
290 throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
291 }
292 synchronized (mLock) {
293 String parentId = info.getParentId();
Wonsik Kim38feae92014-07-21 21:35:50 +0900294 int parentIndex = indexOfEqualValue(mHardwareInputIdMap, parentId);
295 if (parentIndex < 0) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900296 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
297 }
Jae Seo546c6352014-08-07 11:57:01 -0700298 String oldInputId = mHdmiInputIdMap.get(logicalAddress);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900299 if (oldInputId != null) {
300 Slog.w(TAG, "Trying to override previous registration: old = "
301 + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
302 + info + ":" + logicalAddress);
303 }
Jae Seo546c6352014-08-07 11:57:01 -0700304 mHdmiInputIdMap.put(logicalAddress, info.getId());
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900305 mInputMap.put(info.getId(), info);
306 }
307 }
308
309 public void removeTvInput(String inputId) {
310 synchronized (mLock) {
311 mInputMap.remove(inputId);
Wonsik Kim38feae92014-07-21 21:35:50 +0900312 int hardwareIndex = indexOfEqualValue(mHardwareInputIdMap, inputId);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900313 if (hardwareIndex >= 0) {
314 mHardwareInputIdMap.removeAt(hardwareIndex);
315 }
Jae Seo546c6352014-08-07 11:57:01 -0700316 int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
317 if (deviceIndex >= 0) {
318 mHdmiInputIdMap.removeAt(deviceIndex);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900319 }
320 }
321 }
322
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000323 /**
324 * Create a TvInputHardware object with a specific deviceId. One service at a time can access
325 * the object, and if more than one process attempts to create hardware with the same deviceId,
326 * the latest service will get the object and all the other hardware are released. The
327 * release is notified via ITvInputHardwareCallback.onReleased().
328 */
329 public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
Wonsik Kim969167d2014-06-24 16:33:17 +0900330 TvInputInfo info, int callingUid, int resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000331 if (callback == null) {
332 throw new NullPointerException();
333 }
334 synchronized (mLock) {
335 Connection connection = mConnections.get(deviceId);
336 if (connection == null) {
337 Slog.e(TAG, "Invalid deviceId : " + deviceId);
338 return null;
339 }
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900340 if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900341 TvInputHardwareImpl hardware =
342 new TvInputHardwareImpl(connection.getHardwareInfoLocked());
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000343 try {
344 callback.asBinder().linkToDeath(connection, 0);
345 } catch (RemoteException e) {
346 hardware.release();
347 return null;
348 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900349 connection.resetLocked(hardware, callback, info, callingUid, resolvedUserId);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000350 }
351 return connection.getHardwareLocked();
352 }
353 }
354
355 /**
356 * Release the specified hardware.
357 */
358 public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
359 int resolvedUserId) {
360 synchronized (mLock) {
361 Connection connection = mConnections.get(deviceId);
362 if (connection == null) {
363 Slog.e(TAG, "Invalid deviceId : " + deviceId);
364 return;
365 }
366 if (connection.getHardwareLocked() != hardware
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900367 || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000368 return;
369 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900370 connection.resetLocked(null, null, null, null, null);
371 }
372 }
373
Wonsik Kimd922a542014-07-24 18:25:29 +0900374 private TvInputHardwareInfo findHardwareInfoForHdmiPortLocked(int port) {
375 for (TvInputHardwareInfo hardwareInfo : mHardwareList) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900376 if (hardwareInfo.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI
377 && hardwareInfo.getHdmiPortId() == port) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900378 return hardwareInfo;
Wonsik Kim969167d2014-06-24 16:33:17 +0900379 }
380 }
381 return null;
382 }
383
Terry Heoc086a3d2014-06-18 14:26:44 +0900384 private int findDeviceIdForInputIdLocked(String inputId) {
385 for (int i = 0; i < mConnections.size(); ++i) {
386 Connection connection = mConnections.get(i);
387 if (connection.getInfoLocked().getId().equals(inputId)) {
388 return i;
389 }
390 }
391 return -1;
392 }
393
394 /**
395 * Get the list of TvStreamConfig which is buffered mode.
396 */
397 public List<TvStreamConfig> getAvailableTvStreamConfigList(String inputId, int callingUid,
398 int resolvedUserId) {
399 List<TvStreamConfig> configsList = new ArrayList<TvStreamConfig>();
400 synchronized (mLock) {
401 int deviceId = findDeviceIdForInputIdLocked(inputId);
402 if (deviceId < 0) {
403 Slog.e(TAG, "Invalid inputId : " + inputId);
404 return configsList;
405 }
406 Connection connection = mConnections.get(deviceId);
407 for (TvStreamConfig config : connection.getConfigsLocked()) {
408 if (config.getType() == TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
409 configsList.add(config);
410 }
411 }
412 }
413 return configsList;
414 }
415
416 /**
417 * Take a snapshot of the given TV input into the provided Surface.
418 */
419 public boolean captureFrame(String inputId, Surface surface, final TvStreamConfig config,
420 int callingUid, int resolvedUserId) {
421 synchronized (mLock) {
422 int deviceId = findDeviceIdForInputIdLocked(inputId);
423 if (deviceId < 0) {
424 Slog.e(TAG, "Invalid inputId : " + inputId);
425 return false;
426 }
427 Connection connection = mConnections.get(deviceId);
428 final TvInputHardwareImpl hardwareImpl = connection.getHardwareImplLocked();
429 if (hardwareImpl != null) {
430 // Stop previous capture.
431 Runnable runnable = connection.getOnFirstFrameCapturedLocked();
432 if (runnable != null) {
433 runnable.run();
434 connection.setOnFirstFrameCapturedLocked(null);
435 }
436
437 boolean result = hardwareImpl.startCapture(surface, config);
438 if (result) {
439 connection.setOnFirstFrameCapturedLocked(new Runnable() {
440 @Override
441 public void run() {
442 hardwareImpl.stopCapture(config);
443 }
444 });
445 }
446 return result;
447 }
448 }
449 return false;
450 }
451
Wonsik Kimd922a542014-07-24 18:25:29 +0900452 private void processPendingHdmiDeviceEventsLocked() {
453 for (Iterator<Message> it = mPendingHdmiDeviceEvents.iterator(); it.hasNext(); ) {
454 Message msg = it.next();
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900455 HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
Wonsik Kimd922a542014-07-24 18:25:29 +0900456 TvInputHardwareInfo hardwareInfo =
457 findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId());
458 if (hardwareInfo != null) {
459 msg.sendToTarget();
460 it.remove();
461 }
462 }
463 }
464
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000465 private class Connection implements IBinder.DeathRecipient {
Wonsik Kim969167d2014-06-24 16:33:17 +0900466 private final TvInputHardwareInfo mHardwareInfo;
467 private TvInputInfo mInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000468 private TvInputHardwareImpl mHardware = null;
469 private ITvInputHardwareCallback mCallback;
470 private TvStreamConfig[] mConfigs = null;
471 private Integer mCallingUid = null;
472 private Integer mResolvedUserId = null;
Terry Heoc086a3d2014-06-18 14:26:44 +0900473 private Runnable mOnFirstFrameCaptured;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000474
Wonsik Kim969167d2014-06-24 16:33:17 +0900475 public Connection(TvInputHardwareInfo hardwareInfo) {
476 mHardwareInfo = hardwareInfo;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000477 }
478
479 // *Locked methods assume TvInputHardwareManager.mLock is held.
480
Wonsik Kim969167d2014-06-24 16:33:17 +0900481 public void resetLocked(TvInputHardwareImpl hardware, ITvInputHardwareCallback callback,
482 TvInputInfo info, Integer callingUid, Integer resolvedUserId) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000483 if (mHardware != null) {
484 try {
485 mCallback.onReleased();
486 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900487 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000488 }
489 mHardware.release();
490 }
491 mHardware = hardware;
492 mCallback = callback;
Wonsik Kim969167d2014-06-24 16:33:17 +0900493 mInfo = info;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000494 mCallingUid = callingUid;
495 mResolvedUserId = resolvedUserId;
Terry Heoc086a3d2014-06-18 14:26:44 +0900496 mOnFirstFrameCaptured = null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000497
498 if (mHardware != null && mCallback != null) {
499 try {
500 mCallback.onStreamConfigChanged(getConfigsLocked());
501 } catch (RemoteException e) {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900502 Slog.e(TAG, "error in Connection::resetLocked", e);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000503 }
504 }
505 }
506
507 public void updateConfigsLocked(TvStreamConfig[] configs) {
508 mConfigs = configs;
509 }
510
Wonsik Kim969167d2014-06-24 16:33:17 +0900511 public TvInputHardwareInfo getHardwareInfoLocked() {
512 return mHardwareInfo;
513 }
514
515 public TvInputInfo getInfoLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000516 return mInfo;
517 }
518
519 public ITvInputHardware getHardwareLocked() {
520 return mHardware;
521 }
522
Terry Heoc086a3d2014-06-18 14:26:44 +0900523 public TvInputHardwareImpl getHardwareImplLocked() {
524 return mHardware;
525 }
526
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000527 public ITvInputHardwareCallback getCallbackLocked() {
528 return mCallback;
529 }
530
531 public TvStreamConfig[] getConfigsLocked() {
532 return mConfigs;
533 }
534
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900535 public Integer getCallingUidLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000536 return mCallingUid;
537 }
538
Wonsik Kime7ae0ce2014-06-19 00:48:35 +0900539 public Integer getResolvedUserIdLocked() {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000540 return mResolvedUserId;
541 }
542
Terry Heoc086a3d2014-06-18 14:26:44 +0900543 public void setOnFirstFrameCapturedLocked(Runnable runnable) {
544 mOnFirstFrameCaptured = runnable;
545 }
546
547 public Runnable getOnFirstFrameCapturedLocked() {
548 return mOnFirstFrameCaptured;
549 }
550
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000551 @Override
552 public void binderDied() {
553 synchronized (mLock) {
Wonsik Kim969167d2014-06-24 16:33:17 +0900554 resetLocked(null, null, null, null, null);
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000555 }
556 }
557 }
558
559 private class TvInputHardwareImpl extends ITvInputHardware.Stub {
560 private final TvInputHardwareInfo mInfo;
561 private boolean mReleased = false;
562 private final Object mImplLock = new Object();
563
Wonsik Kim06c41a32014-08-04 18:57:47 +0900564 private final AudioManager.OnAudioPortUpdateListener mAudioListener =
565 new AudioManager.OnAudioPortUpdateListener() {
566 @Override
567 public void onAudioPortListUpdate(AudioPort[] portList) {
568 synchronized (mImplLock) {
569 updateAudioSinkLocked();
570 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE && mAudioSource == null) {
571 mAudioSource = findAudioDevicePort(mInfo.getAudioType(),
572 mInfo.getAudioAddress());
573 if (mActiveConfig != null) {
574 updateAudioPatchLocked();
575 }
576 }
577 }
578 }
579
580 @Override
581 public void onAudioPatchListUpdate(AudioPatch[] patchList) {
582 // No-op
583 }
584
585 @Override
586 public void onServiceDied() {
587 synchronized (mImplLock) {
588 mAudioSource = null;
589 mAudioSink = null;
590 mAudioPatch = null;
591 }
592 }
593 };
594 private int mOverrideAudioType = AudioManager.DEVICE_NONE;
595 private String mOverrideAudioAddress = "";
596 private AudioDevicePort mAudioSource;
Wonsik Kimca17a902014-07-31 10:09:46 +0900597 private AudioDevicePort mAudioSink;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900598 private AudioPatch mAudioPatch = null;
Wonsik Kim8e45a332014-08-04 10:17:18 +0900599 private float mCommittedVolume = 0.0f;
600 private float mVolume = 0.0f;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900601
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900602 private TvStreamConfig mActiveConfig = null;
603
Wonsik Kimca17a902014-07-31 10:09:46 +0900604 private int mDesiredSamplingRate = 0;
605 private int mDesiredChannelMask = AudioFormat.CHANNEL_OUT_DEFAULT;
606 private int mDesiredFormat = AudioFormat.ENCODING_DEFAULT;
607
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000608 public TvInputHardwareImpl(TvInputHardwareInfo info) {
609 mInfo = info;
Wonsik Kim06c41a32014-08-04 18:57:47 +0900610 mAudioManager.registerAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900611 if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900612 mAudioSource = findAudioDevicePort(mInfo.getAudioType(), mInfo.getAudioAddress());
Wonsik Kimca17a902014-07-31 10:09:46 +0900613 mAudioSink = findAudioSinkFromAudioPolicy();
614 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900615 }
616
617 private AudioDevicePort findAudioSinkFromAudioPolicy() {
618 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
619 if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
620 int sinkDevice = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
621 for (AudioPort port : devicePorts) {
622 AudioDevicePort devicePort = (AudioDevicePort) port;
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900623 if ((devicePort.type() & sinkDevice) != 0) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900624 return devicePort;
Wonsik Kimd7c29182014-05-27 10:38:21 +0900625 }
626 }
627 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900628 return null;
629 }
630
631 private AudioDevicePort findAudioDevicePort(int type, String address) {
632 if (type == AudioManager.DEVICE_NONE) {
633 return null;
634 }
635 ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
636 if (mAudioManager.listAudioDevicePorts(devicePorts) != AudioManager.SUCCESS) {
637 return null;
638 }
639 for (AudioPort port : devicePorts) {
640 AudioDevicePort devicePort = (AudioDevicePort) port;
641 if (devicePort.type() == type && devicePort.address().equals(address)) {
642 return devicePort;
643 }
644 }
645 return null;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000646 }
647
648 public void release() {
649 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900650 mAudioManager.unregisterAudioPortUpdateListener(mAudioListener);
Wonsik Kimd7c29182014-05-27 10:38:21 +0900651 if (mAudioPatch != null) {
652 mAudioManager.releaseAudioPatch(mAudioPatch);
653 mAudioPatch = null;
654 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000655 mReleased = true;
656 }
657 }
658
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900659 // A TvInputHardwareImpl object holds only one active session. Therefore, if a client
660 // attempts to call setSurface with different TvStreamConfig objects, the last call will
661 // prevail.
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000662 @Override
663 public boolean setSurface(Surface surface, TvStreamConfig config)
664 throws RemoteException {
665 synchronized (mImplLock) {
666 if (mReleased) {
667 throw new IllegalStateException("Device already released.");
668 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900669 if (surface != null && config == null) {
670 return false;
671 }
672 if (surface == null && mActiveConfig == null) {
673 return false;
674 }
Wonsik Kimd7c29182014-05-27 10:38:21 +0900675 if (mAudioSource != null && mAudioSink != null) {
676 if (surface != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900677 updateAudioPatchLocked();
Wonsik Kimd7c29182014-05-27 10:38:21 +0900678 } else {
679 mAudioManager.releaseAudioPatch(mAudioPatch);
680 mAudioPatch = null;
681 }
682 }
Wonsik Kim839ae5f2014-07-03 19:06:56 +0900683 int result = TvInputHal.ERROR_UNKNOWN;
684 if (surface == null) {
685 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
686 mActiveConfig = null;
687 } else {
688 if (config != mActiveConfig && mActiveConfig != null) {
689 result = mHal.removeStream(mInfo.getDeviceId(), mActiveConfig);
690 if (result != TvInputHal.SUCCESS) {
691 mActiveConfig = null;
692 return false;
693 }
694 }
695 result = mHal.addStream(mInfo.getDeviceId(), surface, config);
696 if (result == TvInputHal.SUCCESS) {
697 mActiveConfig = config;
698 }
699 }
700 return result == TvInputHal.SUCCESS;
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000701 }
702 }
703
Wonsik Kimca17a902014-07-31 10:09:46 +0900704 private void updateAudioPatchLocked() {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900705 if (mAudioSource == null || mAudioSink == null) {
706 if (mAudioPatch != null) {
707 throw new IllegalStateException("Audio patch should be null if audio source "
708 + "or sink is null.");
709 }
710 return;
711 }
712
Wonsik Kim8e45a332014-08-04 10:17:18 +0900713 AudioGainConfig sourceGainConfig = null;
714 if (mAudioSource.gains().length > 0 && mVolume != mCommittedVolume) {
715 AudioGain sourceGain = null;
716 for (AudioGain gain : mAudioSource.gains()) {
717 if ((gain.mode() & AudioGain.MODE_JOINT) != 0) {
718 sourceGain = gain;
719 break;
720 }
721 }
722 // NOTE: we only change the source gain in MODE_JOINT here.
723 if (sourceGain != null) {
724 int steps = (sourceGain.maxValue() - sourceGain.minValue())
725 / sourceGain.stepValue();
726 int gainValue = sourceGain.minValue();
727 if (mVolume < 1.0f) {
728 gainValue += sourceGain.stepValue() * (int) (mVolume * steps + 0.5);
729 } else {
730 gainValue = sourceGain.maxValue();
731 }
732 int numChannels = 0;
733 for (int mask = sourceGain.channelMask(); mask > 0; mask >>= 1) {
734 numChannels += (mask & 1);
735 }
736 int[] gainValues = new int[numChannels];
737 Arrays.fill(gainValues, gainValue);
738 sourceGainConfig = sourceGain.buildConfig(AudioGain.MODE_JOINT,
739 sourceGain.channelMask(), gainValues, 0);
740 } else {
741 Slog.w(TAG, "No audio source gain with MODE_JOINT support exists.");
742 }
743 }
744
Wonsik Kimca17a902014-07-31 10:09:46 +0900745 AudioPortConfig sourceConfig = mAudioSource.activeConfig();
746 AudioPortConfig sinkConfig = mAudioSink.activeConfig();
747 AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
Wonsik Kim8e45a332014-08-04 10:17:18 +0900748 boolean shouldRecreateAudioPatch = false;
Wonsik Kimca17a902014-07-31 10:09:46 +0900749 if (sinkConfig == null
750 || (mDesiredSamplingRate != 0
751 && sinkConfig.samplingRate() != mDesiredSamplingRate)
752 || (mDesiredChannelMask != AudioFormat.CHANNEL_OUT_DEFAULT
753 && sinkConfig.channelMask() != mDesiredChannelMask)
754 || (mDesiredFormat != AudioFormat.ENCODING_DEFAULT
755 && sinkConfig.format() != mDesiredFormat)) {
756 sinkConfig = mAudioSource.buildConfig(mDesiredSamplingRate, mDesiredChannelMask,
757 mDesiredFormat, null);
Wonsik Kim8e45a332014-08-04 10:17:18 +0900758 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900759 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900760 if (sourceConfig == null || sourceGainConfig != null) {
Wonsik Kimca17a902014-07-31 10:09:46 +0900761 sourceConfig = mAudioSource.buildConfig(sinkConfig.samplingRate(),
Wonsik Kim8e45a332014-08-04 10:17:18 +0900762 sinkConfig.channelMask(), sinkConfig.format(), sourceGainConfig);
763 shouldRecreateAudioPatch = true;
Wonsik Kimca17a902014-07-31 10:09:46 +0900764 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900765 if (shouldRecreateAudioPatch) {
766 mCommittedVolume = mVolume;
767 mAudioManager.createAudioPatch(
768 audioPatchArray,
769 new AudioPortConfig[] { sourceConfig },
770 new AudioPortConfig[] { sinkConfig });
771 mAudioPatch = audioPatchArray[0];
772 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900773 }
774
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000775 @Override
Wonsik Kim8e45a332014-08-04 10:17:18 +0900776 public void setStreamVolume(float volume) throws RemoteException {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000777 synchronized (mImplLock) {
778 if (mReleased) {
779 throw new IllegalStateException("Device already released.");
780 }
Wonsik Kim8e45a332014-08-04 10:17:18 +0900781 mVolume = volume;
782 updateAudioPatchLocked();
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000783 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000784 }
785
786 @Override
787 public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
788 synchronized (mImplLock) {
789 if (mReleased) {
790 throw new IllegalStateException("Device already released.");
791 }
792 }
Wonsik Kim610ccd92014-07-19 18:49:49 +0900793 if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000794 return false;
795 }
796 // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
797 return false;
798 }
Terry Heoc086a3d2014-06-18 14:26:44 +0900799
800 private boolean startCapture(Surface surface, TvStreamConfig config) {
801 synchronized (mImplLock) {
802 if (mReleased) {
803 return false;
804 }
805 if (surface == null || config == null) {
806 return false;
807 }
808 if (config.getType() != TvStreamConfig.STREAM_TYPE_BUFFER_PRODUCER) {
809 return false;
810 }
811
812 int result = mHal.addStream(mInfo.getDeviceId(), surface, config);
813 return result == TvInputHal.SUCCESS;
814 }
815 }
816
817 private boolean stopCapture(TvStreamConfig config) {
818 synchronized (mImplLock) {
819 if (mReleased) {
820 return false;
821 }
822 if (config == null) {
823 return false;
824 }
825
826 int result = mHal.removeStream(mInfo.getDeviceId(), config);
827 return result == TvInputHal.SUCCESS;
828 }
829 }
Wonsik Kimca17a902014-07-31 10:09:46 +0900830
Wonsik Kim06c41a32014-08-04 18:57:47 +0900831 private void updateAudioSinkLocked() {
832 if (mInfo.getAudioType() == AudioManager.DEVICE_NONE) {
833 return;
834 }
835 if (mOverrideAudioType == AudioManager.DEVICE_NONE) {
836 mAudioSink = findAudioSinkFromAudioPolicy();
837 } else {
838 AudioDevicePort audioSink =
839 findAudioDevicePort(mOverrideAudioType, mOverrideAudioAddress);
840 if (audioSink != null) {
841 mAudioSink = audioSink;
842 }
843 }
844 }
845
Jungshik Jang1f819bb2014-08-08 10:46:54 +0900846 private void handleAudioSinkUpdated() {
847 synchronized (mImplLock) {
848 updateAudioSinkLocked();
849 updateAudioPatchLocked();
850 }
851 }
852
Wonsik Kimca17a902014-07-31 10:09:46 +0900853 @Override
854 public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
855 int channelMask, int format) {
856 synchronized (mImplLock) {
Wonsik Kim06c41a32014-08-04 18:57:47 +0900857 mOverrideAudioType = audioType;
858 mOverrideAudioAddress = audioAddress;
859 updateAudioSinkLocked();
860
Wonsik Kimca17a902014-07-31 10:09:46 +0900861 mDesiredSamplingRate = samplingRate;
862 mDesiredChannelMask = channelMask;
863 mDesiredFormat = format;
864
865 if (mAudioPatch != null) {
866 updateAudioPatchLocked();
867 }
868 }
869 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +0000870 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900871
Wonsik Kim187423c2014-06-25 14:12:48 +0900872 interface Listener {
873 public void onStateChanged(String inputId, int state);
874 public void onHardwareDeviceAdded(TvInputHardwareInfo info);
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900875 public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
Jae Seo546c6352014-08-07 11:57:01 -0700876 public void onHdmiDeviceAdded(HdmiDeviceInfo device);
877 public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900878 public void onHdmiDeviceUpdated(HdmiDeviceInfo device);
Wonsik Kim187423c2014-06-25 14:12:48 +0900879 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900880
Wonsik Kim187423c2014-06-25 14:12:48 +0900881 private class ListenerHandler extends Handler {
882 private static final int STATE_CHANGED = 1;
883 private static final int HARDWARE_DEVICE_ADDED = 2;
884 private static final int HARDWARE_DEVICE_REMOVED = 3;
Jae Seo546c6352014-08-07 11:57:01 -0700885 private static final int HDMI_DEVICE_ADDED = 4;
886 private static final int HDMI_DEVICE_REMOVED = 5;
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900887 private static final int HDMI_DEVICE_UPDATED = 6;
Wonsik Kim969167d2014-06-24 16:33:17 +0900888
889 @Override
890 public final void handleMessage(Message msg) {
891 switch (msg.what) {
Wonsik Kim187423c2014-06-25 14:12:48 +0900892 case STATE_CHANGED: {
Wonsik Kim969167d2014-06-24 16:33:17 +0900893 String inputId = (String) msg.obj;
894 int state = msg.arg1;
Wonsik Kim187423c2014-06-25 14:12:48 +0900895 mListener.onStateChanged(inputId, state);
896 break;
897 }
898 case HARDWARE_DEVICE_ADDED: {
899 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
900 mListener.onHardwareDeviceAdded(info);
901 break;
902 }
903 case HARDWARE_DEVICE_REMOVED: {
Ji-Hwan Lee4f9f57c2014-07-19 22:20:31 +0900904 TvInputHardwareInfo info = (TvInputHardwareInfo) msg.obj;
905 mListener.onHardwareDeviceRemoved(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900906 break;
907 }
Jae Seo546c6352014-08-07 11:57:01 -0700908 case HDMI_DEVICE_ADDED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900909 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700910 mListener.onHdmiDeviceAdded(info);
Wonsik Kim187423c2014-06-25 14:12:48 +0900911 break;
912 }
Jae Seo546c6352014-08-07 11:57:01 -0700913 case HDMI_DEVICE_REMOVED: {
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900914 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
Jae Seo546c6352014-08-07 11:57:01 -0700915 mListener.onHdmiDeviceRemoved(info);
Wonsik Kim969167d2014-06-24 16:33:17 +0900916 break;
917 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900918 case HDMI_DEVICE_UPDATED: {
919 HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
920 mListener.onHdmiDeviceUpdated(info);
921 }
Wonsik Kim969167d2014-06-24 16:33:17 +0900922 default: {
923 Slog.w(TAG, "Unhandled message: " + msg);
924 break;
925 }
926 }
927 }
928 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900929
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900930 // Listener implementations for HdmiControlService
931
932 private final class HdmiHotplugEventListener extends IHdmiHotplugEventListener.Stub {
933 @Override
934 public void onReceived(HdmiHotplugEvent event) {
935 synchronized (mLock) {
936 mHdmiStateMap.put(event.getPort(), event.isConnected());
Wonsik Kimd922a542014-07-24 18:25:29 +0900937 TvInputHardwareInfo hardwareInfo =
938 findHardwareInfoForHdmiPortLocked(event.getPort());
939 if (hardwareInfo == null) {
940 return;
941 }
942 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900943 if (inputId == null) {
944 return;
945 }
946 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
947 convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
948 }
949 }
950 }
951
Wonsik Kim187423c2014-06-25 14:12:48 +0900952 private final class HdmiDeviceEventListener extends IHdmiDeviceEventListener.Stub {
953 @Override
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900954 public void onStatusChanged(HdmiDeviceInfo deviceInfo, int status) {
Wonsik Kimd922a542014-07-24 18:25:29 +0900955 synchronized (mLock) {
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900956 int messageType = 0;
957 switch (status) {
958 case HdmiControlManager.DEVICE_EVENT_ADD_DEVICE: {
959 if (!mHdmiDeviceList.contains(deviceInfo)) {
960 mHdmiDeviceList.add(deviceInfo);
961 } else {
962 Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
963 return;
964 }
965 messageType = ListenerHandler.HDMI_DEVICE_ADDED;
966 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900967 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900968 case HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE: {
969 if (!mHdmiDeviceList.remove(deviceInfo)) {
970 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
971 return;
972 }
973 messageType = ListenerHandler.HDMI_DEVICE_REMOVED;
974 break;
975 }
976 case HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE: {
977 if (!mHdmiDeviceList.remove(deviceInfo)) {
978 Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
979 return;
980 }
981 mHdmiDeviceList.add(deviceInfo);
982 messageType = ListenerHandler.HDMI_DEVICE_UPDATED;
983 break;
Wonsik Kimd922a542014-07-24 18:25:29 +0900984 }
985 }
Jungshik Jang61daf6b2014-08-08 11:38:28 +0900986
987 Message msg = mHandler.obtainMessage(messageType, 0, 0, deviceInfo);
Wonsik Kimd922a542014-07-24 18:25:29 +0900988 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
989 msg.sendToTarget();
990 } else {
991 mPendingHdmiDeviceEvents.add(msg);
992 }
993 }
Wonsik Kim187423c2014-06-25 14:12:48 +0900994 }
995 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +0900996
997 private final class HdmiInputChangeListener extends IHdmiInputChangeListener.Stub {
998 @Override
Jungshik Jang61f4fbd2014-08-06 19:21:12 +0900999 public void onChanged(HdmiDeviceInfo device) throws RemoteException {
Jinsuk Kimb2b31512014-07-21 17:14:44 +09001000 String inputId;
1001 synchronized (mLock) {
1002 if (device.isCecDevice()) {
Jae Seo546c6352014-08-07 11:57:01 -07001003 inputId = mHdmiInputIdMap.get(device.getLogicalAddress());
Jinsuk Kimb2b31512014-07-21 17:14:44 +09001004 } else {
Wonsik Kimd922a542014-07-24 18:25:29 +09001005 TvInputHardwareInfo hardwareInfo =
1006 findHardwareInfoForHdmiPortLocked(device.getPortId());
1007 inputId = (hardwareInfo == null) ? null
1008 : mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
Jinsuk Kimb2b31512014-07-21 17:14:44 +09001009 }
1010 }
1011 if (inputId != null) {
1012 Intent intent = new Intent(Intent.ACTION_VIEW);
1013 intent.setData(TvContract.buildChannelUriForPassthroughTvInput(inputId));
Jinsuk Kim58500f42014-08-05 12:48:35 +09001014 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Jinsuk Kimb2b31512014-07-21 17:14:44 +09001015 mContext.startActivity(intent);
1016 } else {
1017 Slog.w(TAG, "onChanged: InputId cannot be found for :" + device);
1018 }
Jinsuk Kim7474fac2014-07-18 17:22:26 +09001019 }
1020 }
Jungshik Jang1f819bb2014-08-08 10:46:54 +09001021
1022 private final class HdmiSystemAudioModeChangeListener extends
1023 IHdmiSystemAudioModeChangeListener.Stub {
1024 @Override
1025 public void onStatusChanged(boolean enabled) throws RemoteException {
1026 synchronized (mLock) {
1027 for (int i = 0; i < mConnections.size(); ++i) {
1028 TvInputHardwareImpl impl = mConnections.valueAt(i).getHardwareImplLocked();
1029 impl.handleAudioSinkUpdated();
1030 }
1031 }
1032 }
1033 }
Wonsik Kimc22dbb62014-05-26 02:26:04 +00001034}